Updated GNU utilities

This commit is contained in:
Jordan K. Hubbard 1993-06-18 04:22:21 +00:00
parent 7c434002a4
commit b76095a430
126 changed files with 74864 additions and 0 deletions

339
gnu/COPYING Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

5
gnu/Makefile Normal file
View file

@ -0,0 +1,5 @@
# @(#)Makefile 5.33.1.1 (Berkeley) 5/6/91
SUBDIR= gawk groff tar
.include <bsd.subdir.mk>

View file

@ -0,0 +1,21 @@
The current developers of Gawk would like to thank and acknowledge the
many people who have contributed to the development through bug reports
and fixes and suggestions. Unfortunately, we have not been organized
enough to keep track of all the names -- for that we apologize.
Another group of people have assisted even more by porting Gawk to new
platforms and providing a great deal of feedback. They are:
Hal Peterson <hrp@pecan.cray.com> (Cray)
Pat Rankin <gawk.rankin@EQL.Caltech.Edu> (VMS)
Michal Jaegermann <NTOMCZAK@vm.ucs.UAlberta.CA> (Atari, NeXT, DEC 3100)
Mike Lijewski <mjlx@eagle.cnsf.cornell.edu> (IBM RS6000)
Scott Deifik <scottd@amgen.com> (MSDOS 2.14)
Kent Williams (MSDOS 2.11)
Conrad Kwok (MSDOS earlier versions)
Scott Garfinkle (MSDOS earlier versions)
Last, but far from least, we would like to thank Brian Kernighan who
has helped to clear up many dark corners of the language and provided a
restraining touch when we have been overly tempted by "feeping
creaturism".

340
gnu/usr.bin/awk/COPYING Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

120
gnu/usr.bin/awk/FUTURES Normal file
View file

@ -0,0 +1,120 @@
This file lists future projects and enhancements for gawk. Items are listed
in roughly the order they will be done for a given release. This file is
mainly for use by the developers to help keep themselves on track, please
don't bug us too much about schedules or what all this really means.
For 2.16
========
David:
Move to autoconf-based configure system.
Allow RS to be a regexp.
RT variable to hold text of record terminator
RECLEN variable for fixed length records
Feedback alloca.s changes to FSF
Extensible hashing in memory of awk arrays
Split() with null string as third arg to split up strings
Analogously, setting FS="" would split the input record into individual
characters.
Arnold:
Generalize IGNORECASE
any value makes it work, not just numeric non-zero
make it apply to *all* string comparisons
Fix FILENAME to have an initial value of "", not "-"
Clean up code by isolating system-specific functions in separate files.
Undertake significant directory reorganization.
Extensive manual cleanup:
Use of texinfo 2.0 features
Lots more examples
Document all of the above.
In 2.17
=======
David:
Incorporate newer dfa.c and regex.c (go to POSIX regexps)
Make regex + dfa less dependant on gawk header file includes
General sub functions:
edit(line, pat, sub) and gedit(line, pat, sub)
that return the substituted strings and allow \1 etc. in the sub string.
Arnold:
DBM storage of awk arrays. Try to allow multiple dbm packages
? Have strftime() pay attention to the value of ENVIRON["TZ"]
Additional manual features:
Document posix regexps
Document use of dbm arrays
? Add an error messages section to the manual
? A section on where gawk is bounded
regex
i/o
sun fp conversions
For 2.18
========
Arnold:
Add chdir and stat built-in functions.
Add function pointers as valid variable types.
Add an `ftw' built-in function that takes a function pointer.
David:
Do an optimization pass over parse tree?
For 2.19 or later:
==================
Add variables similar to C's __FILE__ and __LINE__ for better diagnostics
from within awk programs.
Add an explicit concatenation operator and assignment version.
? Add a switch statement
Add the ability to seek on an open file and retrieve the current file position.
Add lint checking everywhere, including check for use of builtin vars.
only in new awk.
"restart" keyword
Add |&
Make awk '/foo/' files... run at egrep speeds
Do a reference card
Allow OFMT to be other than a floating point format.
Allow redefining of builtin functions?
Make it faster and smaller.
For 3.x:
========
Create a gawk compiler?
Create a gawk-to-C translator? (or C++??)
Provide awk profiling and debugging.

View file

@ -0,0 +1,14 @@
This file describes limits of gawk on a Unix system (although it
is variable even then). Non-Unix systems may have other limits.
# of fields in a record: MAX_INT
Length of input record: MAX_INT
Length of output record: unlimited
Size of a field: MAX_INT
Size of a printf string: MAX_INT
Size of a literal string: MAX_INT
Characters in a character class: 2^(# of bits per byte)
# of file redirections: unlimited
# of pipe redirections: min(# of processes per user, # of open files)
double-precision floating point
Length of source line: unlimited

13
gnu/usr.bin/awk/Makefile Normal file
View file

@ -0,0 +1,13 @@
PROG= awk
SRCS= main.c eval.c builtin.c msg.c iop.c io.c field.c array.c \
node.c version.c re.c awk.c regex.c dfa.c \
getopt.c getopt1.c
CFLAGS+= -DGAWK
LDADD= -lm
DPADD= ${LIBM}
CLEANFILES+= awk.c y.tab.h
MAN1= awk.0
.include <bsd.prog.mk>
.include "../../usr.bin/Makefile.inc"

1295
gnu/usr.bin/awk/NEWS Normal file

File diff suppressed because it is too large Load diff

32
gnu/usr.bin/awk/PORTS Normal file
View file

@ -0,0 +1,32 @@
A recent version of gawk has been successfully compiled and run "make test"
on the following:
Sun 4/490 running 4.1
NeXT running 2.0
DECstation 3100 running Ultrix 4.0 or Ultrix 3.1 (different config)
AtariST (16-bit ints, gcc compiler, byacc, running under TOS)
ESIX V.3.2 Rev D (== System V Release 3.2), the 386. compiler was gcc + bison
IBM RS/6000 (see README.rs6000)
486 running SVR4, using cc and bison
SGI running IRIX 3.3 using gcc (fails with cc)
Sequent Balance running Dynix V3.1
Cray Y-MP8 running Unicos 6.0.11
Cray 2 running Unicos 6.1 (modulo trailing zeroes in chem)
VAX/VMS V5.x (should also work on 4.6 and 4.7)
VMS POSIX V1.0, V1.1
OpenVMS AXP V1.0
MSDOS - Microsoft C 5.1, compiles and runs very simple testing
BSD 4.4alpha
From: ghazi@caip.rutgers.edu (Kaveh R. Ghazi):
arch configured as:
---- --------------
Hpux 9.0 hpux8x
NeXTStep 2.0 next20
Sgi Irix 4.0.5 (/bin/cc) sgi405.cc (new file)
Stardent Titan 1500 OSv2.5 sysv3
Stardent Vistra (i860) SVR4 sysv4
SunOS 4.1.2 sunos41
Tektronix XD88 (UTekV 3.2e) sysv3
Ultrix 4.2 ultrix41

95
gnu/usr.bin/awk/POSIX Normal file
View file

@ -0,0 +1,95 @@
Right now, the numeric vs. string comparisons are screwed up in draft
11.2. What prompted me to check it out was the note in gnu.bug.utils
which observed that gawk was doing the comparison $1 == "000"
numerically. I think that we can agree that intuitively, this should
be done as a string comparison. Version 2.13.2 of gawk follows the
current POSIX draft. Following is how I (now) think this
stuff should be done.
1. A numeric literal or the result of a numeric operation has the NUMERIC
attribute.
2. A string literal or the result of a string operation has the STRING
attribute.
3. Fields, getline input, FILENAME, ARGV elements, ENVIRON elements and the
elements of an array created by split() that are numeric strings
have the STRNUM attribute. Otherwise, they have the STRING attribute.
Uninitialized variables also have the STRNUM attribute.
4. Attributes propagate across assignments, but are not changed by
any use. (Although a use may cause the entity to acquire an additional
value such that it has both a numeric and string value -- this leaves the
attribute unchanged.)
When two operands are compared, either string comparison or numeric comparison
may be used, depending on the attributes of the operands, according to the
following (symmetric) matrix:
+----------------------------------------------
| STRING NUMERIC STRNUM
--------+----------------------------------------------
|
STRING | string string string
|
NUMERIC | string numeric numeric
|
STRNUM | string numeric numeric
--------+----------------------------------------------
So, the following program should print all OKs.
echo '0e2 0a 0 0b
0e2 0a 0 0b' |
$AWK '
NR == 1 {
num = 0
str = "0e2"
print ++test ": " ( (str == "0e2") ? "OK" : "OOPS" )
print ++test ": " ( ("0e2" != 0) ? "OK" : "OOPS" )
print ++test ": " ( ("0" != $2) ? "OK" : "OOPS" )
print ++test ": " ( ("0e2" == $1) ? "OK" : "OOPS" )
print ++test ": " ( (0 == "0") ? "OK" : "OOPS" )
print ++test ": " ( (0 == num) ? "OK" : "OOPS" )
print ++test ": " ( (0 != $2) ? "OK" : "OOPS" )
print ++test ": " ( (0 == $1) ? "OK" : "OOPS" )
print ++test ": " ( ($1 != "0") ? "OK" : "OOPS" )
print ++test ": " ( ($1 == num) ? "OK" : "OOPS" )
print ++test ": " ( ($2 != 0) ? "OK" : "OOPS" )
print ++test ": " ( ($2 != $1) ? "OK" : "OOPS" )
print ++test ": " ( ($3 == 0) ? "OK" : "OOPS" )
print ++test ": " ( ($3 == $1) ? "OK" : "OOPS" )
print ++test ": " ( ($2 != $4) ? "OK" : "OOPS" ) # 15
}
{
a = "+2"
b = 2
if (NR % 2)
c = a + b
print ++test ": " ( (a != b) ? "OK" : "OOPS" ) # 16 and 22
d = "2a"
b = 2
if (NR % 2)
c = d + b
print ++test ": " ( (d != b) ? "OK" : "OOPS" )
print ++test ": " ( (d + 0 == b) ? "OK" : "OOPS" )
e = "2"
print ++test ": " ( (e == b "") ? "OK" : "OOPS" )
a = "2.13"
print ++test ": " ( (a == 2.13) ? "OK" : "OOPS" )
a = "2.130000"
print ++test ": " ( (a != 2.13) ? "OK" : "OOPS" )
if (NR == 2) {
CONVFMT = "%.6f"
print ++test ": " ( (a == 2.13) ? "OK" : "OOPS" )
}
}'

6
gnu/usr.bin/awk/PROBLEMS Normal file
View file

@ -0,0 +1,6 @@
This is a list of known problems in gawk 2.15.
Hopefully they will all be fixed in the next major release of gawk.
Please keep in mind that the code is still undergoing significant evolution.
1. Gawk's printf is probably still not POSIX compliant.

116
gnu/usr.bin/awk/README Normal file
View file

@ -0,0 +1,116 @@
README:
This is GNU Awk 2.15. It should be upwardly compatible with the
System V Release 4 awk. It is almost completely compliant with draft 11.3
of POSIX 1003.2.
This release adds new features -- see NEWS for details.
See the installation instructions, below.
Known problems are given in the PROBLEMS file. Work to be done is
described briefly in the FUTURES file. Verified ports are listed in
the PORTS file. Changes in this version are summarized in the CHANGES file.
Please read the LIMITATIONS and ACKNOWLEDGMENT files.
Read the file POSIX for a discussion of how the standard says comparisons
should be done vs. how they really should be done and how gawk does them.
To format the documentation with TeX, you must use texinfo.tex 2.53
or later. Otherwise footnotes look unacceptable.
If you wish to remake the Info files, you should use makeinfo. The 2.15
version of makeinfo works with no errors.
The man page is up to date.
INSTALLATION:
Check whether there is a system-specific README file for your system.
Makefile.in may need some tailoring. The only changes necessary should
be to change installation targets or to change compiler flags.
The changes to make in Makefile.in are commented and should be obvious.
All other changes should be made in a config file. Samples for
various systems are included in the config directory. Starting with
2.11, our intent has been to make the code conform to standards (ANSI,
POSIX, SVID, in that order) whenever possible, and to not penalize
standard conforming systems. We have included substitute versions of
routines not universally available. Simply add the appropriate define
for the missing feature(s) on your system.
If you have neither bison nor yacc, use the awktab.c file here. It was
generated with bison, and should have no AT&T code in it. (Note that
modifying awk.y without bison or yacc will be difficult, at best. You might
want to get a copy of bison from the FSF too.)
If no config file is included for your system, start by copying one
for a similar system. One way of determining the defines needed is to
try to load gawk with nothing defined and see what routines are
unresolved by the loader. This should give you a good idea of how to
proceed.
The next release will use the FSF autoconfig program, so we are no longer
soliciting new config files.
If you have an MS-DOS system, use the stuff in the pc directory.
For an Atari there is an atari directory and similarly one for VMS.
Chapter 16 of The GAWK Manual discusses configuration in detail.
After successful compilation, do 'make test' to run a small test
suite. There should be no output from the 'cmp' invocations except in
the cases where there are small differences in floating point values.
If there are other differences, please investigate and report the
problem.
PRINTING THE MANUAL
The 'support' directory contains texinfo.tex 2.65, which will be necessary
for printing the manual, and the texindex.c program from the texinfo
distribution which is also necessary. See the makefile for the steps needed
to get a DVI file from the manual.
CAVEATS
The existence of a patchlevel.h file does *N*O*T* imply a commitment on
our part to issue bug fixes or patches. It is there in case we should
decide to do so.
BUG REPORTS AND FIXES (Un*x systems):
Please coordinate changes through David Trueman and/or Arnold Robbins.
David Trueman
Department of Mathematics, Statistics and Computing Science,
Dalhousie University, Halifax, Nova Scotia, Canada
UUCP: {uunet utai watmath}!dalcs!david
INTERNET: david@cs.dal.ca
Arnold Robbins
1736 Reindeer Drive
Atlanta, GA, 30329, USA
INTERNET: arnold@skeeve.atl.ga.us
UUCP: { gatech, emory, emoryu1 }!skeeve!arnold
BUG REPORTS AND FIXES (non-Unix ports):
MS-DOS:
Scott Deifik
AMGEN Inc.
Amgen Center, Bldg.17-Dept.393
Thousand Oaks, CA 91320-1789
Tel-805-499-5725 ext.4677
Fax-805-498-0358
scottd@amgen.com
VMS:
Pat Rankin
rankin@eql.caltech.edu (e-mail only)
Atari ST:
Michal Jaegermann
NTOMCZAK@vm.ucs.UAlberta.CA (e-mail only)

293
gnu/usr.bin/awk/array.c Normal file
View file

@ -0,0 +1,293 @@
/*
* array.c - routines for associative arrays.
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
static NODE *assoc_find P((NODE *symbol, NODE *subs, int hash1));
NODE *
concat_exp(tree)
register NODE *tree;
{
register NODE *r;
char *str;
char *s;
unsigned len;
int offset;
int subseplen;
char *subsep;
if (tree->type != Node_expression_list)
return force_string(tree_eval(tree));
r = force_string(tree_eval(tree->lnode));
if (tree->rnode == NULL)
return r;
subseplen = SUBSEP_node->lnode->stlen;
subsep = SUBSEP_node->lnode->stptr;
len = r->stlen + subseplen + 2;
emalloc(str, char *, len, "concat_exp");
memcpy(str, r->stptr, r->stlen+1);
s = str + r->stlen;
free_temp(r);
tree = tree->rnode;
while (tree) {
if (subseplen == 1)
*s++ = *subsep;
else {
memcpy(s, subsep, subseplen+1);
s += subseplen;
}
r = force_string(tree_eval(tree->lnode));
len += r->stlen + subseplen;
offset = s - str;
erealloc(str, char *, len, "concat_exp");
s = str + offset;
memcpy(s, r->stptr, r->stlen+1);
s += r->stlen;
free_temp(r);
tree = tree->rnode;
}
r = make_str_node(str, s - str, ALREADY_MALLOCED);
r->flags |= TEMP;
return r;
}
/* Flush all the values in symbol[] before doing a split() */
void
assoc_clear(symbol)
NODE *symbol;
{
int i;
NODE *bucket, *next;
if (symbol->var_array == 0)
return;
for (i = 0; i < HASHSIZE; i++) {
for (bucket = symbol->var_array[i]; bucket; bucket = next) {
next = bucket->ahnext;
unref(bucket->ahname);
unref(bucket->ahvalue);
freenode(bucket);
}
symbol->var_array[i] = 0;
}
}
/*
* calculate the hash function of the string in subs
*/
unsigned int
hash(s, len)
register char *s;
register int len;
{
register unsigned long h = 0, g;
while (len--) {
h = (h << 4) + *s++;
g = (h & 0xf0000000);
if (g) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
if (h < HASHSIZE)
return h;
else
return h%HASHSIZE;
}
/*
* locate symbol[subs]
*/
static NODE * /* NULL if not found */
assoc_find(symbol, subs, hash1)
NODE *symbol;
register NODE *subs;
int hash1;
{
register NODE *bucket, *prev = 0;
for (bucket = symbol->var_array[hash1]; bucket; bucket = bucket->ahnext) {
if (cmp_nodes(bucket->ahname, subs) == 0) {
if (prev) { /* move found to front of chain */
prev->ahnext = bucket->ahnext;
bucket->ahnext = symbol->var_array[hash1];
symbol->var_array[hash1] = bucket;
}
return bucket;
} else
prev = bucket; /* save previous list entry */
}
return NULL;
}
/*
* test whether the array element symbol[subs] exists or not
*/
int
in_array(symbol, subs)
NODE *symbol, *subs;
{
register int hash1;
if (symbol->type == Node_param_list)
symbol = stack_ptr[symbol->param_cnt];
if (symbol->var_array == 0)
return 0;
subs = concat_exp(subs); /* concat_exp returns a string node */
hash1 = hash(subs->stptr, subs->stlen);
if (assoc_find(symbol, subs, hash1) == NULL) {
free_temp(subs);
return 0;
} else {
free_temp(subs);
return 1;
}
}
/*
* SYMBOL is the address of the node (or other pointer) being dereferenced.
* SUBS is a number or string used as the subscript.
*
* Find SYMBOL[SUBS] in the assoc array. Install it with value "" if it
* isn't there. Returns a pointer ala get_lhs to where its value is stored
*/
NODE **
assoc_lookup(symbol, subs)
NODE *symbol, *subs;
{
register int hash1;
register NODE *bucket;
(void) force_string(subs);
hash1 = hash(subs->stptr, subs->stlen);
if (symbol->var_array == 0) { /* this table really should grow
* dynamically */
unsigned size;
size = sizeof(NODE *) * HASHSIZE;
emalloc(symbol->var_array, NODE **, size, "assoc_lookup");
memset((char *)symbol->var_array, 0, size);
symbol->type = Node_var_array;
} else {
bucket = assoc_find(symbol, subs, hash1);
if (bucket != NULL) {
free_temp(subs);
return &(bucket->ahvalue);
}
}
/* It's not there, install it. */
if (do_lint && subs->stlen == 0)
warning("subscript of array `%s' is null string",
symbol->vname);
getnode(bucket);
bucket->type = Node_ahash;
if (subs->flags & TEMP)
bucket->ahname = dupnode(subs);
else {
unsigned int saveflags = subs->flags;
subs->flags &= ~MALLOC;
bucket->ahname = dupnode(subs);
subs->flags = saveflags;
}
free_temp(subs);
/* array subscripts are strings */
bucket->ahname->flags &= ~NUMBER;
bucket->ahname->flags |= STRING;
bucket->ahvalue = Nnull_string;
bucket->ahnext = symbol->var_array[hash1];
symbol->var_array[hash1] = bucket;
return &(bucket->ahvalue);
}
void
do_delete(symbol, tree)
NODE *symbol, *tree;
{
register int hash1;
register NODE *bucket, *last;
NODE *subs;
if (symbol->type == Node_param_list)
symbol = stack_ptr[symbol->param_cnt];
if (symbol->var_array == 0)
return;
subs = concat_exp(tree); /* concat_exp returns string node */
hash1 = hash(subs->stptr, subs->stlen);
last = NULL;
for (bucket = symbol->var_array[hash1]; bucket; last = bucket, bucket = bucket->ahnext)
if (cmp_nodes(bucket->ahname, subs) == 0)
break;
free_temp(subs);
if (bucket == NULL)
return;
if (last)
last->ahnext = bucket->ahnext;
else
symbol->var_array[hash1] = bucket->ahnext;
unref(bucket->ahname);
unref(bucket->ahvalue);
freenode(bucket);
}
void
assoc_scan(symbol, lookat)
NODE *symbol;
struct search *lookat;
{
if (!symbol->var_array) {
lookat->retval = NULL;
return;
}
lookat->arr_ptr = symbol->var_array;
lookat->arr_end = lookat->arr_ptr + HASHSIZE; /* added */
lookat->bucket = symbol->var_array[0];
assoc_next(lookat);
}
void
assoc_next(lookat)
struct search *lookat;
{
while (lookat->arr_ptr < lookat->arr_end) {
if (lookat->bucket != 0) {
lookat->retval = lookat->bucket->ahname;
lookat->bucket = lookat->bucket->ahnext;
return;
}
lookat->arr_ptr++;
if (lookat->arr_ptr < lookat->arr_end)
lookat->bucket = *(lookat->arr_ptr);
else
lookat->retval = NULL;
}
return;
}

1873
gnu/usr.bin/awk/awk.1 Normal file

File diff suppressed because it is too large Load diff

763
gnu/usr.bin/awk/awk.h Normal file
View file

@ -0,0 +1,763 @@
/*
* awk.h -- Definitions for gawk.
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* ------------------------------ Includes ------------------------------ */
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <setjmp.h>
#include <varargs.h>
#include <time.h>
#include <errno.h>
#if !defined(errno) && !defined(MSDOS)
extern int errno;
#endif
#ifdef __GNU_LIBRARY__
#ifndef linux
#include <signum.h>
#endif
#endif
/* ----------------- System dependencies (with more includes) -----------*/
#if !defined(VMS) || (!defined(VAXC) && !defined(__DECC))
#include <sys/types.h>
#include <sys/stat.h>
#else /* VMS w/ VAXC or DECC */
#include <types.h>
#include <stat.h>
#include <file.h> /* avoid <fcntl.h> in io.c */
#endif
#include <signal.h>
#include "config.h"
#ifdef __STDC__
#define P(s) s
#define MALLOC_ARG_T size_t
#else
#define P(s) ()
#define MALLOC_ARG_T unsigned
#define volatile
#define const
#endif
#ifndef SIGTYPE
#define SIGTYPE void
#endif
#ifdef SIZE_T_MISSING
typedef unsigned int size_t;
#endif
#ifndef SZTC
#define SZTC
#define INTC
#endif
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <string.h>
#ifdef NeXT
#include <libc.h>
#undef atof
#else
#if defined(atarist) || defined(VMS)
#include <unixlib.h>
#else /* atarist || VMS */
#ifndef MSDOS
#include <unistd.h>
#endif /* MSDOS */
#endif /* atarist || VMS */
#endif /* Next */
#else /* STDC_HEADERS */
#include "protos.h"
#endif /* STDC_HEADERS */
#if defined(ultrix) && !defined(Ultrix41)
extern char * getenv P((char *name));
extern double atof P((char *s));
#endif
#ifndef __GNUC__
#ifdef sparc
/* nasty nasty SunOS-ism */
#include <alloca.h>
#ifdef lint
extern char *alloca();
#endif
#else /* not sparc */
#if !defined(alloca) && !defined(ALLOCA_PROTO)
extern char *alloca();
#endif
#endif /* sparc */
#endif /* __GNUC__ */
#ifdef HAVE_UNDERSCORE_SETJMP
/* nasty nasty berkelixm */
#define setjmp _setjmp
#define longjmp _longjmp
#endif
/*
* if you don't have vprintf, try this and cross your fingers.
*/
#if defined(VPRINTF_MISSING)
#define vfprintf(fp,fmt,arg) _doprnt((fmt), (arg), (fp))
#endif
#ifdef VMS
/* some macros to redirect to code in vms/vms_misc.c */
#define exit vms_exit
#define open vms_open
#define strerror vms_strerror
#define strdup vms_strdup
extern void exit P((int));
extern int open P((const char *,int,...));
extern char *strerror P((int));
extern char *strdup P((const char *str));
extern int vms_devopen P((const char *,int));
# ifndef NO_TTY_FWRITE
#define fwrite tty_fwrite
#define fclose tty_fclose
extern size_t fwrite P((const void *,size_t,size_t,FILE *));
extern int fclose P((FILE *));
# endif
extern FILE *popen P((const char *,const char *));
extern int pclose P((FILE *));
extern void vms_arg_fixup P((int *,char ***));
/* some things not in STDC_HEADERS */
extern int gnu_strftime P((char *,size_t,const char *,const struct tm *));
extern int unlink P((const char *));
extern int getopt P((int,char **,char *));
extern int isatty P((int));
#ifndef fileno
extern int fileno P((FILE *));
#endif
extern int close(), dup(), dup2(), fstat(), read(), stat();
#endif /*VMS*/
#ifdef MSDOS
#include <io.h>
extern FILE *popen P((char *, char *));
extern int pclose P((FILE *));
#endif
#define GNU_REGEX
#ifdef GNU_REGEX
#include "regex.h"
#include "dfa.h"
typedef struct Regexp {
struct re_pattern_buffer pat;
struct re_registers regs;
struct regexp dfareg;
int dfa;
} Regexp;
#define RESTART(rp,s) (rp)->regs.start[0]
#define REEND(rp,s) (rp)->regs.end[0]
#else /* GNU_REGEX */
#endif /* GNU_REGEX */
#ifdef atarist
#define read _text_read /* we do not want all these CR's to mess our input */
extern int _text_read (int, char *, int);
#endif
#ifndef DEFPATH
#define DEFPATH ".:/usr/local/lib/awk:/usr/lib/awk"
#endif
#ifndef ENVSEP
#define ENVSEP ':'
#endif
/* ------------------ Constants, Structures, Typedefs ------------------ */
#define AWKNUM double
typedef enum {
/* illegal entry == 0 */
Node_illegal,
/* binary operators lnode and rnode are the expressions to work on */
Node_times,
Node_quotient,
Node_mod,
Node_plus,
Node_minus,
Node_cond_pair, /* conditional pair (see Node_line_range) */
Node_subscript,
Node_concat,
Node_exp,
/* unary operators subnode is the expression to work on */
/*10*/ Node_preincrement,
Node_predecrement,
Node_postincrement,
Node_postdecrement,
Node_unary_minus,
Node_field_spec,
/* assignments lnode is the var to assign to, rnode is the exp */
Node_assign,
Node_assign_times,
Node_assign_quotient,
Node_assign_mod,
/*20*/ Node_assign_plus,
Node_assign_minus,
Node_assign_exp,
/* boolean binaries lnode and rnode are expressions */
Node_and,
Node_or,
/* binary relationals compares lnode and rnode */
Node_equal,
Node_notequal,
Node_less,
Node_greater,
Node_leq,
/*30*/ Node_geq,
Node_match,
Node_nomatch,
/* unary relationals works on subnode */
Node_not,
/* program structures */
Node_rule_list, /* lnode is a rule, rnode is rest of list */
Node_rule_node, /* lnode is pattern, rnode is statement */
Node_statement_list, /* lnode is statement, rnode is more list */
Node_if_branches, /* lnode is to run on true, rnode on false */
Node_expression_list, /* lnode is an exp, rnode is more list */
Node_param_list, /* lnode is a variable, rnode is more list */
/* keywords */
/*40*/ Node_K_if, /* lnode is conditonal, rnode is if_branches */
Node_K_while, /* lnode is condtional, rnode is stuff to run */
Node_K_for, /* lnode is for_struct, rnode is stuff to run */
Node_K_arrayfor, /* lnode is for_struct, rnode is stuff to run */
Node_K_break, /* no subs */
Node_K_continue, /* no stuff */
Node_K_print, /* lnode is exp_list, rnode is redirect */
Node_K_printf, /* lnode is exp_list, rnode is redirect */
Node_K_next, /* no subs */
Node_K_exit, /* subnode is return value, or NULL */
/*50*/ Node_K_do, /* lnode is conditional, rnode stuff to run */
Node_K_return,
Node_K_delete,
Node_K_getline,
Node_K_function, /* lnode is statement list, rnode is params */
/* I/O redirection for print statements */
Node_redirect_output, /* subnode is where to redirect */
Node_redirect_append, /* subnode is where to redirect */
Node_redirect_pipe, /* subnode is where to redirect */
Node_redirect_pipein, /* subnode is where to redirect */
Node_redirect_input, /* subnode is where to redirect */
/* Variables */
/*60*/ Node_var, /* rnode is value, lnode is array stuff */
Node_var_array, /* array is ptr to elements, asize num of
* eles */
Node_val, /* node is a value - type in flags */
/* Builtins subnode is explist to work on, proc is func to call */
Node_builtin,
/*
* pattern: conditional ',' conditional ; lnode of Node_line_range
* is the two conditionals (Node_cond_pair), other word (rnode place)
* is a flag indicating whether or not this range has been entered.
*/
Node_line_range,
/*
* boolean test of membership in array lnode is string-valued
* expression rnode is array name
*/
Node_in_array,
Node_func, /* lnode is param. list, rnode is body */
Node_func_call, /* lnode is name, rnode is argument list */
Node_cond_exp, /* lnode is conditonal, rnode is if_branches */
Node_regex,
/*70*/ Node_hashnode,
Node_ahash,
Node_NF,
Node_NR,
Node_FNR,
Node_FS,
Node_RS,
Node_FIELDWIDTHS,
Node_IGNORECASE,
Node_OFS,
Node_ORS,
Node_OFMT,
Node_CONVFMT,
Node_K_nextfile
} NODETYPE;
/*
* NOTE - this struct is a rather kludgey -- it is packed to minimize
* space usage, at the expense of cleanliness. Alter at own risk.
*/
typedef struct exp_node {
union {
struct {
union {
struct exp_node *lptr;
char *param_name;
} l;
union {
struct exp_node *rptr;
struct exp_node *(*pptr) ();
Regexp *preg;
struct for_loop_header *hd;
struct exp_node **av;
int r_ent; /* range entered */
} r;
union {
char *name;
struct exp_node *extra;
} x;
short number;
unsigned char reflags;
# define CASE 1
# define CONST 2
# define FS_DFLT 4
} nodep;
struct {
AWKNUM fltnum; /* this is here for optimal packing of
* the structure on many machines
*/
char *sp;
size_t slen;
unsigned char sref;
char idx;
} val;
struct {
struct exp_node *next;
char *name;
int length;
struct exp_node *value;
} hash;
#define hnext sub.hash.next
#define hname sub.hash.name
#define hlength sub.hash.length
#define hvalue sub.hash.value
struct {
struct exp_node *next;
struct exp_node *name;
struct exp_node *value;
} ahash;
#define ahnext sub.ahash.next
#define ahname sub.ahash.name
#define ahvalue sub.ahash.value
} sub;
NODETYPE type;
unsigned short flags;
# define MALLOC 1 /* can be free'd */
# define TEMP 2 /* should be free'd */
# define PERM 4 /* can't be free'd */
# define STRING 8 /* assigned as string */
# define STR 16 /* string value is current */
# define NUM 32 /* numeric value is current */
# define NUMBER 64 /* assigned as number */
# define MAYBE_NUM 128 /* user input: if NUMERIC then
* a NUMBER
*/
char *vname; /* variable's name */
} NODE;
#define lnode sub.nodep.l.lptr
#define nextp sub.nodep.l.lptr
#define rnode sub.nodep.r.rptr
#define source_file sub.nodep.x.name
#define source_line sub.nodep.number
#define param_cnt sub.nodep.number
#define param sub.nodep.l.param_name
#define subnode lnode
#define proc sub.nodep.r.pptr
#define re_reg sub.nodep.r.preg
#define re_flags sub.nodep.reflags
#define re_text lnode
#define re_exp sub.nodep.x.extra
#define re_cnt sub.nodep.number
#define forsub lnode
#define forloop rnode->sub.nodep.r.hd
#define stptr sub.val.sp
#define stlen sub.val.slen
#define stref sub.val.sref
#define stfmt sub.val.idx
#define numbr sub.val.fltnum
#define var_value lnode
#define var_array sub.nodep.r.av
#define condpair lnode
#define triggered sub.nodep.r.r_ent
#ifdef DONTDEF
int primes[] = {31, 61, 127, 257, 509, 1021, 2053, 4099, 8191, 16381};
#endif
/* a quick profile suggests that the following is a good value */
#define HASHSIZE 127
typedef struct for_loop_header {
NODE *init;
NODE *cond;
NODE *incr;
} FOR_LOOP_HEADER;
/* for "for(iggy in foo) {" */
struct search {
NODE **arr_ptr;
NODE **arr_end;
NODE *bucket;
NODE *retval;
};
/* for faster input, bypass stdio */
typedef struct iobuf {
int fd;
char *buf;
char *off;
char *end;
size_t size; /* this will be determined by an fstat() call */
int cnt;
long secsiz;
int flag;
# define IOP_IS_TTY 1
# define IOP_IS_INTERNAL 2
# define IOP_NO_FREE 4
} IOBUF;
typedef void (*Func_ptr)();
/*
* structure used to dynamically maintain a linked-list of open files/pipes
*/
struct redirect {
unsigned int flag;
# define RED_FILE 1
# define RED_PIPE 2
# define RED_READ 4
# define RED_WRITE 8
# define RED_APPEND 16
# define RED_NOBUF 32
# define RED_USED 64
# define RED_EOF 128
char *value;
FILE *fp;
IOBUF *iop;
int pid;
int status;
struct redirect *prev;
struct redirect *next;
};
/* structure for our source, either a command line string or a source file */
struct src {
enum srctype { CMDLINE = 1, SOURCEFILE } stype;
char *val;
};
/* longjmp return codes, must be nonzero */
/* Continue means either for loop/while continue, or next input record */
#define TAG_CONTINUE 1
/* Break means either for/while break, or stop reading input */
#define TAG_BREAK 2
/* Return means return from a function call; leave value in ret_node */
#define TAG_RETURN 3
#define HUGE INT_MAX
/* -------------------------- External variables -------------------------- */
/* gawk builtin variables */
extern int NF;
extern int NR;
extern int FNR;
extern int IGNORECASE;
extern char *RS;
extern char *OFS;
extern int OFSlen;
extern char *ORS;
extern int ORSlen;
extern char *OFMT;
extern char *CONVFMT;
extern int CONVFMTidx;
extern int OFMTidx;
extern NODE *FS_node, *NF_node, *RS_node, *NR_node;
extern NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
extern NODE *CONVFMT_node;
extern NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
extern NODE *IGNORECASE_node;
extern NODE *FIELDWIDTHS_node;
extern NODE **stack_ptr;
extern NODE *Nnull_string;
extern NODE **fields_arr;
extern int sourceline;
extern char *source;
extern NODE *expression_value;
extern NODE *_t; /* used as temporary in tree_eval */
extern const char *myname;
extern NODE *nextfree;
extern int field0_valid;
extern int do_unix;
extern int do_posix;
extern int do_lint;
extern int in_begin_rule;
extern int in_end_rule;
/* ------------------------- Pseudo-functions ------------------------- */
#define is_identchar(c) (isalnum(c) || (c) == '_')
#ifndef MPROF
#define getnode(n) if (nextfree) n = nextfree, nextfree = nextfree->nextp;\
else n = more_nodes()
#define freenode(n) ((n)->nextp = nextfree, nextfree = (n))
#else
#define getnode(n) emalloc(n, NODE *, sizeof(NODE), "getnode")
#define freenode(n) free(n)
#endif
#ifdef DEBUG
#define tree_eval(t) r_tree_eval(t)
#else
#define tree_eval(t) (_t = (t),(_t) == NULL ? Nnull_string : \
((_t)->type == Node_val ? (_t) : \
((_t)->type == Node_var ? (_t)->var_value : \
((_t)->type == Node_param_list ? \
(stack_ptr[(_t)->param_cnt])->var_value : \
r_tree_eval((_t))))))
#endif
#define make_number(x) mk_number((x), (MALLOC|NUM|NUMBER))
#define tmp_number(x) mk_number((x), (MALLOC|TEMP|NUM|NUMBER))
#define free_temp(n) do {if ((n)->flags&TEMP) { unref(n); }} while (0)
#define make_string(s,l) make_str_node((s), SZTC (l),0)
#define SCAN 1
#define ALREADY_MALLOCED 2
#define cant_happen() fatal("internal error line %d, file: %s", \
__LINE__, __FILE__);
#if defined(__STDC__) && !defined(NO_TOKEN_PASTING)
#define emalloc(var,ty,x,str) (void)((var=(ty)malloc((MALLOC_ARG_T)(x))) ||\
(fatal("%s: %s: can't allocate memory (%s)",\
(str), #var, strerror(errno)),0))
#define erealloc(var,ty,x,str) (void)((var=(ty)realloc((char *)var,\
(MALLOC_ARG_T)(x))) ||\
(fatal("%s: %s: can't allocate memory (%s)",\
(str), #var, strerror(errno)),0))
#else /* __STDC__ */
#define emalloc(var,ty,x,str) (void)((var=(ty)malloc((MALLOC_ARG_T)(x))) ||\
(fatal("%s: %s: can't allocate memory (%s)",\
(str), "var", strerror(errno)),0))
#define erealloc(var,ty,x,str) (void)((var=(ty)realloc((char *)var,\
(MALLOC_ARG_T)(x))) ||\
(fatal("%s: %s: can't allocate memory (%s)",\
(str), "var", strerror(errno)),0))
#endif /* __STDC__ */
#ifdef DEBUG
#define force_number r_force_number
#define force_string r_force_string
#else /* not DEBUG */
#ifdef lint
extern AWKNUM force_number();
#endif
#ifdef MSDOS
extern double _msc51bug;
#define force_number(n) (_msc51bug=(_t = (n),(_t->flags & NUM) ? _t->numbr : r_force_number(_t)))
#else /* not MSDOS */
#define force_number(n) (_t = (n),(_t->flags & NUM) ? _t->numbr : r_force_number(_t))
#endif /* MSDOS */
#define force_string(s) (_t = (s),(_t->flags & STR) ? _t : r_force_string(_t))
#endif /* not DEBUG */
#define STREQ(a,b) (*(a) == *(b) && strcmp((a), (b)) == 0)
#define STREQN(a,b,n) ((n)&& *(a)== *(b) && strncmp((a), (b), SZTC (n)) == 0)
/* ------------- Function prototypes or defs (as appropriate) ------------- */
/* array.c */
extern NODE *concat_exp P((NODE *tree));
extern void assoc_clear P((NODE *symbol));
extern unsigned int hash P((char *s, int len));
extern int in_array P((NODE *symbol, NODE *subs));
extern NODE **assoc_lookup P((NODE *symbol, NODE *subs));
extern void do_delete P((NODE *symbol, NODE *tree));
extern void assoc_scan P((NODE *symbol, struct search *lookat));
extern void assoc_next P((struct search *lookat));
/* awk.tab.c */
extern char *tokexpand P((void));
extern char nextc P((void));
extern NODE *node P((NODE *left, NODETYPE op, NODE *right));
extern NODE *install P((char *name, NODE *value));
extern NODE *lookup P((char *name));
extern NODE *variable P((char *name, int can_free));
extern int yyparse P((void));
/* builtin.c */
extern NODE *do_exp P((NODE *tree));
extern NODE *do_index P((NODE *tree));
extern NODE *do_int P((NODE *tree));
extern NODE *do_length P((NODE *tree));
extern NODE *do_log P((NODE *tree));
extern NODE *do_sprintf P((NODE *tree));
extern void do_printf P((NODE *tree));
extern void print_simple P((NODE *tree, FILE *fp));
extern NODE *do_sqrt P((NODE *tree));
extern NODE *do_substr P((NODE *tree));
extern NODE *do_strftime P((NODE *tree));
extern NODE *do_systime P((NODE *tree));
extern NODE *do_system P((NODE *tree));
extern void do_print P((NODE *tree));
extern NODE *do_tolower P((NODE *tree));
extern NODE *do_toupper P((NODE *tree));
extern NODE *do_atan2 P((NODE *tree));
extern NODE *do_sin P((NODE *tree));
extern NODE *do_cos P((NODE *tree));
extern NODE *do_rand P((NODE *tree));
extern NODE *do_srand P((NODE *tree));
extern NODE *do_match P((NODE *tree));
extern NODE *do_gsub P((NODE *tree));
extern NODE *do_sub P((NODE *tree));
/* eval.c */
extern int interpret P((NODE *volatile tree));
extern NODE *r_tree_eval P((NODE *tree));
extern int cmp_nodes P((NODE *t1, NODE *t2));
extern NODE **get_lhs P((NODE *ptr, Func_ptr *assign));
extern void set_IGNORECASE P((void));
void set_OFS P((void));
void set_ORS P((void));
void set_OFMT P((void));
void set_CONVFMT P((void));
/* field.c */
extern void init_fields P((void));
extern void set_record P((char *buf, int cnt, int freeold));
extern void reset_record P((void));
extern void set_NF P((void));
extern NODE **get_field P((int num, Func_ptr *assign));
extern NODE *do_split P((NODE *tree));
extern void set_FS P((void));
extern void set_RS P((void));
extern void set_FIELDWIDTHS P((void));
/* io.c */
extern void set_FNR P((void));
extern void set_NR P((void));
extern void do_input P((void));
extern struct redirect *redirect P((NODE *tree, int *errflg));
extern NODE *do_close P((NODE *tree));
extern int flush_io P((void));
extern int close_io P((void));
extern int devopen P((char *name, char *mode));
extern int pathopen P((char *file));
extern NODE *do_getline P((NODE *tree));
extern void do_nextfile P((void));
/* iop.c */
extern int optimal_bufsize P((int fd));
extern IOBUF *iop_alloc P((int fd));
extern int get_a_record P((char **out, IOBUF *iop, int rs, int *errcode));
/* main.c */
extern int main P((int argc, char **argv));
extern Regexp *mk_re_parse P((char *s, int ignorecase));
extern void load_environ P((void));
extern char *arg_assign P((char *arg));
extern SIGTYPE catchsig P((int sig, int code));
/* msg.c */
#ifdef MSDOS
extern void err P((char *s, char *emsg, char *va_list, ...));
extern void msg P((char *va_alist, ...));
extern void warning P((char *va_alist, ...));
extern void fatal P((char *va_alist, ...));
#else
extern void err ();
extern void msg ();
extern void warning ();
extern void fatal ();
#endif
/* node.c */
extern AWKNUM r_force_number P((NODE *n));
extern NODE *r_force_string P((NODE *s));
extern NODE *dupnode P((NODE *n));
extern NODE *mk_number P((AWKNUM x, unsigned int flags));
extern NODE *make_str_node P((char *s, size_t len, int scan ));
extern NODE *tmp_string P((char *s, size_t len ));
extern NODE *more_nodes P((void));
#ifdef DEBUG
extern void freenode P((NODE *it));
#endif
extern void unref P((NODE *tmp));
extern int parse_escape P((char **string_ptr));
/* re.c */
extern Regexp *make_regexp P((char *s, int len, int ignorecase, int dfa));
extern int research P((Regexp *rp, char *str, int start, int len, int need_start));
extern void refree P((Regexp *rp));
extern void reg_error P((const char *s));
extern Regexp *re_update P((NODE *t));
extern void resyntax P((int syntax));
extern void resetup P((void));
/* strcase.c */
extern int strcasecmp P((const char *s1, const char *s2));
extern int strncasecmp P((const char *s1, const char *s2, register size_t n));
#ifdef atarist
/* atari/tmpnam.c */
extern char *tmpnam P((char *buf));
extern char *tempnam P((const char *path, const char *base));
#endif
/* Figure out what '\a' really is. */
#ifdef __STDC__
#define BELL '\a' /* sure makes life easy, don't it? */
#else
# if 'z' - 'a' == 25 /* ascii */
# if 'a' != 97 /* machine is dumb enough to use mark parity */
# define BELL '\207'
# else
# define BELL '\07'
# endif
# else
# define BELL '\057'
# endif
#endif
extern char casetable[]; /* for case-independent regexp matching */

1804
gnu/usr.bin/awk/awk.y Normal file

File diff suppressed because it is too large Load diff

1133
gnu/usr.bin/awk/builtin.c Normal file

File diff suppressed because it is too large Load diff

272
gnu/usr.bin/awk/config.h Normal file
View file

@ -0,0 +1,272 @@
/*
* config.h -- configuration definitions for gawk.
*
* For generic 4.4 alpha
*/
/*
* Copyright (C) 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* This file isolates configuration dependencies for gnu awk.
* You should know something about your system, perhaps by having
* a manual handy, when you edit this file. You should copy config.h-dist
* to config.h, and edit config.h. Do not modify config.h-dist, so that
* it will be easy to apply any patches that may be distributed.
*
* The general idea is that systems conforming to the various standards
* should need to do the least amount of changing. Definining the various
* items in ths file usually means that your system is missing that
* particular feature.
*
* The order of preference in standard conformance is ANSI C, POSIX,
* and the SVID.
*
* If you have no clue as to what's going on with your system, try
* compiling gawk without editing this file and see what shows up
* missing in the link stage. From there, you can probably figure out
* which defines to turn on.
*/
/**************************/
/* Miscellanious features */
/**************************/
/*
* BLKSIZE_MISSING
*
* Check your /usr/include/sys/stat.h file. If the stat structure
* does not have a member named st_blksize, define this. (This will
* most likely be the case on most System V systems prior to V.4.)
*/
/* #define BLKSIZE_MISSING 1 */
/*
* SIGTYPE
*
* The return type of the routines passed to the signal function.
* Modern systems use `void', older systems use `int'.
* If left undefined, it will default to void.
*/
/* #define SIGTYPE int */
/*
* SIZE_T_MISSING
*
* If your system has no typedef for size_t, define this to get a default
*/
/* #define SIZE_T_MISSING 1 */
/*
* CHAR_UNSIGNED
*
* If your machine uses unsigned characters (IBM RT and RS/6000 and others)
* then define this for use in regex.c
*/
/* #define CHAR_UNSIGNED 1 */
/*
* HAVE_UNDERSCORE_SETJMP
*
* Check in your /usr/include/setjmp.h file. If there are routines
* there named _setjmp and _longjmp, then you should define this.
* Typically only systems derived from Berkeley Unix have this.
*/
#define HAVE_UNDERSCORE_SETJMP 1
/***********************************************/
/* Missing library subroutines or system calls */
/***********************************************/
/*
* MEMCMP_MISSING
* MEMCPY_MISSING
* MEMSET_MISSING
*
* These three routines are for manipulating blocks of memory. Most
* likely they will either all three be present or all three be missing,
* so they're grouped together.
*/
/* #define MEMCMP_MISSING 1 */
/* #define MEMCPY_MISSING 1 */
/* #define MEMSET_MISSING 1 */
/*
* RANDOM_MISSING
*
* Your system does not have the random(3) suite of random number
* generating routines. These are different than the old rand(3)
* routines!
*/
/* #define RANDOM_MISSING 1 */
/*
* STRCASE_MISSING
*
* Your system does not have the strcasemp() and strncasecmp()
* routines that originated in Berkeley Unix.
*/
/* #define STRCASE_MISSING 1 */
/*
* STRCHR_MISSING
*
* Your system does not have the strchr() and strrchr() functions.
*/
/* #define STRCHR_MISSING 1 */
/*
* STRERROR_MISSING
*
* Your system lacks the ANSI C strerror() routine for returning the
* strings associated with errno values.
*/
/* #define STRERROR_MISSING 1 */
/*
* STRTOD_MISSING
*
* Your system does not have the strtod() routine for converting
* strings to double precision floating point values.
*/
/* #define STRTOD_MISSING 1 */
/*
* STRFTIME_MISSING
*
* Your system lacks the ANSI C strftime() routine for formatting
* broken down time values.
*/
/* #define STRFTIME_MISSING 1 */
/*
* TZSET_MISSING
*
* If you have a 4.2 BSD vintage system, then the strftime() routine
* supplied in the missing directory won't be enough, because it relies on the
* tzset() routine from System V / Posix. Fortunately, there is an
* emulation for tzset() too that should do the trick. If you don't
* have tzset(), define this.
*/
/* #define TZSET_MISSING 1 */
/*
* TZNAME_MISSING
*
* Some systems do not support the external variables tzname and daylight.
* If this is the case *and* strftime() is missing, define this.
*/
/* #define TZNAME_MISSING 1 */
/*
* STDC_HEADERS
*
* If your system does have ANSI compliant header files that
* provide prototypes for library routines, then define this.
*/
#define STDC_HEADERS 1
/*
* NO_TOKEN_PASTING
*
* If your compiler define's __STDC__ but does not support token
* pasting (tok##tok), then define this.
*/
/* #define NO_TOKEN_PASTING 1 */
/*****************************************************************/
/* Stuff related to the Standard I/O Library. */
/*****************************************************************/
/* Much of this is (still, unfortunately) black magic in nature. */
/* You may have to use some or all of these together to get gawk */
/* to work correctly. */
/*****************************************************************/
/*
* NON_STD_SPRINTF
*
* Look in your /usr/include/stdio.h file. If the return type of the
* sprintf() function is NOT `int', define this.
*/
/* #define NON_STD_SPRINTF 1 */
/*
* VPRINTF_MISSING
*
* Define this if your system lacks vprintf() and the other routines
* that go with it. This will trigger an attempt to use _doprnt().
* If you don't have that, this attempt will fail and you are on your own.
*/
/* #define VPRINTF_MISSING 1 */
/*
* Casts from size_t to int and back. These will become unnecessary
* at some point in the future, but for now are required where the
* two types are a different representation.
*/
/* #define SZTC */
/* #define INTC */
/*
* SYSTEM_MISSING
*
* Define this if your library does not provide a system function
* or you are not entirely happy with it and would rather use
* a provided replacement (atari only).
*/
/* #define SYSTEM_MISSING 1 */
/*
* FMOD_MISSING
*
* Define this if your system lacks the fmod() function and modf() will
* be used instead.
*/
/* #define FMOD_MISSING 1 */
/*******************************/
/* Gawk configuration options. */
/*******************************/
/*
* DEFPATH
*
* The default search path for the -f option of gawk. It is used
* if the AWKPATH environment variable is undefined. The default
* definition is provided here. Most likely you should not change
* this.
*/
/* #define DEFPATH ".:/usr/lib/awk:/usr/local/lib/awk" */
/* #define ENVSEP ':' */
/*
* alloca already has a prototype defined - don't redefine it
*/
#define ALLOCA_PROTO 1
/*
* srandom already has a prototype defined - don't redefine it
*/
#define SRANDOM_PROTO 1
/* anything that follows is for system-specific short-term kludges */

2291
gnu/usr.bin/awk/dfa.c Normal file

File diff suppressed because it is too large Load diff

543
gnu/usr.bin/awk/dfa.h Normal file
View file

@ -0,0 +1,543 @@
/* dfa.h - declarations for GNU deterministic regexp compiler
Copyright (C) 1988 Free Software Foundation, Inc.
Written June, 1988 by Mike Haertel
NO WARRANTY
BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
GENERAL PUBLIC LICENSE TO COPY
1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy a valid copyright notice "Copyright
(C) 1988 Free Software Foundation, Inc."; and include following the
copyright notice a verbatim copy of the above disclaimer of warranty
and of this License. You may charge a distribution fee for the
physical act of transferring a copy.
2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating
that you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish,
that in whole or in part contains or is a derivative of this
program or any part thereof, to be licensed at no charge to all
third parties on terms identical to those contained in this
License Agreement (except that you may choose to grant more extensive
warranty protection to some or all third parties, at your option).
c) You may charge a distribution fee for the physical act of
transferring a copy, and you may at your option offer warranty
protection in exchange for a fee.
Mere aggregation of another unrelated program with this program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other program under the scope of these terms.
3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:
a) accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of
Paragraphs 1 and 2 above; or,
b) accompany it with a written offer, valid for at least three
years, to give any third party free (except for a nominal
shipping charge) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
For an executable file, complete source code means all the source code for
all modules it contains; but, as a special exception, it need not include
source code for modules which are standard libraries that accompany the
operating system on which the executable file runs.
4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement. Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated. However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.
5. If you wish to incorporate parts of this program into other free
programs whose distribution conditions are different, write to the Free
Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet
worked out a simple rule that can be stated here, but we will often permit
this. We will be guided by the two goals of preserving the free status of
all derivatives our free software and of promoting the sharing and reuse of
software.
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them. Help stamp out software-hoarding! */
#ifdef __STDC__
#ifdef SOMEDAY
#define ISALNUM(c) isalnum(c)
#define ISALPHA(c) isalpha(c)
#define ISUPPER(c) isupper(c)
#else
#define ISALNUM(c) (isascii(c) && isalnum(c))
#define ISALPHA(c) (isascii(c) && isalpha(c))
#define ISUPPER(c) (isascii(c) && isupper(c))
#endif
#else /* ! __STDC__ */
#define const
#define ISALNUM(c) (isascii(c) && isalnum(c))
#define ISALPHA(c) (isascii(c) && isalpha(c))
#define ISUPPER(c) (isascii(c) && isupper(c))
#endif /* ! __STDC__ */
/* 1 means plain parentheses serve as grouping, and backslash
parentheses are needed for literal searching.
0 means backslash-parentheses are grouping, and plain parentheses
are for literal searching. */
#define RE_NO_BK_PARENS 1L
/* 1 means plain | serves as the "or"-operator, and \| is a literal.
0 means \| serves as the "or"-operator, and | is a literal. */
#define RE_NO_BK_VBAR (1L << 1)
/* 0 means plain + or ? serves as an operator, and \+, \? are literals.
1 means \+, \? are operators and plain +, ? are literals. */
#define RE_BK_PLUS_QM (1L << 2)
/* 1 means | binds tighter than ^ or $.
0 means the contrary. */
#define RE_TIGHT_VBAR (1L << 3)
/* 1 means treat \n as an _OR operator
0 means treat it as a normal character */
#define RE_NEWLINE_OR (1L << 4)
/* 0 means that a special characters (such as *, ^, and $) always have
their special meaning regardless of the surrounding context.
1 means that special characters may act as normal characters in some
contexts. Specifically, this applies to:
^ - only special at the beginning, or after ( or |
$ - only special at the end, or before ) or |
*, +, ? - only special when not after the beginning, (, or | */
#define RE_CONTEXT_INDEP_OPS (1L << 5)
/* 1 means that \ in a character class escapes the next character (typically
a hyphen. It also is overloaded to mean that hyphen at the end of the range
is allowable and means that the hyphen is to be taken literally. */
#define RE_AWK_CLASS_HACK (1L << 6)
/* Now define combinations of bits for the standard possibilities. */
#ifdef notdef
#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR)
#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
#define RE_SYNTAX_EMACS 0
#endif
/* The NULL pointer. */
#ifndef NULL
#define NULL 0
#endif
/* Number of bits in an unsigned char. */
#ifndef CHARBITS
#define CHARBITS 8
#endif
/* First integer value that is greater than any character code. */
#define _NOTCHAR (1 << CHARBITS)
/* INTBITS need not be exact, just a lower bound. */
#ifndef INTBITS
#define INTBITS (CHARBITS * sizeof (int))
#endif
/* Number of ints required to hold a bit for every character. */
#define _CHARSET_INTS ((_NOTCHAR + INTBITS - 1) / INTBITS)
/* Sets of unsigned characters are stored as bit vectors in arrays of ints. */
typedef int _charset[_CHARSET_INTS];
/* The regexp is parsed into an array of tokens in postfix form. Some tokens
are operators and others are terminal symbols. Most (but not all) of these
codes are returned by the lexical analyzer. */
#ifdef __STDC__
typedef enum
{
_END = -1, /* _END is a terminal symbol that matches the
end of input; any value of _END or less in
the parse tree is such a symbol. Accepting
states of the DFA are those that would have
a transition on _END. */
/* Ordinary character values are terminal symbols that match themselves. */
_EMPTY = _NOTCHAR, /* _EMPTY is a terminal symbol that matches
the empty string. */
_BACKREF, /* _BACKREF is generated by \<digit>; it
it not completely handled. If the scanner
detects a transition on backref, it returns
a kind of "semi-success" indicating that
the match will have to be verified with
a backtracking matcher. */
_BEGLINE, /* _BEGLINE is a terminal symbol that matches
the empty string if it is at the beginning
of a line. */
_ALLBEGLINE, /* _ALLBEGLINE is a terminal symbol that
matches the empty string if it is at the
beginning of a line; _ALLBEGLINE applies
to the entire regexp and can only occur
as the first token thereof. _ALLBEGLINE
never appears in the parse tree; a _BEGLINE
is prepended with _CAT to the entire
regexp instead. */
_ENDLINE, /* _ENDLINE is a terminal symbol that matches
the empty string if it is at the end of
a line. */
_ALLENDLINE, /* _ALLENDLINE is to _ENDLINE as _ALLBEGLINE
is to _BEGLINE. */
_BEGWORD, /* _BEGWORD is a terminal symbol that matches
the empty string if it is at the beginning
of a word. */
_ENDWORD, /* _ENDWORD is a terminal symbol that matches
the empty string if it is at the end of
a word. */
_LIMWORD, /* _LIMWORD is a terminal symbol that matches
the empty string if it is at the beginning
or the end of a word. */
_NOTLIMWORD, /* _NOTLIMWORD is a terminal symbol that
matches the empty string if it is not at
the beginning or end of a word. */
_QMARK, /* _QMARK is an operator of one argument that
matches zero or one occurences of its
argument. */
_STAR, /* _STAR is an operator of one argument that
matches the Kleene closure (zero or more
occurrences) of its argument. */
_PLUS, /* _PLUS is an operator of one argument that
matches the positive closure (one or more
occurrences) of its argument. */
_CAT, /* _CAT is an operator of two arguments that
matches the concatenation of its
arguments. _CAT is never returned by the
lexical analyzer. */
_OR, /* _OR is an operator of two arguments that
matches either of its arguments. */
_LPAREN, /* _LPAREN never appears in the parse tree,
it is only a lexeme. */
_RPAREN, /* _RPAREN never appears in the parse tree. */
_SET /* _SET and (and any value greater) is a
terminal symbol that matches any of a
class of characters. */
} _token;
#else /* ! __STDC__ */
typedef short _token;
#define _END -1
#define _EMPTY _NOTCHAR
#define _BACKREF (_EMPTY + 1)
#define _BEGLINE (_EMPTY + 2)
#define _ALLBEGLINE (_EMPTY + 3)
#define _ENDLINE (_EMPTY + 4)
#define _ALLENDLINE (_EMPTY + 5)
#define _BEGWORD (_EMPTY + 6)
#define _ENDWORD (_EMPTY + 7)
#define _LIMWORD (_EMPTY + 8)
#define _NOTLIMWORD (_EMPTY + 9)
#define _QMARK (_EMPTY + 10)
#define _STAR (_EMPTY + 11)
#define _PLUS (_EMPTY + 12)
#define _CAT (_EMPTY + 13)
#define _OR (_EMPTY + 14)
#define _LPAREN (_EMPTY + 15)
#define _RPAREN (_EMPTY + 16)
#define _SET (_EMPTY + 17)
#endif /* ! __STDC__ */
/* Sets are stored in an array in the compiled regexp; the index of the
array corresponding to a given set token is given by _SET_INDEX(t). */
#define _SET_INDEX(t) ((t) - _SET)
/* Sometimes characters can only be matched depending on the surrounding
context. Such context decisions depend on what the previous character
was, and the value of the current (lookahead) character. Context
dependent constraints are encoded as 8 bit integers. Each bit that
is set indicates that the constraint succeeds in the corresponding
context.
bit 7 - previous and current are newlines
bit 6 - previous was newline, current isn't
bit 5 - previous wasn't newline, current is
bit 4 - neither previous nor current is a newline
bit 3 - previous and current are word-constituents
bit 2 - previous was word-constituent, current isn't
bit 1 - previous wasn't word-constituent, current is
bit 0 - neither previous nor current is word-constituent
Word-constituent characters are those that satisfy isalnum().
The macro _SUCCEEDS_IN_CONTEXT determines whether a a given constraint
succeeds in a particular context. Prevn is true if the previous character
was a newline, currn is true if the lookahead character is a newline.
Prevl and currl similarly depend upon whether the previous and current
characters are word-constituent letters. */
#define _MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
((constraint) & (1 << (((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4)))
#define _MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \
((constraint) & (1 << (((prevl) ? 2 : 0) + ((currl) ? 1 : 0))))
#define _SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \
(_MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
&& _MATCHES_LETTER_CONTEXT(constraint, prevl, currl))
/* The following macros give information about what a constraint depends on. */
#define _PREV_NEWLINE_DEPENDENT(constraint) \
(((constraint) & 0xc0) >> 2 != ((constraint) & 0x30))
#define _PREV_LETTER_DEPENDENT(constraint) \
(((constraint) & 0x0c) >> 2 != ((constraint) & 0x03))
/* Tokens that match the empty string subject to some constraint actually
work by applying that constraint to determine what may follow them,
taking into account what has gone before. The following values are
the constraints corresponding to the special tokens previously defined. */
#define _NO_CONSTRAINT 0xff
#define _BEGLINE_CONSTRAINT 0xcf
#define _ENDLINE_CONSTRAINT 0xaf
#define _BEGWORD_CONSTRAINT 0xf2
#define _ENDWORD_CONSTRAINT 0xf4
#define _LIMWORD_CONSTRAINT 0xf6
#define _NOTLIMWORD_CONSTRAINT 0xf9
/* States of the recognizer correspond to sets of positions in the parse
tree, together with the constraints under which they may be matched.
So a position is encoded as an index into the parse tree together with
a constraint. */
typedef struct
{
unsigned index; /* Index into the parse array. */
unsigned constraint; /* Constraint for matching this position. */
} _position;
/* Sets of positions are stored as arrays. */
typedef struct
{
_position *elems; /* Elements of this position set. */
int nelem; /* Number of elements in this set. */
} _position_set;
/* A state of the regexp consists of a set of positions, some flags,
and the token value of the lowest-numbered position of the state that
contains an _END token. */
typedef struct
{
int hash; /* Hash of the positions of this state. */
_position_set elems; /* Positions this state could match. */
char newline; /* True if previous state matched newline. */
char letter; /* True if previous state matched a letter. */
char backref; /* True if this state matches a \<digit>. */
unsigned char constraint; /* Constraint for this state to accept. */
int first_end; /* Token value of the first _END in elems. */
} _dfa_state;
/* If an r.e. is at most MUST_MAX characters long, we look for a string which
must appear in it; whatever's found is dropped into the struct reg. */
#define MUST_MAX 50
/* A compiled regular expression. */
struct regexp
{
/* Stuff built by the scanner. */
_charset *charsets; /* Array of character sets for _SET tokens. */
int cindex; /* Index for adding new charsets. */
int calloc; /* Number of charsets currently allocated. */
/* Stuff built by the parser. */
_token *tokens; /* Postfix parse array. */
int tindex; /* Index for adding new tokens. */
int talloc; /* Number of tokens currently allocated. */
int depth; /* Depth required of an evaluation stack
used for depth-first traversal of the
parse tree. */
int nleaves; /* Number of leaves on the parse tree. */
int nregexps; /* Count of parallel regexps being built
with regparse(). */
/* Stuff owned by the state builder. */
_dfa_state *states; /* States of the regexp. */
int sindex; /* Index for adding new states. */
int salloc; /* Number of states currently allocated. */
/* Stuff built by the structure analyzer. */
_position_set *follows; /* Array of follow sets, indexed by position
index. The follow of a position is the set
of positions containing characters that
could conceivably follow a character
matching the given position in a string
matching the regexp. Allocated to the
maximum possible position index. */
int searchflag; /* True if we are supposed to build a searching
as opposed to an exact matcher. A searching
matcher finds the first and shortest string
matching a regexp anywhere in the buffer,
whereas an exact matcher finds the longest
string matching, but anchored to the
beginning of the buffer. */
/* Stuff owned by the executor. */
int tralloc; /* Number of transition tables that have
slots so far. */
int trcount; /* Number of transition tables that have
actually been built. */
int **trans; /* Transition tables for states that can
never accept. If the transitions for a
state have not yet been computed, or the
state could possibly accept, its entry in
this table is NULL. */
int **realtrans; /* Trans always points to realtrans + 1; this
is so trans[-1] can contain NULL. */
int **fails; /* Transition tables after failing to accept
on a state that potentially could do so. */
int *success; /* Table of acceptance conditions used in
regexecute and computed in build_state. */
int *newlines; /* Transitions on newlines. The entry for a
newline in any transition table is always
-1 so we can count lines without wasting
too many cycles. The transition for a
newline is stored separately and handled
as a special case. Newline is also used
as a sentinel at the end of the buffer. */
char must[MUST_MAX];
int mustn;
};
/* Some macros for user access to regexp internals. */
/* ACCEPTING returns true if s could possibly be an accepting state of r. */
#define ACCEPTING(s, r) ((r).states[s].constraint)
/* ACCEPTS_IN_CONTEXT returns true if the given state accepts in the
specified context. */
#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, reg) \
_SUCCEEDS_IN_CONTEXT((reg).states[state].constraint, \
prevn, currn, prevl, currl)
/* FIRST_MATCHING_REGEXP returns the index number of the first of parallel
regexps that a given state could accept. Parallel regexps are numbered
starting at 1. */
#define FIRST_MATCHING_REGEXP(state, reg) (-(reg).states[state].first_end)
/* Entry points. */
#ifdef __STDC__
/* Regsyntax() takes two arguments; the first sets the syntax bits described
earlier in this file, and the second sets the case-folding flag. */
extern void regsyntax(long, int);
/* Compile the given string of the given length into the given struct regexp.
Final argument is a flag specifying whether to build a searching or an
exact matcher. */
extern void regcompile(const char *, size_t, struct regexp *, int);
/* Execute the given struct regexp on the buffer of characters. The
first char * points to the beginning, and the second points to the
first character after the end of the buffer, which must be a writable
place so a sentinel end-of-buffer marker can be stored there. The
second-to-last argument is a flag telling whether to allow newlines to
be part of a string matching the regexp. The next-to-last argument,
if non-NULL, points to a place to increment every time we see a
newline. The final argument, if non-NULL, points to a flag that will
be set if further examination by a backtracking matcher is needed in
order to verify backreferencing; otherwise the flag will be cleared.
Returns NULL if no match is found, or a pointer to the first
character after the first & shortest matching string in the buffer. */
extern char *regexecute(struct regexp *, char *, char *, int, int *, int *);
/* Free the storage held by the components of a struct regexp. */
extern void reg_free(struct regexp *);
/* Entry points for people who know what they're doing. */
/* Initialize the components of a struct regexp. */
extern void reginit(struct regexp *);
/* Incrementally parse a string of given length into a struct regexp. */
extern void regparse(const char *, size_t, struct regexp *);
/* Analyze a parsed regexp; second argument tells whether to build a searching
or an exact matcher. */
extern void reganalyze(struct regexp *, int);
/* Compute, for each possible character, the transitions out of a given
state, storing them in an array of integers. */
extern void regstate(int, struct regexp *, int []);
/* Error handling. */
/* Regerror() is called by the regexp routines whenever an error occurs. It
takes a single argument, a NUL-terminated string describing the error.
The default reg_error() prints the error message to stderr and exits.
The user can provide a different reg_free() if so desired. */
extern void reg_error(const char *);
#else /* ! __STDC__ */
extern void regsyntax(), regcompile(), reg_free(), reginit(), regparse();
extern void reganalyze(), regstate(), reg_error();
extern char *regexecute();
#endif

1225
gnu/usr.bin/awk/eval.c Normal file

File diff suppressed because it is too large Load diff

645
gnu/usr.bin/awk/field.c Normal file
View file

@ -0,0 +1,645 @@
/*
* field.c - routines for dealing with fields and record parsing
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
static int (*parse_field) P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
static void rebuild_record P((void));
static int re_parse_field P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
static int def_parse_field P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
static int sc_parse_field P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
static int fw_parse_field P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
static void set_element P((int, char *, int, NODE *));
static void grow_fields_arr P((int num));
static void set_field P((int num, char *str, int len, NODE *dummy));
static Regexp *FS_regexp = NULL;
static char *parse_extent; /* marks where to restart parse of record */
static int parse_high_water=0; /* field number that we have parsed so far */
static int nf_high_water = 0; /* size of fields_arr */
static int resave_fs;
static NODE *save_FS; /* save current value of FS when line is read,
* to be used in deferred parsing
*/
NODE **fields_arr; /* array of pointers to the field nodes */
int field0_valid; /* $(>0) has not been changed yet */
int default_FS;
static NODE **nodes; /* permanent repository of field nodes */
static int *FIELDWIDTHS = NULL;
void
init_fields()
{
NODE *n;
emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields");
emalloc(nodes, NODE **, sizeof(NODE *), "init_fields");
getnode(n);
*n = *Nnull_string;
fields_arr[0] = nodes[0] = n;
parse_extent = fields_arr[0]->stptr;
save_FS = dupnode(FS_node->var_value);
field0_valid = 1;
}
static void
grow_fields_arr(num)
int num;
{
register int t;
register NODE *n;
erealloc(fields_arr, NODE **, (num + 1) * sizeof(NODE *), "set_field");
erealloc(nodes, NODE **, (num+1) * sizeof(NODE *), "set_field");
for (t = nf_high_water+1; t <= num; t++) {
getnode(n);
*n = *Nnull_string;
fields_arr[t] = nodes[t] = n;
}
nf_high_water = num;
}
/*ARGSUSED*/
static void
set_field(num, str, len, dummy)
int num;
char *str;
int len;
NODE *dummy; /* not used -- just to make interface same as set_element */
{
register NODE *n;
if (num > nf_high_water)
grow_fields_arr(num);
n = nodes[num];
n->stptr = str;
n->stlen = len;
n->flags = (PERM|STR|STRING|MAYBE_NUM);
fields_arr[num] = n;
}
/* Someone assigned a value to $(something). Fix up $0 to be right */
static void
rebuild_record()
{
register int tlen;
register NODE *tmp;
NODE *ofs;
char *ops;
register char *cops;
register NODE **ptr;
register int ofslen;
tlen = 0;
ofs = force_string(OFS_node->var_value);
ofslen = ofs->stlen;
ptr = &fields_arr[NF];
while (ptr > &fields_arr[0]) {
tmp = force_string(*ptr);
tlen += tmp->stlen;
ptr--;
}
tlen += (NF - 1) * ofslen;
if (tlen < 0)
tlen = 0;
emalloc(ops, char *, tlen + 2, "fix_fields");
cops = ops;
ops[0] = '\0';
for (ptr = &fields_arr[1]; ptr <= &fields_arr[NF]; ptr++) {
tmp = *ptr;
if (tmp->stlen == 1)
*cops++ = tmp->stptr[0];
else if (tmp->stlen != 0) {
memcpy(cops, tmp->stptr, tmp->stlen);
cops += tmp->stlen;
}
if (ptr != &fields_arr[NF]) {
if (ofslen == 1)
*cops++ = ofs->stptr[0];
else if (ofslen != 0) {
memcpy(cops, ofs->stptr, ofslen);
cops += ofslen;
}
}
}
tmp = make_str_node(ops, tlen, ALREADY_MALLOCED);
unref(fields_arr[0]);
fields_arr[0] = tmp;
field0_valid = 1;
}
/*
* setup $0, but defer parsing rest of line until reference is made to $(>0)
* or to NF. At that point, parse only as much as necessary.
*/
void
set_record(buf, cnt, freeold)
char *buf;
int cnt;
int freeold;
{
register int i;
NF = -1;
for (i = 1; i <= parse_high_water; i++) {
unref(fields_arr[i]);
}
parse_high_water = 0;
if (freeold) {
unref(fields_arr[0]);
if (resave_fs) {
resave_fs = 0;
unref(save_FS);
save_FS = dupnode(FS_node->var_value);
}
nodes[0]->stptr = buf;
nodes[0]->stlen = cnt;
nodes[0]->stref = 1;
nodes[0]->flags = (STRING|STR|PERM|MAYBE_NUM);
fields_arr[0] = nodes[0];
}
fields_arr[0]->flags |= MAYBE_NUM;
field0_valid = 1;
}
void
reset_record()
{
(void) force_string(fields_arr[0]);
set_record(fields_arr[0]->stptr, fields_arr[0]->stlen, 0);
}
void
set_NF()
{
register int i;
NF = (int) force_number(NF_node->var_value);
if (NF > nf_high_water)
grow_fields_arr(NF);
for (i = parse_high_water + 1; i <= NF; i++) {
unref(fields_arr[i]);
fields_arr[i] = Nnull_string;
}
field0_valid = 0;
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a regular
* expression -- either user-defined or because RS=="" and FS==" "
*/
static int
re_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
NODE *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
if (*RS == 0 && default_FS)
while (scan < end && isspace(*scan))
scan++;
field = scan;
while (scan < end
&& research(rp, scan, 0, (int)(end - scan), 1) != -1
&& nf < up_to) {
if (REEND(rp, scan) == RESTART(rp, scan)) { /* null match */
scan++;
if (scan == end) {
(*set)(++nf, field, scan - field, n);
up_to = nf;
break;
}
continue;
}
(*set)(++nf, field, scan + RESTART(rp, scan) - field, n);
scan += REEND(rp, scan);
field = scan;
if (scan == end) /* FS at end of record */
(*set)(++nf, field, 0, n);
}
if (nf != up_to && scan < end) {
(*set)(++nf, scan, (int)(end - scan), n);
scan = end;
}
*buf = scan;
return (nf);
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a single space
* character.
*/
static int
def_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
NODE *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
char sav;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
/* before doing anything save the char at *end */
sav = *end;
/* because it will be destroyed now: */
*end = ' '; /* sentinel character */
for (; nf < up_to; scan++) {
/*
* special case: fs is single space, strip leading whitespace
*/
while (scan < end && (*scan == ' ' || *scan == '\t'))
scan++;
if (scan >= end)
break;
field = scan;
while (*scan != ' ' && *scan != '\t')
scan++;
(*set)(++nf, field, (int)(scan - field), n);
if (scan == end)
break;
}
/* everything done, restore original char at *end */
*end = sav;
*buf = scan;
return nf;
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a single character
* other than space.
*/
static int
sc_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
NODE *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register char fschar;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
char sav;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
if (*RS == 0 && fs->stlen == 0)
fschar = '\n';
else
fschar = fs->stptr[0];
/* before doing anything save the char at *end */
sav = *end;
/* because it will be destroyed now: */
*end = fschar; /* sentinel character */
for (; nf < up_to; scan++) {
field = scan;
while (*scan++ != fschar)
;
scan--;
(*set)(++nf, field, (int)(scan - field), n);
if (scan == end)
break;
}
/* everything done, restore original char at *end */
*end = sav;
*buf = scan;
return nf;
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for fields are fixed widths.
*/
static int
fw_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
NODE *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *end = scan + len;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
for (; nf < up_to && (len = FIELDWIDTHS[nf+1]) != -1; ) {
if (len > end - scan)
len = end - scan;
(*set)(++nf, scan, len, n);
scan += len;
}
if (len == -1)
*buf = end;
else
*buf = scan;
return nf;
}
NODE **
get_field(requested, assign)
register int requested;
Func_ptr *assign; /* this field is on the LHS of an assign */
{
/*
* if requesting whole line but some other field has been altered,
* then the whole line must be rebuilt
*/
if (requested == 0) {
if (!field0_valid) {
/* first, parse remainder of input record */
if (NF == -1) {
NF = (*parse_field)(HUGE-1, &parse_extent,
fields_arr[0]->stlen -
(parse_extent - fields_arr[0]->stptr),
save_FS, FS_regexp, set_field,
(NODE *)NULL);
parse_high_water = NF;
}
rebuild_record();
}
if (assign)
*assign = reset_record;
return &fields_arr[0];
}
/* assert(requested > 0); */
if (assign)
field0_valid = 0; /* $0 needs reconstruction */
if (requested <= parse_high_water) /* already parsed this field */
return &fields_arr[requested];
if (NF == -1) { /* have not yet parsed to end of record */
/*
* parse up to requested fields, calling set_field() for each,
* saving in parse_extent the point where the parse left off
*/
if (parse_high_water == 0) /* starting at the beginning */
parse_extent = fields_arr[0]->stptr;
parse_high_water = (*parse_field)(requested, &parse_extent,
fields_arr[0]->stlen - (parse_extent-fields_arr[0]->stptr),
save_FS, FS_regexp, set_field, (NODE *)NULL);
/*
* if we reached the end of the record, set NF to the number of
* fields so far. Note that requested might actually refer to
* a field that is beyond the end of the record, but we won't
* set NF to that value at this point, since this is only a
* reference to the field and NF only gets set if the field
* is assigned to -- this case is handled below
*/
if (parse_extent == fields_arr[0]->stptr + fields_arr[0]->stlen)
NF = parse_high_water;
if (requested == HUGE-1) /* HUGE-1 means set NF */
requested = parse_high_water;
}
if (parse_high_water < requested) { /* requested beyond end of record */
if (assign) { /* expand record */
register int i;
if (requested > nf_high_water)
grow_fields_arr(requested);
/* fill in fields that don't exist */
for (i = parse_high_water + 1; i <= requested; i++)
fields_arr[i] = Nnull_string;
NF = requested;
parse_high_water = requested;
} else
return &Nnull_string;
}
return &fields_arr[requested];
}
static void
set_element(num, s, len, n)
int num;
char *s;
int len;
NODE *n;
{
register NODE *it;
it = make_string(s, len);
it->flags |= MAYBE_NUM;
*assoc_lookup(n, tmp_number((AWKNUM) (num))) = it;
}
NODE *
do_split(tree)
NODE *tree;
{
NODE *t1, *t2, *t3, *tmp;
NODE *fs;
char *s;
int (*parseit)P((int, char **, int, NODE *,
Regexp *, void (*)(), NODE *));
Regexp *rp = NULL;
t1 = tree_eval(tree->lnode);
t2 = tree->rnode->lnode;
t3 = tree->rnode->rnode->lnode;
(void) force_string(t1);
if (t2->type == Node_param_list)
t2 = stack_ptr[t2->param_cnt];
if (t2->type != Node_var && t2->type != Node_var_array)
fatal("second argument of split is not a variable");
assoc_clear(t2);
if (t3->re_flags & FS_DFLT) {
parseit = parse_field;
fs = force_string(FS_node->var_value);
rp = FS_regexp;
} else {
tmp = force_string(tree_eval(t3->re_exp));
if (tmp->stlen == 1) {
if (tmp->stptr[0] == ' ')
parseit = def_parse_field;
else
parseit = sc_parse_field;
} else {
parseit = re_parse_field;
rp = re_update(t3);
}
fs = tmp;
}
s = t1->stptr;
tmp = tmp_number((AWKNUM) (*parseit)(HUGE, &s, (int)t1->stlen,
fs, rp, set_element, t2));
free_temp(t1);
free_temp(t3);
return tmp;
}
void
set_FS()
{
NODE *tmp = NULL;
char buf[10];
NODE *fs;
buf[0] = '\0';
default_FS = 0;
if (FS_regexp) {
refree(FS_regexp);
FS_regexp = NULL;
}
fs = force_string(FS_node->var_value);
if (fs->stlen > 1)
parse_field = re_parse_field;
else if (*RS == 0) {
parse_field = sc_parse_field;
if (fs->stlen == 1) {
if (fs->stptr[0] == ' ') {
default_FS = 1;
strcpy(buf, "[ \t\n]+");
} else if (fs->stptr[0] != '\n')
sprintf(buf, "[%c\n]", fs->stptr[0]);
}
} else {
parse_field = def_parse_field;
if (fs->stptr[0] == ' ' && fs->stlen == 1)
default_FS = 1;
else if (fs->stptr[0] != ' ' && fs->stlen == 1) {
if (IGNORECASE == 0)
parse_field = sc_parse_field;
else
sprintf(buf, "[%c]", fs->stptr[0]);
}
}
if (buf[0]) {
FS_regexp = make_regexp(buf, strlen(buf), IGNORECASE, 1);
parse_field = re_parse_field;
} else if (parse_field == re_parse_field) {
FS_regexp = make_regexp(fs->stptr, fs->stlen, IGNORECASE, 1);
} else
FS_regexp = NULL;
resave_fs = 1;
}
void
set_RS()
{
(void) force_string(RS_node->var_value);
RS = RS_node->var_value->stptr;
set_FS();
}
void
set_FIELDWIDTHS()
{
register char *scan;
char *end;
register int i;
static int fw_alloc = 1;
static int warned = 0;
extern double strtod();
if (do_lint && ! warned) {
warned = 1;
warning("use of FIELDWIDTHS is a gawk extension");
}
if (do_unix) /* quick and dirty, does the trick */
return;
parse_field = fw_parse_field;
scan = force_string(FIELDWIDTHS_node->var_value)->stptr;
end = scan + 1;
if (FIELDWIDTHS == NULL)
emalloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
FIELDWIDTHS[0] = 0;
for (i = 1; ; i++) {
if (i >= fw_alloc) {
fw_alloc *= 2;
erealloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
}
FIELDWIDTHS[i] = (int) strtod(scan, &end);
if (end == scan)
break;
scan = end;
}
FIELDWIDTHS[i] = -1;
}

11270
gnu/usr.bin/awk/gawk.texi Normal file

File diff suppressed because it is too large Load diff

662
gnu/usr.bin/awk/getopt.c Normal file
View file

@ -0,0 +1,662 @@
/* Getopt for GNU.
NOTE: getopt is now part of the C library, so if you don't know what
"Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
before changing it!
Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef GAWK
#include "config.h"
#endif
#include <stdio.h>
/* This needs to come after some library #include
to get __GNU_LIBRARY__ defined. */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#include <string.h>
#endif /* GNU C library. */
#ifndef __STDC__
#define const
#endif
/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
long-named option. Because this is not POSIX.2 compliant, it is
being phased out. */
#define GETOPT_COMPAT
/* This version of `getopt' appears to the caller like standard Unix `getopt'
but it behaves differently for the user, since it allows the user
to intersperse the options with the other arguments.
As `getopt' works, it permutes the elements of ARGV so that,
when it is done, all the options precede everything else. Thus
all application programs are extended to handle flexible argument order.
Setting the environment variable POSIXLY_CORRECT disables permutation.
Then the behavior is completely standard.
GNU application programs can use a third alternative mode in which
they can distinguish the relative order of options and other arguments. */
#include "getopt.h"
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here.
Also, when `ordering' is RETURN_IN_ORDER,
each non-option ARGV-element is returned here. */
char *optarg = 0;
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
On entry to `getopt', zero means this is the first call; initialize.
When `getopt' returns EOF, this is the index of the first of the
non-option elements that the caller should itself scan.
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
int optind = 0;
/* The next char to be scanned in the option-element
in which the last option character we returned was found.
This allows us to pick up the scan where we left off.
If this is zero, or a null string, it means resume the scan
by advancing to the next ARGV-element. */
static char *nextchar;
/* Callers store zero here to inhibit the error message
for unrecognized options. */
int opterr = 1;
/* Describe how to deal with options that follow non-option ARGV-elements.
If the caller did not specify anything,
the default is REQUIRE_ORDER if the environment variable
POSIXLY_CORRECT is defined, PERMUTE otherwise.
REQUIRE_ORDER means don't recognize them as options;
stop option processing when the first non-option is seen.
This is what Unix does.
This mode of operation is selected by either setting the environment
variable POSIXLY_CORRECT, or using `+' as the first character
of the list of option characters.
PERMUTE is the default. We permute the contents of ARGV as we scan,
so that eventually all the non-options are at the end. This allows options
to be given in any order, even with programs that were not written to
expect this.
RETURN_IN_ORDER is an option available to programs that were written
to expect options and other ARGV-elements in any order and that care about
the ordering of the two. We describe each non-option ARGV-element
as if it were the argument of an option with character code 1.
Using `-' as the first character of the list of option characters
selects this mode of operation.
The special argument `--' forces an end of option-scanning regardless
of the value of `ordering'. In the case of RETURN_IN_ORDER, only
`--' can cause `getopt' to return EOF with `optind' != ARGC. */
static enum
{
REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;
#ifdef __GNU_LIBRARY__
#include <string.h>
#define my_index strchr
#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
#else
/* Avoid depending on library functions or files
whose names are inconsistent. */
char *getenv ();
static char *
my_index (string, chr)
char *string;
int chr;
{
while (*string)
{
if (*string == chr)
return string;
string++;
}
return 0;
}
static void
my_bcopy (from, to, size)
char *from, *to;
int size;
{
int i;
for (i = 0; i < size; i++)
to[i] = from[i];
}
#endif /* GNU C library. */
/* Handle permutation of arguments. */
/* Describe the part of ARGV that contains non-options that have
been skipped. `first_nonopt' is the index in ARGV of the first of them;
`last_nonopt' is the index after the last of them. */
static int first_nonopt;
static int last_nonopt;
/* Exchange two adjacent subsequences of ARGV.
One subsequence is elements [first_nonopt,last_nonopt)
which contains all the non-options that have been skipped so far.
The other is elements [last_nonopt,optind), which contains all
the options processed since those non-options were skipped.
`first_nonopt' and `last_nonopt' are relocated so that they describe
the new indices of the non-options in ARGV after they are moved. */
static void
exchange (argv)
char **argv;
{
int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
char **temp = (char **) malloc (nonopts_size);
/* Interchange the two blocks of data in ARGV. */
my_bcopy (&argv[first_nonopt], temp, nonopts_size);
my_bcopy (&argv[last_nonopt], &argv[first_nonopt],
(optind - last_nonopt) * sizeof (char *));
my_bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
free(temp);
/* Update records for the slots the non-options now occupy. */
first_nonopt += (optind - last_nonopt);
last_nonopt = optind;
}
/* Scan elements of ARGV (whose length is ARGC) for option characters
given in OPTSTRING.
If an element of ARGV starts with '-', and is not exactly "-" or "--",
then it is an option element. The characters of this element
(aside from the initial '-') are option characters. If `getopt'
is called repeatedly, it returns successively each of the option characters
from each of the option elements.
If `getopt' finds another option character, it returns that character,
updating `optind' and `nextchar' so that the next call to `getopt' can
resume the scan with the following option character or ARGV-element.
If there are no more option characters, `getopt' returns `EOF'.
Then `optind' is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.)
OPTSTRING is a string containing the legitimate option characters.
If an option character is seen that is not listed in OPTSTRING,
return '?' after printing an error message. If you set `opterr' to
zero, the error message is suppressed but we still return '?'.
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned in `optarg'. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned in `optarg', otherwise `optarg' is set to zero.
If OPTSTRING starts with `-' or `+', it requests different methods of
handling the non-option ARGV-elements.
See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
Long-named options begin with `--' instead of `-'.
Their names may be abbreviated as long as the abbreviation is unique
or is an exact match for some defined option. If they have an
argument, it follows the option name in the same ARGV-element, separated
from the option name by a `=', or else the in next ARGV-element.
When `getopt' finds a long-named option, it returns 0 if that option's
`flag' field is nonzero, the value of the option's `val' field
if the `flag' field is zero.
The elements of ARGV aren't really const, because we permute them.
But we pretend they're const in the prototype to be compatible
with other systems.
LONGOPTS is a vector of `struct option' terminated by an
element containing a name which is zero.
LONGIND returns the index in LONGOPT of the long-named option found.
It is only valid when a long-named option has been found by the most
recent call.
If LONG_ONLY is nonzero, '-' as well as '--' can introduce
long-named options. */
int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
int argc;
char *const *argv;
const char *optstring;
const struct option *longopts;
int *longind;
int long_only;
{
int option_index;
optarg = 0;
/* Initialize the internal data when the first call is made.
Start processing options with ARGV-element 1 (since ARGV-element 0
is the program name); the sequence of previously skipped
non-option ARGV-elements is empty. */
if (optind == 0)
{
first_nonopt = last_nonopt = optind = 1;
nextchar = NULL;
/* Determine how to handle the ordering of options and nonoptions. */
if (optstring[0] == '-')
{
ordering = RETURN_IN_ORDER;
++optstring;
}
else if (optstring[0] == '+')
{
ordering = REQUIRE_ORDER;
++optstring;
}
else if (getenv ("POSIXLY_CORRECT") != NULL)
ordering = REQUIRE_ORDER;
else
ordering = PERMUTE;
}
if (nextchar == NULL || *nextchar == '\0')
{
if (ordering == PERMUTE)
{
/* If we have just processed some options following some non-options,
exchange them so that the options come first. */
if (first_nonopt != last_nonopt && last_nonopt != optind)
exchange ((char **) argv);
else if (last_nonopt != optind)
first_nonopt = optind;
/* Now skip any additional non-options
and extend the range of non-options previously skipped. */
while (optind < argc
&& (argv[optind][0] != '-' || argv[optind][1] == '\0')
#ifdef GETOPT_COMPAT
&& (longopts == NULL
|| argv[optind][0] != '+' || argv[optind][1] == '\0')
#endif /* GETOPT_COMPAT */
)
optind++;
last_nonopt = optind;
}
/* Special ARGV-element `--' means premature end of options.
Skip it like a null option,
then exchange with previous non-options as if it were an option,
then skip everything else like a non-option. */
if (optind != argc && !strcmp (argv[optind], "--"))
{
optind++;
if (first_nonopt != last_nonopt && last_nonopt != optind)
exchange ((char **) argv);
else if (first_nonopt == last_nonopt)
first_nonopt = optind;
last_nonopt = argc;
optind = argc;
}
/* If we have done all the ARGV-elements, stop the scan
and back over any non-options that we skipped and permuted. */
if (optind == argc)
{
/* Set the next-arg-index to point at the non-options
that we previously skipped, so the caller will digest them. */
if (first_nonopt != last_nonopt)
optind = first_nonopt;
return EOF;
}
/* If we have come to a non-option and did not permute it,
either stop the scan or describe it to the caller and pass it by. */
if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
#ifdef GETOPT_COMPAT
&& (longopts == NULL
|| argv[optind][0] != '+' || argv[optind][1] == '\0')
#endif /* GETOPT_COMPAT */
)
{
if (ordering == REQUIRE_ORDER)
return EOF;
optarg = argv[optind++];
return 1;
}
/* We have found another option-ARGV-element.
Start decoding its characters. */
nextchar = (argv[optind] + 1
+ (longopts != NULL && argv[optind][1] == '-'));
}
if (longopts != NULL
&& ((argv[optind][0] == '-'
&& (argv[optind][1] == '-' || long_only))
#ifdef GETOPT_COMPAT
|| argv[optind][0] == '+'
#endif /* GETOPT_COMPAT */
))
{
const struct option *p;
char *s = nextchar;
int exact = 0;
int ambig = 0;
const struct option *pfound = NULL;
int indfound = 0;
extern int strncmp();
while (*s && *s != '=')
s++;
/* Test all options for either exact match or abbreviated matches. */
for (p = longopts, option_index = 0; p->name;
p++, option_index++)
if (!strncmp (p->name, nextchar, s - nextchar))
{
if (s - nextchar == strlen (p->name))
{
/* Exact match found. */
pfound = p;
indfound = option_index;
exact = 1;
break;
}
else if (pfound == NULL)
{
/* First nonexact match found. */
pfound = p;
indfound = option_index;
}
else
/* Second nonexact match found. */
ambig = 1;
}
if (ambig && !exact)
{
if (opterr)
fprintf (stderr, "%s: option `%s' is ambiguous\n",
argv[0], argv[optind]);
nextchar += strlen (nextchar);
optind++;
return '?';
}
if (pfound != NULL)
{
option_index = indfound;
optind++;
if (*s)
{
/* Don't test has_arg with >, because some C compilers don't
allow it to be used on enums. */
if (pfound->has_arg)
optarg = s + 1;
else
{
if (opterr)
{
if (argv[optind - 1][1] == '-')
/* --option */
fprintf (stderr,
"%s: option `--%s' doesn't allow an argument\n",
argv[0], pfound->name);
else
/* +option or -option */
fprintf (stderr,
"%s: option `%c%s' doesn't allow an argument\n",
argv[0], argv[optind - 1][0], pfound->name);
}
nextchar += strlen (nextchar);
return '?';
}
}
else if (pfound->has_arg == 1)
{
if (optind < argc)
optarg = argv[optind++];
else
{
if (opterr)
fprintf (stderr, "%s: option `%s' requires an argument\n",
argv[0], argv[optind - 1]);
nextchar += strlen (nextchar);
return '?';
}
}
nextchar += strlen (nextchar);
if (longind != NULL)
*longind = option_index;
if (pfound->flag)
{
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
/* Can't find it as a long option. If this is not getopt_long_only,
or the option starts with '--' or is not a valid short
option, then it's an error.
Otherwise interpret it as a short option. */
if (!long_only || argv[optind][1] == '-'
#ifdef GETOPT_COMPAT
|| argv[optind][0] == '+'
#endif /* GETOPT_COMPAT */
|| my_index (optstring, *nextchar) == NULL)
{
if (opterr)
{
if (argv[optind][1] == '-')
/* --option */
fprintf (stderr, "%s: unrecognized option `--%s'\n",
argv[0], nextchar);
else
/* +option or -option */
fprintf (stderr, "%s: unrecognized option `%c%s'\n",
argv[0], argv[optind][0], nextchar);
}
nextchar = (char *) "";
optind++;
return '?';
}
}
/* Look at and handle the next option-character. */
{
char c = *nextchar++;
char *temp = my_index (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == '\0')
++optind;
if (temp == NULL || c == ':')
{
if (opterr)
{
if (c < 040 || c >= 0177)
fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
argv[0], c);
else
fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
}
return '?';
}
if (temp[1] == ':')
{
if (temp[2] == ':')
{
/* This is an option that accepts an argument optionally. */
if (*nextchar != '\0')
{
optarg = nextchar;
optind++;
}
else
optarg = 0;
nextchar = NULL;
}
else
{
/* This is an option that requires an argument. */
if (*nextchar != '\0')
{
optarg = nextchar;
/* If we end this ARGV-element by taking the rest as an arg,
we must advance to the next element now. */
optind++;
}
else if (optind == argc)
{
if (opterr)
fprintf (stderr, "%s: option `-%c' requires an argument\n",
argv[0], c);
c = '?';
}
else
/* We already incremented `optind' once;
increment it again when taking next ARGV-elt as argument. */
optarg = argv[optind++];
nextchar = NULL;
}
}
return c;
}
}
int
getopt (argc, argv, optstring)
int argc;
char *const *argv;
const char *optstring;
{
return _getopt_internal (argc, argv, optstring,
(const struct option *) 0,
(int *) 0,
0);
}
#ifdef TEST
/* Compile with -DTEST to make an executable for use in testing
the above definition of `getopt'. */
int
main (argc, argv)
int argc;
char **argv;
{
int c;
int digit_optind = 0;
while (1)
{
int this_option_optind = optind ? optind : 1;
c = getopt (argc, argv, "abc:d:0123456789");
if (c == EOF)
break;
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
break;
case 'a':
printf ("option a\n");
break;
case 'b':
printf ("option b\n");
break;
case 'c':
printf ("option c with value `%s'\n", optarg);
break;
case '?':
break;
default:
printf ("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
exit (0);
}
#endif /* TEST */

128
gnu/usr.bin/awk/getopt.h Normal file
View file

@ -0,0 +1,128 @@
/* Declarations for getopt.
Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef _GETOPT_H
#define _GETOPT_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here.
Also, when `ordering' is RETURN_IN_ORDER,
each non-option ARGV-element is returned here. */
extern char *optarg;
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
On entry to `getopt', zero means this is the first call; initialize.
When `getopt' returns EOF, this is the index of the first of the
non-option elements that the caller should itself scan.
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
extern int optind;
/* Callers store zero here to inhibit the error message `getopt' prints
for unrecognized options. */
extern int opterr;
/* Describe the long-named options requested by the application.
The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
of `struct option' terminated by an element containing a name which is
zero.
The field `has_arg' is:
no_argument (or 0) if the option does not take an argument,
required_argument (or 1) if the option requires an argument,
optional_argument (or 2) if the option takes an optional argument.
If the field `flag' is not NULL, it points to a variable that is set
to the value given in the field `val' when the option is found, but
left unchanged if the option is not found.
To have a long-named option do something other than set an `int' to
a compiled-in constant, such as set a value from `optarg', set the
option's `flag' field to zero and its `val' field to a nonzero
value (the equivalent single-letter option character, if there is
one). For long options that have a zero `flag' field, `getopt'
returns the contents of the `val' field. */
struct option
{
#if __STDC__
const char *name;
#else
char *name;
#endif
/* has_arg can't be an enum because some compilers complain about
type mismatches in all the code that assumes it is an int. */
int has_arg;
int *flag;
int val;
};
/* Names for the values of the `has_arg' field of `struct option'. */
enum _argtype
{
no_argument,
required_argument,
optional_argument
};
#if __STDC__
#if defined(__GNU_LIBRARY__)
/* Many other libraries have conflicting prototypes for getopt, with
differences in the consts, in stdlib.h. To avoid compilation
errors, only prototype getopt for the GNU C library. */
extern int getopt (int argc, char *const *argv, const char *shortopts);
#else /* not __GNU_LIBRARY__ */
extern int getopt ();
#endif /* not __GNU_LIBRARY__ */
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
const struct option *longopts, int *longind);
extern int getopt_long_only (int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind);
/* Internal only. Users should not call this directly. */
extern int _getopt_internal (int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind,
int long_only);
#else /* not __STDC__ */
extern int getopt ();
extern int getopt_long ();
extern int getopt_long_only ();
extern int _getopt_internal ();
#endif /* not __STDC__ */
#ifdef __cplusplus
}
#endif
#endif /* _GETOPT_H */

160
gnu/usr.bin/awk/getopt1.c Normal file
View file

@ -0,0 +1,160 @@
/* Getopt for GNU.
Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
Libiberty is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with libiberty; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA. */
#ifdef LIBC
/* For when compiled as part of the GNU C library. */
#include <ansidecl.h>
#endif
#include "getopt.h"
#ifndef __STDC__
#define const
#endif
#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) || defined (LIBC)
#include <stdlib.h>
#else /* STDC_HEADERS or __GNU_LIBRARY__ */
char *getenv ();
#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
#if !defined (NULL)
#define NULL 0
#endif
int
getopt_long (argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}
/* Like getopt_long, but '-' as well as '--' can indicate a long option.
If an option that starts with '-' (not '--') doesn't match a long option,
but does match a short option, it is parsed as a short option
instead. */
int
getopt_long_only (argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}
#ifdef TEST
#include <stdio.h>
int
main (argc, argv)
int argc;
char **argv;
{
int c;
int digit_optind = 0;
while (1)
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] =
{
{"add", 1, 0, 0},
{"append", 0, 0, 0},
{"delete", 1, 0, 0},
{"verbose", 0, 0, 0},
{"create", 0, 0, 0},
{"file", 1, 0, 0},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, "abc:d:0123456789",
long_options, &option_index);
if (c == EOF)
break;
switch (c)
{
case 0:
printf ("option %s", long_options[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
break;
case 'a':
printf ("option a\n");
break;
case 'b':
printf ("option b\n");
break;
case 'c':
printf ("option c with value `%s'\n", optarg);
break;
case 'd':
printf ("option d with value `%s'\n", optarg);
break;
case '?':
break;
default:
printf ("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
exit (0);
}
#endif /* TEST */

1207
gnu/usr.bin/awk/io.c Normal file

File diff suppressed because it is too large Load diff

318
gnu/usr.bin/awk/iop.c Normal file
View file

@ -0,0 +1,318 @@
/*
* iop.c - do i/o related things.
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
#ifndef atarist
#define INVALID_HANDLE (-1)
#else
#include <stddef.h>
#include <fcntl.h>
#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)
#endif /* atarist */
#ifdef TEST
int bufsize = 8192;
void
fatal(s)
char *s;
{
printf("%s\n", s);
exit(1);
}
#endif
int
optimal_bufsize(fd)
int fd;
{
struct stat stb;
#ifdef VMS
/*
* These values correspond with the RMS multi-block count used by
* vms_open() in vms/vms_misc.c.
*/
if (isatty(fd) > 0)
return BUFSIZ;
else if (fstat(fd, &stb) < 0)
return 8*512; /* conservative in case of DECnet access */
else
return 24*512;
#else
/*
* System V doesn't have the file system block size in the
* stat structure. So we have to make some sort of reasonable
* guess. We use stdio's BUFSIZ, since that is what it was
* meant for in the first place.
*/
#ifdef BLKSIZE_MISSING
#define DEFBLKSIZE BUFSIZ
#else
#define DEFBLKSIZE (stb.st_blksize ? stb.st_blksize : BUFSIZ)
#endif
#ifdef TEST
return bufsize;
#else
#ifndef atarist
if (isatty(fd))
#else
/*
* On ST redirected stdin does not have a name attached
* (this could be hard to do to) and fstat would fail
*/
if (0 == fd || isatty(fd))
#endif /*atarist */
return BUFSIZ;
#ifndef BLKSIZE_MISSING
/* VMS POSIX 1.0: st_blksize is never assigned a value, so zero it */
stb.st_blksize = 0;
#endif
if (fstat(fd, &stb) == -1)
fatal("can't stat fd %d (%s)", fd, strerror(errno));
if (lseek(fd, (off_t)0, 0) == -1)
return DEFBLKSIZE;
return ((int) (stb.st_size < DEFBLKSIZE ? stb.st_size : DEFBLKSIZE));
#endif /*! TEST */
#endif /*! VMS */
}
IOBUF *
iop_alloc(fd)
int fd;
{
IOBUF *iop;
if (fd == INVALID_HANDLE)
return NULL;
emalloc(iop, IOBUF *, sizeof(IOBUF), "iop_alloc");
iop->flag = 0;
if (isatty(fd))
iop->flag |= IOP_IS_TTY;
iop->size = optimal_bufsize(fd);
iop->secsiz = -2;
errno = 0;
iop->fd = fd;
iop->off = iop->buf = NULL;
iop->cnt = 0;
return iop;
}
/*
* Get the next record. Uses a "split buffer" where the latter part is
* the normal read buffer and the head part is an "overflow" area that is used
* when a record spans the end of the normal buffer, in which case the first
* part of the record is copied into the overflow area just before the
* normal buffer. Thus, the eventual full record can be returned as a
* contiguous area of memory with a minimum of copying. The overflow area
* is expanded as needed, so that records are unlimited in length.
* We also mark both the end of the buffer and the end of the read() with
* a sentinel character (the current record separator) so that the inside
* loop can run as a single test.
*/
int
get_a_record(out, iop, grRS, errcode)
char **out;
IOBUF *iop;
register int grRS;
int *errcode;
{
register char *bp = iop->off;
char *bufend;
char *start = iop->off; /* beginning of record */
int saw_newline;
char rs;
int eat_whitespace;
if (iop->cnt == EOF) /* previous read hit EOF */
return EOF;
if (grRS == 0) { /* special case: grRS == "" */
rs = '\n';
eat_whitespace = 0;
saw_newline = 0;
} else
rs = (char) grRS;
/* set up sentinel */
if (iop->buf) {
bufend = iop->buf + iop->size + iop->secsiz;
*bufend = rs;
} else
bufend = NULL;
for (;;) { /* break on end of record, read error or EOF */
/* Following code is entered on the first call of this routine
* for a new iop, or when we scan to the end of the buffer.
* In the latter case, we copy the current partial record to
* the space preceding the normal read buffer. If necessary,
* we expand this space. This is done so that we can return
* the record as a contiguous area of memory.
*/
if ((iop->flag & IOP_IS_INTERNAL) == 0 && bp >= bufend) {
char *oldbuf = NULL;
char *oldsplit = iop->buf + iop->secsiz;
long len; /* record length so far */
if ((iop->flag & IOP_IS_INTERNAL) != 0)
cant_happen();
len = bp - start;
if (len > iop->secsiz) {
/* expand secondary buffer */
if (iop->secsiz == -2)
iop->secsiz = 256;
while (len > iop->secsiz)
iop->secsiz *= 2;
oldbuf = iop->buf;
emalloc(iop->buf, char *,
iop->size+iop->secsiz+2, "get_a_record");
bufend = iop->buf + iop->size + iop->secsiz;
*bufend = rs;
}
if (len > 0) {
char *newsplit = iop->buf + iop->secsiz;
if (start < oldsplit) {
memcpy(newsplit - len, start,
oldsplit - start);
memcpy(newsplit - (bp - oldsplit),
oldsplit, bp - oldsplit);
} else
memcpy(newsplit - len, start, len);
}
bp = iop->end = iop->off = iop->buf + iop->secsiz;
start = bp - len;
if (oldbuf) {
free(oldbuf);
oldbuf = NULL;
}
}
/* Following code is entered whenever we have no more data to
* scan. In most cases this will read into the beginning of
* the main buffer, but in some cases (terminal, pipe etc.)
* we may be doing smallish reads into more advanced positions.
*/
if (bp >= iop->end) {
if ((iop->flag & IOP_IS_INTERNAL) != 0) {
iop->cnt = EOF;
break;
}
iop->cnt = read(iop->fd, iop->end, bufend - iop->end);
if (iop->cnt == -1) {
if (! do_unix && errcode != NULL) {
*errcode = errno;
iop->cnt = EOF;
break;
} else
fatal("error reading input: %s",
strerror(errno));
} else if (iop->cnt == 0) {
iop->cnt = EOF;
break;
}
iop->end += iop->cnt;
*iop->end = rs;
}
if (grRS == 0) {
extern int default_FS;
if (default_FS && (bp == start || eat_whitespace)) {
while (bp < iop->end && isspace(*bp))
bp++;
if (bp == iop->end) {
eat_whitespace = 1;
continue;
} else
eat_whitespace = 0;
}
if (saw_newline && *bp == rs) {
bp++;
break;
}
saw_newline = 0;
}
while (*bp++ != rs)
;
if (bp <= iop->end) {
if (grRS == 0)
saw_newline = 1;
else
break;
} else
bp--;
if ((iop->flag & IOP_IS_INTERNAL) != 0)
iop->cnt = bp - start;
}
if (iop->cnt == EOF
&& (((iop->flag & IOP_IS_INTERNAL) != 0) || start == bp))
return EOF;
iop->off = bp;
bp--;
if (*bp != rs)
bp++;
*bp = '\0';
if (grRS == 0) {
if (*--bp == rs)
*bp = '\0';
else
bp++;
}
*out = start;
return bp - start;
}
#ifdef TEST
main(argc, argv)
int argc;
char *argv[];
{
IOBUF *iop;
char *out;
int cnt;
char rs[2];
rs[0] = 0;
if (argc > 1)
bufsize = atoi(argv[1]);
if (argc > 2)
rs[0] = *argv[2];
iop = iop_alloc(0);
while ((cnt = get_a_record(&out, iop, rs[0], NULL)) > 0) {
fwrite(out, 1, cnt, stdout);
fwrite(rs, 1, 1, stdout);
}
}
#endif

731
gnu/usr.bin/awk/main.c Normal file
View file

@ -0,0 +1,731 @@
/*
* main.c -- Expression tree constructors and main program for gawk.
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "getopt.h"
#include "awk.h"
#include "patchlevel.h"
static void usage P((int exitval));
static void copyleft P((void));
static void cmdline_fs P((char *str));
static void init_args P((int argc0, int argc, char *argv0, char **argv));
static void init_vars P((void));
static void pre_assign P((char *v));
SIGTYPE catchsig P((int sig, int code));
static void gawk_option P((char *optstr));
static void nostalgia P((void));
static void version P((void));
char *gawk_name P((char *filespec));
#ifdef MSDOS
extern int isatty P((int));
#endif
extern void resetup P((void));
/* These nodes store all the special variables AWK uses */
NODE *FS_node, *NF_node, *RS_node, *NR_node;
NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
NODE *CONVFMT_node;
NODE *ERRNO_node;
NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
NODE *ENVIRON_node, *IGNORECASE_node;
NODE *ARGC_node, *ARGV_node, *ARGIND_node;
NODE *FIELDWIDTHS_node;
int NF;
int NR;
int FNR;
int IGNORECASE;
char *RS;
char *OFS;
char *ORS;
char *OFMT;
char *CONVFMT;
/*
* The parse tree and field nodes are stored here. Parse_end is a dummy item
* used to free up unneeded fields without freeing the program being run
*/
int errcount = 0; /* error counter, used by yyerror() */
/* The global null string */
NODE *Nnull_string;
/* The name the program was invoked under, for error messages */
const char *myname;
/* A block of AWK code to be run before running the program */
NODE *begin_block = 0;
/* A block of AWK code to be run after the last input file */
NODE *end_block = 0;
int exiting = 0; /* Was an "exit" statement executed? */
int exit_val = 0; /* optional exit value */
#if defined(YYDEBUG) || defined(DEBUG)
extern int yydebug;
#endif
struct src *srcfiles = NULL; /* source file name(s) */
int numfiles = -1; /* how many source files */
int do_unix = 0; /* turn off gnu extensions */
int do_posix = 0; /* turn off gnu and unix extensions */
int do_lint = 0; /* provide warnings about questionable stuff */
int do_nostalgia = 0; /* provide a blast from the past */
int in_begin_rule = 0; /* we're in a BEGIN rule */
int in_end_rule = 0; /* we're in a END rule */
int output_is_tty = 0; /* control flushing of output */
extern char *version_string; /* current version, for printing */
NODE *expression_value;
static struct option optab[] = {
{ "compat", no_argument, & do_unix, 1 },
{ "lint", no_argument, & do_lint, 1 },
{ "posix", no_argument, & do_posix, 1 },
{ "nostalgia", no_argument, & do_nostalgia, 1 },
{ "copyleft", no_argument, NULL, 'C' },
{ "copyright", no_argument, NULL, 'C' },
{ "field-separator", required_argument, NULL, 'F' },
{ "file", required_argument, NULL, 'f' },
{ "assign", required_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ "usage", no_argument, NULL, 'u' },
{ "help", no_argument, NULL, 'u' },
{ "source", required_argument, NULL, 's' },
#ifdef DEBUG
{ "parsedebug", no_argument, NULL, 'D' },
#endif
{ 0, 0, 0, 0 }
};
int
main(argc, argv)
int argc;
char **argv;
{
int c;
char *scan;
extern int optind;
extern int opterr;
extern char *optarg;
int i;
(void) signal(SIGFPE, (SIGTYPE (*) P((int))) catchsig);
(void) signal(SIGSEGV, (SIGTYPE (*) P((int))) catchsig);
#ifdef SIGBUS
(void) signal(SIGBUS, (SIGTYPE (*) P((int))) catchsig);
#endif
myname = gawk_name(argv[0]);
argv[0] = (char *)myname;
#ifdef VMS
vms_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
#endif
/* remove sccs gunk */
if (strncmp(version_string, "@(#)", 4) == 0)
version_string += 4;
if (argc < 2)
usage(1);
/* initialize the null string */
Nnull_string = make_string("", 0);
Nnull_string->numbr = 0.0;
Nnull_string->type = Node_val;
Nnull_string->flags = (PERM|STR|STRING|NUM|NUMBER);
/* Set up the special variables */
/*
* Note that this must be done BEFORE arg parsing else -F
* breaks horribly
*/
init_vars();
/* worst case */
emalloc(srcfiles, struct src *, argc * sizeof(struct src), "main");
memset(srcfiles, '\0', argc * sizeof(struct src));
/* Tell the regex routines how they should work. . . */
resetup();
/* we do error messages ourselves on invalid options */
opterr = 0;
/* the + on the front tells GNU getopt not to rearrange argv */
while ((c = getopt_long(argc, argv, "+F:f:v:W:", optab, NULL)) != EOF) {
if (do_posix)
opterr = 1;
switch (c) {
case 'F':
cmdline_fs(optarg);
break;
case 'f':
/*
* a la MKS awk, allow multiple -f options.
* this makes function libraries real easy.
* most of the magic is in the scanner.
*/
/* The following is to allow for whitespace at the end
* of a #! /bin/gawk line in an executable file
*/
scan = optarg;
while (isspace(*scan))
scan++;
++numfiles;
srcfiles[numfiles].stype = SOURCEFILE;
if (*scan == '\0')
srcfiles[numfiles].val = argv[optind++];
else
srcfiles[numfiles].val = optarg;
break;
case 'v':
pre_assign(optarg);
break;
case 'W': /* gawk specific options */
gawk_option(optarg);
break;
/* These can only come from long form options */
case 'V':
version();
break;
case 'C':
copyleft();
break;
case 'u':
usage(0);
break;
case 's':
if (strlen(optarg) == 0)
warning("empty argument to --source ignored");
else {
srcfiles[++numfiles].stype = CMDLINE;
srcfiles[numfiles].val = optarg;
}
break;
#ifdef DEBUG
case 'D':
yydebug = 2;
break;
#endif
case '?':
default:
/*
* New behavior. If not posix, an unrecognized
* option stops argument processing so that it can
* go into ARGV for the awk program to see. This
* makes use of ``#! /bin/gawk -f'' easier.
*/
if (! do_posix)
goto out;
/* else
let getopt print error message for us */
break;
}
}
out:
if (do_nostalgia)
nostalgia();
/* POSIX compliance also implies no Unix extensions either */
if (do_posix)
do_unix = 1;
#ifdef DEBUG
setbuf(stdout, (char *) NULL); /* make debugging easier */
#endif
if (isatty(fileno(stdout)))
output_is_tty = 1;
/* No -f or --source options, use next arg */
if (numfiles == -1) {
if (optind > argc - 1) /* no args left */
usage(1);
srcfiles[++numfiles].stype = CMDLINE;
srcfiles[numfiles].val = argv[optind];
optind++;
}
init_args(optind, argc, (char *) myname, argv);
(void) tokexpand();
/* Read in the program */
if (yyparse() || errcount)
exit(1);
/* Set up the field variables */
init_fields();
if (begin_block) {
in_begin_rule = 1;
(void) interpret(begin_block);
}
in_begin_rule = 0;
if (!exiting && (expression_value || end_block))
do_input();
if (end_block) {
in_end_rule = 1;
(void) interpret(end_block);
}
in_end_rule = 0;
if (close_io() != 0 && exit_val == 0)
exit_val = 1;
exit(exit_val); /* more portable */
return exit_val; /* to suppress warnings */
}
/* usage --- print usage information and exit */
static void
usage(exitval)
int exitval;
{
char *opt1 = " -f progfile [--]";
char *opt2 = " [--] 'program'";
char *regops = " [POSIX or GNU style options]";
version();
fprintf(stderr, "usage: %s%s%s file ...\n %s%s%s file ...\n",
myname, regops, opt1, myname, regops, opt2);
/* GNU long options info. Gack. */
fputs("\nPOSIX options:\t\tGNU long options:\n", stderr);
fputs("\t-f progfile\t\t--file=progfile\n", stderr);
fputs("\t-F fs\t\t\t--field-separator=fs\n", stderr);
fputs("\t-v var=val\t\t--assign=var=val\n", stderr);
fputs("\t-W compat\t\t--compat\n", stderr);
fputs("\t-W copyleft\t\t--copyleft\n", stderr);
fputs("\t-W copyright\t\t--copyright\n", stderr);
fputs("\t-W help\t\t\t--help\n", stderr);
fputs("\t-W lint\t\t\t--lint\n", stderr);
#if 0
fputs("\t-W nostalgia\t\t--nostalgia\n", stderr);
#endif
#ifdef DEBUG
fputs("\t-W parsedebug\t\t--parsedebug\n", stderr);
#endif
fputs("\t-W posix\t\t--posix\n", stderr);
fputs("\t-W source=program-text\t--source=program-text\n", stderr);
fputs("\t-W usage\t\t--usage\n", stderr);
fputs("\t-W version\t\t--version\n", stderr);
exit(exitval);
}
static void
copyleft ()
{
static char blurb_part1[] =
"Copyright (C) 1989, 1991, 1992, Free Software Foundation.\n\
\n\
This program is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 2 of the License, or\n\
(at your option) any later version.\n\
\n";
static char blurb_part2[] =
"This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n";
static char blurb_part3[] =
"You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software\n\
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
version();
fputs(blurb_part1, stderr);
fputs(blurb_part2, stderr);
fputs(blurb_part3, stderr);
fflush(stderr);
}
static void
cmdline_fs(str)
char *str;
{
register NODE **tmp;
int len = strlen(str);
tmp = get_lhs(FS_node, (Func_ptr *) 0);
unref(*tmp);
/*
* Only if in full compatibility mode check for the stupid special
* case so -F\t works as documented in awk even though the shell
* hands us -Ft. Bleah!
*
* Thankfully, Posix didn't propogate this "feature".
*/
if (str[0] == 't' && str[1] == '\0') {
if (do_lint)
warning("-Ft does not set FS to tab in POSIX awk");
if (do_unix && ! do_posix)
str[0] = '\t';
}
*tmp = make_str_node(str, len, SCAN); /* do process escapes */
set_FS();
}
static void
init_args(argc0, argc, argv0, argv)
int argc0, argc;
char *argv0;
char **argv;
{
int i, j;
NODE **aptr;
ARGV_node = install("ARGV", node(Nnull_string, Node_var, (NODE *)NULL));
aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
*aptr = make_string(argv0, strlen(argv0));
(*aptr)->flags |= MAYBE_NUM;
for (i = argc0, j = 1; i < argc; i++) {
aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
*aptr = make_string(argv[i], strlen(argv[i]));
(*aptr)->flags |= MAYBE_NUM;
j++;
}
ARGC_node = install("ARGC",
node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
}
/*
* Set all the special variables to their initial values.
*/
struct varinit {
NODE **spec;
char *name;
NODETYPE type;
char *strval;
AWKNUM numval;
Func_ptr assign;
};
static struct varinit varinit[] = {
{&NF_node, "NF", Node_NF, 0, -1, set_NF },
{&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS, "", 0, 0 },
{&NR_node, "NR", Node_NR, 0, 0, set_NR },
{&FNR_node, "FNR", Node_FNR, 0, 0, set_FNR },
{&FS_node, "FS", Node_FS, " ", 0, 0 },
{&RS_node, "RS", Node_RS, "\n", 0, set_RS },
{&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE, 0, 0, set_IGNORECASE },
{&FILENAME_node, "FILENAME", Node_var, "-", 0, 0 },
{&OFS_node, "OFS", Node_OFS, " ", 0, set_OFS },
{&ORS_node, "ORS", Node_ORS, "\n", 0, set_ORS },
{&OFMT_node, "OFMT", Node_OFMT, "%.6g", 0, set_OFMT },
{&CONVFMT_node, "CONVFMT", Node_CONVFMT, "%.6g", 0, set_CONVFMT },
{&RLENGTH_node, "RLENGTH", Node_var, 0, 0, 0 },
{&RSTART_node, "RSTART", Node_var, 0, 0, 0 },
{&SUBSEP_node, "SUBSEP", Node_var, "\034", 0, 0 },
{&ARGIND_node, "ARGIND", Node_var, 0, 0, 0 },
{&ERRNO_node, "ERRNO", Node_var, 0, 0, 0 },
{0, 0, Node_illegal, 0, 0, 0 },
};
static void
init_vars()
{
register struct varinit *vp;
for (vp = varinit; vp->name; vp++) {
*(vp->spec) = install(vp->name,
node(vp->strval == 0 ? make_number(vp->numval)
: make_string(vp->strval, strlen(vp->strval)),
vp->type, (NODE *) NULL));
if (vp->assign)
(*(vp->assign))();
}
}
void
load_environ()
{
#if !defined(MSDOS) && !(defined(VMS) && defined(__DECC))
extern char **environ;
#endif
register char *var, *val;
NODE **aptr;
register int i;
ENVIRON_node = install("ENVIRON",
node(Nnull_string, Node_var, (NODE *) NULL));
for (i = 0; environ[i]; i++) {
static char nullstr[] = "";
var = environ[i];
val = strchr(var, '=');
if (val)
*val++ = '\0';
else
val = nullstr;
aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
*aptr = make_string(val, strlen (val));
(*aptr)->flags |= MAYBE_NUM;
/* restore '=' so that system() gets a valid environment */
if (val != nullstr)
*--val = '=';
}
}
/* Process a command-line assignment */
char *
arg_assign(arg)
char *arg;
{
char *cp;
Func_ptr after_assign = NULL;
NODE *var;
NODE *it;
NODE **lhs;
cp = strchr(arg, '=');
if (cp != NULL) {
*cp++ = '\0';
/*
* Recent versions of nawk expand escapes inside assignments.
* This makes sense, so we do it too.
*/
it = make_str_node(cp, strlen(cp), SCAN);
it->flags |= MAYBE_NUM;
var = variable(arg, 0);
lhs = get_lhs(var, &after_assign);
unref(*lhs);
*lhs = it;
if (after_assign)
(*after_assign)();
*--cp = '='; /* restore original text of ARGV */
}
return cp;
}
static void
pre_assign(v)
char *v;
{
if (!arg_assign(v)) {
fprintf (stderr,
"%s: '%s' argument to -v not in 'var=value' form\n",
myname, v);
usage(1);
}
}
SIGTYPE
catchsig(sig, code)
int sig, code;
{
#ifdef lint
code = 0; sig = code; code = sig;
#endif
if (sig == SIGFPE) {
fatal("floating point exception");
} else if (sig == SIGSEGV
#ifdef SIGBUS
|| sig == SIGBUS
#endif
) {
msg("fatal error: internal error");
/* fatal won't abort() if not compiled for debugging */
abort();
} else
cant_happen();
/* NOTREACHED */
}
/* gawk_option --- do gawk specific things */
static void
gawk_option(optstr)
char *optstr;
{
char *cp;
for (cp = optstr; *cp; cp++) {
switch (*cp) {
case ' ':
case '\t':
case ',':
break;
case 'v':
case 'V':
/* print version */
if (strncasecmp(cp, "version", 7) != 0)
goto unknown;
else
cp += 6;
version();
break;
case 'c':
case 'C':
if (strncasecmp(cp, "copyright", 9) == 0) {
cp += 8;
copyleft();
} else if (strncasecmp(cp, "copyleft", 8) == 0) {
cp += 7;
copyleft();
} else if (strncasecmp(cp, "compat", 6) == 0) {
cp += 5;
do_unix = 1;
} else
goto unknown;
break;
case 'n':
case 'N':
/*
* Undocumented feature,
* inspired by nostalgia, and a T-shirt
*/
if (strncasecmp(cp, "nostalgia", 9) != 0)
goto unknown;
nostalgia();
break;
case 'p':
case 'P':
#ifdef DEBUG
if (strncasecmp(cp, "parsedebug", 10) == 0) {
cp += 9;
yydebug = 2;
break;
}
#endif
if (strncasecmp(cp, "posix", 5) != 0)
goto unknown;
cp += 4;
do_posix = do_unix = 1;
break;
case 'l':
case 'L':
if (strncasecmp(cp, "lint", 4) != 0)
goto unknown;
cp += 3;
do_lint = 1;
break;
case 'H':
case 'h':
if (strncasecmp(cp, "help", 4) != 0)
goto unknown;
cp += 3;
usage(0);
break;
case 'U':
case 'u':
if (strncasecmp(cp, "usage", 5) != 0)
goto unknown;
cp += 4;
usage(0);
break;
case 's':
case 'S':
if (strncasecmp(cp, "source=", 7) != 0)
goto unknown;
cp += 7;
if (strlen(cp) == 0)
warning("empty argument to -Wsource ignored");
else {
srcfiles[++numfiles].stype = CMDLINE;
srcfiles[numfiles].val = cp;
return;
}
break;
default:
unknown:
fprintf(stderr, "'%c' -- unknown option, ignored\n",
*cp);
break;
}
}
}
/* nostalgia --- print the famous error message and die */
static void
nostalgia()
{
fprintf(stderr, "awk: bailing out near line 1\n");
abort();
}
/* version --- print version message */
static void
version()
{
fprintf(stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
}
/* static */
char *
gawk_name(filespec)
char *filespec;
{
char *p;
#ifdef VMS /* "device:[root.][directory.subdir]GAWK.EXE;n" -> "GAWK" */
char *q;
p = strrchr(filespec, ']'); /* directory punctuation */
q = strrchr(filespec, '>'); /* alternate <international> punct */
if (p == NULL || q > p) p = q;
p = strdup(p == NULL ? filespec : (p + 1));
if ((q = strrchr(p, '.')) != NULL) *q = '\0'; /* strip .typ;vers */
return p;
#endif /*VMS*/
#if defined(MSDOS) || defined(atarist)
char *q;
p = filespec;
if (q = strrchr(p, '\\'))
p = q + 1;
if (q = strchr(p, '.'))
*q = '\0';
strlwr(p);
return (p == NULL ? filespec : p);
#endif /* MSDOS || atarist */
/* "path/name" -> "name" */
p = strrchr(filespec, '/');
return (p == NULL ? filespec : p + 1);
}

106
gnu/usr.bin/awk/msg.c Normal file
View file

@ -0,0 +1,106 @@
/*
* msg.c - routines for error messages
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
int sourceline = 0;
char *source = NULL;
/* VARARGS2 */
void
err(s, emsg, argp)
char *s;
char *emsg;
va_list argp;
{
char *file;
(void) fflush(stdout);
(void) fprintf(stderr, "%s: ", myname);
if (sourceline) {
if (source)
(void) fprintf(stderr, "%s:", source);
else
(void) fprintf(stderr, "cmd. line:");
(void) fprintf(stderr, "%d: ", sourceline);
}
if (FNR) {
file = FILENAME_node->var_value->stptr;
if (file)
(void) fprintf(stderr, "(FILENAME=%s ", file);
(void) fprintf(stderr, "FNR=%d) ", FNR);
}
(void) fprintf(stderr, s);
vfprintf(stderr, emsg, argp);
(void) fprintf(stderr, "\n");
(void) fflush(stderr);
}
/*VARARGS0*/
void
msg(va_alist)
va_dcl
{
va_list args;
char *mesg;
va_start(args);
mesg = va_arg(args, char *);
err("", mesg, args);
va_end(args);
}
/*VARARGS0*/
void
warning(va_alist)
va_dcl
{
va_list args;
char *mesg;
va_start(args);
mesg = va_arg(args, char *);
err("warning: ", mesg, args);
va_end(args);
}
/*VARARGS0*/
void
fatal(va_alist)
va_dcl
{
va_list args;
char *mesg;
va_start(args);
mesg = va_arg(args, char *);
err("fatal: ", mesg, args);
va_end(args);
#ifdef DEBUG
abort();
#endif
exit(2);
}

429
gnu/usr.bin/awk/node.c Normal file
View file

@ -0,0 +1,429 @@
/*
* node.c -- routines for node management
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
extern double strtod();
AWKNUM
r_force_number(n)
register NODE *n;
{
register char *cp;
register char *cpend;
char save;
char *ptr;
unsigned int newflags = 0;
#ifdef DEBUG
if (n == NULL)
cant_happen();
if (n->type != Node_val)
cant_happen();
if(n->flags == 0)
cant_happen();
if (n->flags & NUM)
return n->numbr;
#endif
/* all the conditionals are an attempt to avoid the expensive strtod */
n->numbr = 0.0;
n->flags |= NUM;
if (n->stlen == 0)
return 0.0;
cp = n->stptr;
if (isalpha(*cp))
return 0.0;
cpend = cp + n->stlen;
while (cp < cpend && isspace(*cp))
cp++;
if (cp == cpend || isalpha(*cp))
return 0.0;
if (n->flags & MAYBE_NUM) {
newflags = NUMBER;
n->flags &= ~MAYBE_NUM;
}
if (cpend - cp == 1) {
if (isdigit(*cp)) {
n->numbr = (AWKNUM)(*cp - '0');
n->flags |= newflags;
}
return n->numbr;
}
errno = 0;
save = *cpend;
*cpend = '\0';
n->numbr = (AWKNUM) strtod((const char *)cp, &ptr);
/* POSIX says trailing space is OK for NUMBER */
while (isspace(*ptr))
ptr++;
*cpend = save;
/* the >= should be ==, but for SunOS 3.5 strtod() */
if (errno == 0 && ptr >= cpend)
n->flags |= newflags;
else
errno = 0;
return n->numbr;
}
/*
* the following lookup table is used as an optimization in force_string
* (more complicated) variations on this theme didn't seem to pay off, but
* systematic testing might be in order at some point
*/
static char *values[] = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
};
#define NVAL (sizeof(values)/sizeof(values[0]))
NODE *
r_force_string(s)
register NODE *s;
{
char buf[128];
register char *sp = buf;
register long num = 0;
#ifdef DEBUG
if (s == NULL) cant_happen();
if (s->type != Node_val) cant_happen();
if (s->flags & STR) return s;
if (!(s->flags & NUM)) cant_happen();
if (s->stref != 0) ; /*cant_happen();*/
#endif
/* avoids floating point exception in DOS*/
if ( s->numbr <= LONG_MAX && s->numbr >= -LONG_MAX)
num = (long)s->numbr;
if ((AWKNUM) num == s->numbr) { /* integral value */
if (num < NVAL && num >= 0) {
sp = values[num];
s->stlen = 1;
} else {
(void) sprintf(sp, "%ld", num);
s->stlen = strlen(sp);
}
s->stfmt = -1;
} else {
(void) sprintf(sp, CONVFMT, s->numbr);
s->stlen = strlen(sp);
s->stfmt = (char)CONVFMTidx;
}
s->stref = 1;
emalloc(s->stptr, char *, s->stlen + 2, "force_string");
memcpy(s->stptr, sp, s->stlen+1);
s->flags |= STR;
return s;
}
/*
* Duplicate a node. (For strings, "duplicate" means crank up the
* reference count.)
*/
NODE *
dupnode(n)
NODE *n;
{
register NODE *r;
if (n->flags & TEMP) {
n->flags &= ~TEMP;
n->flags |= MALLOC;
return n;
}
if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
if (n->stref < 255)
n->stref++;
return n;
}
getnode(r);
*r = *n;
r->flags &= ~(PERM|TEMP);
r->flags |= MALLOC;
if (n->type == Node_val && (n->flags & STR)) {
r->stref = 1;
emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
memcpy(r->stptr, n->stptr, r->stlen+1);
}
return r;
}
/* this allocates a node with defined numbr */
NODE *
mk_number(x, flags)
AWKNUM x;
unsigned int flags;
{
register NODE *r;
getnode(r);
r->type = Node_val;
r->numbr = x;
r->flags = flags;
#ifdef DEBUG
r->stref = 1;
r->stptr = 0;
r->stlen = 0;
#endif
return r;
}
/*
* Make a string node.
*/
NODE *
make_str_node(s, len, flags)
char *s;
size_t len;
int flags;
{
register NODE *r;
getnode(r);
r->type = Node_val;
r->flags = (STRING|STR|MALLOC);
if (flags & ALREADY_MALLOCED)
r->stptr = s;
else {
emalloc(r->stptr, char *, len + 2, s);
memcpy(r->stptr, s, len);
}
r->stptr[len] = '\0';
if (flags & SCAN) { /* scan for escape sequences */
char *pf;
register char *ptm;
register int c;
register char *end;
end = &(r->stptr[len]);
for (pf = ptm = r->stptr; pf < end;) {
c = *pf++;
if (c == '\\') {
c = parse_escape(&pf);
if (c < 0) {
if (do_lint)
warning("backslash at end of string");
c = '\\';
}
*ptm++ = c;
} else
*ptm++ = c;
}
len = ptm - r->stptr;
erealloc(r->stptr, char *, len + 1, "make_str_node");
r->stptr[len] = '\0';
r->flags |= PERM;
}
r->stlen = len;
r->stref = 1;
r->stfmt = -1;
return r;
}
NODE *
tmp_string(s, len)
char *s;
size_t len;
{
register NODE *r;
r = make_string(s, len);
r->flags |= TEMP;
return r;
}
#define NODECHUNK 100
NODE *nextfree = NULL;
NODE *
more_nodes()
{
register NODE *np;
/* get more nodes and initialize list */
emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
for (np = nextfree; np < &nextfree[NODECHUNK - 1]; np++)
np->nextp = np + 1;
np->nextp = NULL;
np = nextfree;
nextfree = nextfree->nextp;
return np;
}
#ifdef DEBUG
void
freenode(it)
NODE *it;
{
#ifdef MPROF
it->stref = 0;
free((char *) it);
#else /* not MPROF */
/* add it to head of freelist */
it->nextp = nextfree;
nextfree = it;
#endif /* not MPROF */
}
#endif /* DEBUG */
void
unref(tmp)
register NODE *tmp;
{
if (tmp == NULL)
return;
if (tmp->flags & PERM)
return;
if (tmp->flags & (MALLOC|TEMP)) {
tmp->flags &= ~TEMP;
if (tmp->flags & STR) {
if (tmp->stref > 1) {
if (tmp->stref != 255)
tmp->stref--;
return;
}
free(tmp->stptr);
}
freenode(tmp);
}
}
/*
* Parse a C escape sequence. STRING_PTR points to a variable containing a
* pointer to the string to parse. That pointer is updated past the
* characters we use. The value of the escape sequence is returned.
*
* A negative value means the sequence \ newline was seen, which is supposed to
* be equivalent to nothing at all.
*
* If \ is followed by a null character, we return a negative value and leave
* the string pointer pointing at the null character.
*
* If \ is followed by 000, we return 0 and leave the string pointer after the
* zeros. A value of 0 does not mean end of string.
*
* Posix doesn't allow \x.
*/
int
parse_escape(string_ptr)
char **string_ptr;
{
register int c = *(*string_ptr)++;
register int i;
register int count;
switch (c) {
case 'a':
return BELL;
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
case '\n':
return -2;
case 0:
(*string_ptr)--;
return -1;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
i = c - '0';
count = 0;
while (++count < 3) {
if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
i *= 8;
i += c - '0';
} else {
(*string_ptr)--;
break;
}
}
return i;
case 'x':
if (do_lint) {
static int didwarn;
if (! didwarn) {
didwarn = 1;
warning("Posix does not allow \"\\x\" escapes");
}
}
if (do_posix)
return ('x');
i = 0;
while (1) {
if (isxdigit((c = *(*string_ptr)++))) {
i *= 16;
if (isdigit(c))
i += c - '0';
else if (isupper(c))
i += c - 'A' + 10;
else
i += c - 'a' + 10;
} else {
(*string_ptr)--;
break;
}
}
return i;
default:
return c;
}
}

View file

@ -0,0 +1 @@
#define PATCHLEVEL 2

115
gnu/usr.bin/awk/protos.h Normal file
View file

@ -0,0 +1,115 @@
/*
* protos.h -- function prototypes for when the headers don't have them.
*/
/*
* Copyright (C) 1991, 1992, the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __STDC__
#define aptr_t void * /* arbitrary pointer type */
#else
#define aptr_t char *
#endif
extern aptr_t malloc P((MALLOC_ARG_T));
extern aptr_t realloc P((aptr_t, MALLOC_ARG_T));
extern aptr_t calloc P((MALLOC_ARG_T, MALLOC_ARG_T));
extern void free P((aptr_t));
extern char *getenv P((char *));
extern char *strcpy P((char *, const char *));
extern char *strcat P((char *, const char *));
extern char *strncpy P((char *, const char *, int));
extern int strcmp P((const char *, const char *));
extern int strncmp P((const char *, const char *, int));
#ifndef VMS
extern char *strerror P((int));
#else
extern char *strerror P((int,...));
#endif
extern char *strchr P((const char *, int));
extern char *strrchr P((const char *, int));
extern char *strstr P((const char *s1, const char *s2));
extern int strlen P((const char *));
extern long strtol P((const char *, char **, int));
#if !defined(_MSC_VER) && !defined(__GNU_LIBRARY__)
extern int strftime P((char *, int, const char *, const struct tm *));
#endif
extern time_t time P((time_t *));
extern aptr_t memset P((aptr_t, int, size_t));
extern aptr_t memcpy P((aptr_t, const aptr_t, size_t));
extern aptr_t memmove P((aptr_t, const aptr_t, size_t));
extern aptr_t memchr P((const aptr_t, int, size_t));
extern int memcmp P((const aptr_t, const aptr_t, size_t));
/* extern int fprintf P((FILE *, char *, ...)); */
extern int fprintf P(());
#if !defined(MSDOS) && !defined(__GNU_LIBRARY__)
extern int fwrite P((const char *, int, int, FILE *));
extern int fputs P((const char *, FILE *));
extern int unlink P((const char *));
#endif
extern int fflush P((FILE *));
extern int fclose P((FILE *));
extern FILE *popen P((const char *, const char *));
extern int pclose P((FILE *));
extern void abort P(());
extern int isatty P((int));
extern void exit P((int));
extern int system P((const char *));
extern int sscanf P((/* char *, char *, ... */));
#ifndef toupper
extern int toupper P((int));
#endif
#ifndef tolower
extern int tolower P((int));
#endif
extern double pow P((double x, double y));
extern double atof P((char *));
extern double strtod P((const char *, char **));
extern int fstat P((int, struct stat *));
extern int stat P((const char *, struct stat *));
extern off_t lseek P((int, off_t, int));
extern int fseek P((FILE *, long, int));
extern int close P((int));
extern int creat P(());
extern int open P(());
extern int pipe P((int *));
extern int dup P((int));
extern int dup2 P((int,int));
extern int fork P(());
extern int execl P((/* char *, char *, ... */));
extern int read P((int, char *, int));
extern int wait P((int *));
extern void _exit P((int));
#ifndef __STDC__
extern long time P((long *));
#endif
#ifdef NON_STD_SPRINTF
extern char *sprintf();
#else
extern int sprintf();
#endif /* SPRINTF_INT */
#undef aptr_t

208
gnu/usr.bin/awk/re.c Normal file
View file

@ -0,0 +1,208 @@
/*
* re.c - compile regular expressions.
*/
/*
* Copyright (C) 1991, 1992 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h"
/* Generate compiled regular expressions */
Regexp *
make_regexp(s, len, ignorecase, dfa)
char *s;
int len;
int ignorecase;
int dfa;
{
Regexp *rp;
char *err;
char *src = s;
char *temp;
char *end = s + len;
register char *dest;
register int c;
/* Handle escaped characters first. */
/* Build a copy of the string (in dest) with the
escaped characters translated, and generate the regex
from that.
*/
emalloc(dest, char *, len + 2, "make_regexp");
temp = dest;
while (src < end) {
if (*src == '\\') {
c = *++src;
switch (c) {
case 'a':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
case 'x':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = parse_escape(&src);
if (c < 0)
cant_happen();
*dest++ = (char)c;
break;
default:
*dest++ = '\\';
*dest++ = (char)c;
src++;
break;
} /* switch */
} else {
*dest++ = *src++; /* not '\\' */
}
} /* for */
*dest = '\0' ; /* Only necessary if we print dest ? */
emalloc(rp, Regexp *, sizeof(*rp), "make_regexp");
memset((char *) rp, 0, sizeof(*rp));
emalloc(rp->pat.buffer, char *, 16, "make_regexp");
rp->pat.allocated = 16;
emalloc(rp->pat.fastmap, char *, 256, "make_regexp");
if (ignorecase)
rp->pat.translate = casetable;
else
rp->pat.translate = NULL;
len = dest - temp;
if ((err = re_compile_pattern(temp, (size_t) len, &(rp->pat))) != NULL)
fatal("%s: /%s/", err, temp);
if (dfa && !ignorecase) {
regcompile(temp, len, &(rp->dfareg), 1);
rp->dfa = 1;
} else
rp->dfa = 0;
free(temp);
return rp;
}
int
research(rp, str, start, len, need_start)
Regexp *rp;
register char *str;
int start;
register int len;
int need_start;
{
char *ret = str;
if (rp->dfa) {
char save1;
char save2;
int count = 0;
int try_backref;
save1 = str[start+len];
str[start+len] = '\n';
save2 = str[start+len+1];
ret = regexecute(&(rp->dfareg), str+start, str+start+len+1, 1,
&count, &try_backref);
str[start+len] = save1;
str[start+len+1] = save2;
}
if (ret) {
if (need_start || rp->dfa == 0)
return re_search(&(rp->pat), str, start+len, start,
len, &(rp->regs));
else
return 1;
} else
return -1;
}
void
refree(rp)
Regexp *rp;
{
free(rp->pat.buffer);
free(rp->pat.fastmap);
if (rp->dfa)
reg_free(&(rp->dfareg));
free(rp);
}
void
reg_error(s)
const char *s;
{
fatal(s);
}
Regexp *
re_update(t)
NODE *t;
{
NODE *t1;
# define CASE 1
if ((t->re_flags & CASE) == IGNORECASE) {
if (t->re_flags & CONST)
return t->re_reg;
t1 = force_string(tree_eval(t->re_exp));
if (t->re_text) {
if (cmp_nodes(t->re_text, t1) == 0) {
free_temp(t1);
return t->re_reg;
}
unref(t->re_text);
}
t->re_text = dupnode(t1);
free_temp(t1);
}
if (t->re_reg)
refree(t->re_reg);
if (t->re_cnt)
t->re_cnt++;
if (t->re_cnt > 10)
t->re_cnt = 0;
if (!t->re_text) {
t1 = force_string(tree_eval(t->re_exp));
t->re_text = dupnode(t1);
free_temp(t1);
}
t->re_reg = make_regexp(t->re_text->stptr, t->re_text->stlen, IGNORECASE, t->re_cnt);
t->re_flags &= ~CASE;
t->re_flags |= IGNORECASE;
return t->re_reg;
}
void
resetup()
{
(void) re_set_syntax(RE_SYNTAX_AWK);
regsyntax(RE_SYNTAX_AWK, 0);
}

2854
gnu/usr.bin/awk/regex.c Normal file

File diff suppressed because it is too large Load diff

260
gnu/usr.bin/awk/regex.h Normal file
View file

@ -0,0 +1,260 @@
/* Definitions for data structures callers pass the regex library.
Copyright (C) 1985, 1989-90 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef __REGEXP_LIBRARY
#define __REGEXP_LIBRARY
/* Define number of parens for which we record the beginnings and ends.
This affects how much space the `struct re_registers' type takes up. */
#ifndef RE_NREGS
#define RE_NREGS 10
#endif
#define BYTEWIDTH 8
/* Maximum number of duplicates an interval can allow. */
#ifndef RE_DUP_MAX
#define RE_DUP_MAX ((1 << 15) - 1)
#endif
/* This defines the various regexp syntaxes. */
extern long obscure_syntax;
/* The following bits are used in the obscure_syntax variable to choose among
alternative regexp syntaxes. */
/* If this bit is set, plain parentheses serve as grouping, and backslash
parentheses are needed for literal searching.
If not set, backslash-parentheses are grouping, and plain parentheses
are for literal searching. */
#define RE_NO_BK_PARENS 1L
/* If this bit is set, plain | serves as the `or'-operator, and \| is a
literal.
If not set, \| serves as the `or'-operator, and | is a literal. */
#define RE_NO_BK_VBAR (1L << 1)
/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are
literals.
If set, \+, \? are operators and plain +, ? are literals. */
#define RE_BK_PLUS_QM (1L << 2)
/* If this bit is set, | binds tighter than ^ or $.
If not set, the contrary. */
#define RE_TIGHT_VBAR (1L << 3)
/* If this bit is set, then treat newline as an OR operator.
If not set, treat it as a normal character. */
#define RE_NEWLINE_OR (1L << 4)
/* If this bit is set, then special characters may act as normal
characters in some contexts. Specifically, this applies to:
^ -- only special at the beginning, or after ( or |;
$ -- only special at the end, or before ) or |;
*, +, ? -- only special when not after the beginning, (, or |.
If this bit is not set, special characters (such as *, ^, and $)
always have their special meaning regardless of the surrounding
context. */
#define RE_CONTEXT_INDEP_OPS (1L << 5)
/* If this bit is not set, then \ before anything inside [ and ] is taken as
a real \.
If set, then such a \ escapes the following character. This is a
special case for awk. */
#define RE_AWK_CLASS_HACK (1L << 6)
/* If this bit is set, then \{ and \} or { and } serve as interval operators.
If not set, then \{ and \} and { and } are treated as literals. */
#define RE_INTERVALS (1L << 7)
/* If this bit is not set, then \{ and \} serve as interval operators and
{ and } are literals.
If set, then { and } serve as interval operators and \{ and \} are
literals. */
#define RE_NO_BK_CURLY_BRACES (1L << 8)
/* If this bit is set, then character classes are supported; they are:
[:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
[:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
If not set, then character classes are not supported. */
#define RE_CHAR_CLASSES (1L << 9)
/* If this bit is set, then the dot re doesn't match a null byte.
If not set, it does. */
#define RE_DOT_NOT_NULL (1L << 10)
/* If this bit is set, then [^...] doesn't match a newline.
If not set, it does. */
#define RE_HAT_NOT_NEWLINE (1L << 11)
/* If this bit is set, back references are recognized.
If not set, they aren't. */
#define RE_NO_BK_REFS (1L << 12)
/* If this bit is set, back references must refer to a preceding
subexpression. If not set, a back reference to a nonexistent
subexpression is treated as literal characters. */
#define RE_NO_EMPTY_BK_REF (1L << 13)
/* If this bit is set, bracket expressions can't be empty.
If it is set, they can be empty. */
#define RE_NO_EMPTY_BRACKETS (1L << 14)
/* If this bit is set, then *, +, ? and { cannot be first in an re or
immediately after a |, or a (. Furthermore, a | cannot be first or
last in an re, or immediately follow another | or a (. Also, a ^
cannot appear in a nonleading position and a $ cannot appear in a
nontrailing position (outside of bracket expressions, that is). */
#define RE_CONTEXTUAL_INVALID_OPS (1L << 15)
/* If this bit is set, then +, ? and | aren't recognized as operators.
If it's not, they are. */
#define RE_LIMITED_OPS (1L << 16)
/* If this bit is set, then an ending range point has to collate higher
or equal to the starting range point.
If it's not set, then when the ending range point collates higher
than the starting range point, the range is just considered empty. */
#define RE_NO_EMPTY_RANGES (1L << 17)
/* If this bit is set, then a hyphen (-) can't be an ending range point.
If it isn't, then it can. */
#define RE_NO_HYPHEN_RANGE_END (1L << 18)
/* Define combinations of bits for the standard possibilities. */
#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \
| RE_CONTEXT_INDEP_OPS)
#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_AWK_CLASS_HACK)
#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \
| RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR)
#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
#define RE_SYNTAX_EMACS 0
#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \
| RE_CHAR_CLASSES | RE_DOT_NOT_NULL \
| RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \
| RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \
| RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END)
#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \
| RE_NO_BK_VBAR | RE_NO_BK_PARENS \
| RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \
| RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \
| RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \
| RE_NO_HYPHEN_RANGE_END)
/* This data structure is used to represent a compiled pattern. */
struct re_pattern_buffer
{
char *buffer; /* Space holding the compiled pattern commands. */
long allocated; /* Size of space that `buffer' points to. */
long used; /* Length of portion of buffer actually occupied */
char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
/* re_search uses the fastmap, if there is one,
to skip over totally implausible characters. */
char *translate; /* Translate table to apply to all characters before
comparing, or zero for no translation.
The translation is applied to a pattern when it is
compiled and to data when it is matched. */
char fastmap_accurate;
/* Set to zero when a new pattern is stored,
set to one when the fastmap is updated from it. */
char can_be_null; /* Set to one by compiling fastmap
if this pattern might match the null string.
It does not necessarily match the null string
in that case, but if this is zero, it cannot.
2 as value means can match null string
but at end of range or before a character
listed in the fastmap. */
};
/* search.c (search_buffer) needs this one value. It is defined both in
regex.c and here. */
#define RE_EXACTN_VALUE 1
/* Structure to store register contents data in.
Pass the address of such a structure as an argument to re_match, etc.,
if you want this information back.
For i from 1 to RE_NREGS - 1, start[i] records the starting index in
the string of where the ith subexpression matched, and end[i] records
one after the ending index. start[0] and end[0] are analogous, for
the entire pattern. */
struct re_registers
{
int start[RE_NREGS];
int end[RE_NREGS];
};
#ifdef __STDC__
extern char *re_compile_pattern (char *, size_t, struct re_pattern_buffer *);
/* Is this really advertised? */
extern void re_compile_fastmap (struct re_pattern_buffer *);
extern int re_search (struct re_pattern_buffer *, char*, int, int, int,
struct re_registers *);
extern int re_search_2 (struct re_pattern_buffer *, char *, int,
char *, int, int, int,
struct re_registers *, int);
extern int re_match (struct re_pattern_buffer *, char *, int, int,
struct re_registers *);
extern int re_match_2 (struct re_pattern_buffer *, char *, int,
char *, int, int, struct re_registers *, int);
extern long re_set_syntax (long syntax);
#ifndef GAWK
/* 4.2 bsd compatibility. */
extern char *re_comp (char *);
extern int re_exec (char *);
#endif
#else /* !__STDC__ */
extern char *re_compile_pattern ();
/* Is this really advertised? */
extern void re_compile_fastmap ();
extern int re_search (), re_search_2 ();
extern int re_match (), re_match_2 ();
extern long re_set_syntax();
#ifndef GAWK
/* 4.2 bsd compatibility. */
extern char *re_comp ();
extern int re_exec ();
#endif
#endif /* __STDC__ */
#ifdef SYNTAX_TABLE
extern char *re_syntax_table;
#endif
#endif /* !__REGEXP_LIBRARY */

46
gnu/usr.bin/awk/version.c Normal file
View file

@ -0,0 +1,46 @@
char *version_string = "@(#)Gnu Awk (gawk) 2.15";
/* 1.02 fixed /= += *= etc to return the new Left Hand Side instead
of the Right Hand Side */
/* 1.03 Fixed split() to treat strings of space and tab as FS if
the split char is ' '.
Added -v option to print version number
Fixed bug that caused rounding when printing large numbers */
/* 2.00beta Incorporated the functionality of the "new" awk as described
the book (reference not handy). Extensively tested, but no
doubt still buggy. Badly needs tuning and cleanup, in
particular in memory management which is currently almost
non-existent. */
/* 2.01 JF: Modified to compile under GCC, and fixed a few
bugs while I was at it. I hope I didn't add any more.
I modified parse.y to reduce the number of reduce/reduce
conflicts. There are still a few left. */
/* 2.02 Fixed JF's bugs; improved memory management, still needs
lots of work. */
/* 2.10 Major grammar rework and lots of bug fixes from David.
Major changes for performance enhancements from David.
A number of minor bug fixes and new features from Arnold.
Changes for MSDOS from Conrad Kwok and Scott Garfinkle.
The gawk.texinfo and info files included! */
/* 2.11 Bug fix release to 2.10. Lots of changes for portability,
speed, and configurability. */
/* 2.12 Lots of changes for portability, speed, and configurability.
Several bugs fixed. POSIX compliance. Removal of last set
of hard-wired limits. Atari and VMS ports added. */
/* 2.13 Public release of 2.12 */
/* 2.14 Mostly bug fixes. */
/* 2.15 Bug fixes plus intermixing of command-line source and files,
GNU long options, ARGIND, ERRNO and Plan 9 style /dev/ files. */

3
gnu/usr.bin/rcs/Makefile Normal file
View file

@ -0,0 +1,3 @@
SUBDIR= lib ci co ident merge rcs rcsdiff rcsmerge rlog rcsfreeze
.include <bsd.subdir.mk>

View file

@ -0,0 +1,3 @@
# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
BINDIR?= /usr/bin

View file

@ -0,0 +1,7 @@
PROG= ci
SRCS= ci.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

772
gnu/usr.bin/rcs/ci/ci.1 Normal file
View file

@ -0,0 +1,772 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: ci.1,v 5.9 1991/10/07 17:32:46 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH CI 1 \*(Dt GNU
.SH NAME
ci \- check in RCS revisions
.SH SYNOPSIS
.B ci
.RI [ options ] " file " .\|.\|.
.SH DESCRIPTION
.B ci
stores new revisions into \*r files.
Each pathname matching an \*r suffix
is taken to be an \*r file.
All others
are assumed to be working files containing new revisions.
.B ci
deposits the contents of each working file
into the corresponding \*r file.
If only a working file is given,
.B ci
tries to find the corresponding \*r file in an \*r subdirectory
and then in the working file's directory.
For more details, see
.SM "FILE NAMING"
below.
.PP
For
.B ci
to work, the caller's login must be on the access list,
except if the access list is empty or the caller is the superuser or the
owner of the file.
To append a new revision to an existing branch, the tip revision on
that branch must be locked by the caller. Otherwise, only a
new branch can be created. This restriction is not enforced
for the owner of the file if non-strict locking is used
(see
.BR rcs (1)).
A lock held by someone else may be broken with the
.B rcs
command.
.PP
Unless the
.B \-f
option is given,
.B ci
checks whether the revision to be deposited differs from the preceding one.
If not, instead of creating a new revision
.B ci
reverts to the preceding one.
To revert, ordinary
.B ci
removes the working file and any lock;
.B "ci\ \-l"
keeps and
.B "ci\ \-u"
removes any lock, and then they both generate a new working file much as if
.B "co\ \-l"
or
.B "co\ \-u"
had been applied to the preceding revision.
When reverting, any
.B \-n
and
.B \-s
options apply to the preceding revision.
.PP
For each revision deposited,
.B ci
prompts for a log message.
The log message should summarize the change and must be terminated by
end-of-file or by a line containing
.BR \&. "\ by"
itself.
If several files are checked in
.B ci
asks whether to reuse the
previous log message.
If the standard input is not a terminal,
.B ci
suppresses the prompt
and uses the same log message for all files.
See also
.BR \-m .
.PP
If the \*r file does not exist,
.B ci
creates it and
deposits the contents of the working file as the initial revision
(default number:
.BR 1.1 ).
The access list is initialized to empty.
Instead of the log message,
.B ci
requests descriptive text (see
.B \-t
below).
.PP
The number
.I rev
of the deposited revision can be given by any of the options
.BR \-f ,
.BR \-I ,
.BR \-k ,
.BR \-l ,
.BR \-M ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
.I rev
may be symbolic, numeric, or mixed.
If
.I rev
is
.BR $ ,
.B ci
determines the revision number from keyword values in the working file.
.PP
If
.I rev
is a revision number, it must be higher than the latest
one on the branch to which
.I rev
belongs, or must start a new branch.
.PP
If
.I rev
is a branch rather than a revision number,
the new revision is appended to that branch. The level number is obtained
by incrementing the tip revision number of that branch.
If
.I rev
indicates a non-existing branch,
that branch is created with the initial revision numbered
.IB rev .1\f1.\fP
.br
.ne 8
.PP
If
.I rev
is omitted,
.B ci
tries to derive the new revision number from
the caller's last lock. If the caller has locked the tip revision of a branch,
the new revision is appended to that branch.
The new revision number is obtained
by incrementing the tip revision number.
If the caller locked a non-tip revision, a new branch is started at
that revision by incrementing the highest branch number at that revision.
The default initial branch and level numbers are
.BR 1 .
.PP
If
.I rev
is omitted and the caller has no lock, but owns
the file and locking
is not set to
.IR strict ,
then the revision is appended to the
default branch (normally the trunk; see the
.B \-b
option of
.BR rcs (1)).
.PP
Exception: On the trunk, revisions can be appended to the end, but
not inserted.
.SH OPTIONS
.TP
.BR \-r [\f2rev\fP]
checks in a revision, releases the corresponding lock, and
removes the working file. This is the default.
.RS
.PP
The
.B \-r
option has an unusual meaning in
.BR ci .
In other \*r commands,
.B \-r
merely specifies a revision number,
but in
.B ci
it also releases a lock and removes the working file.
See
.B \-u
for a tricky example.
.RE
.TP
.BR \-l [\f2rev\fP]
works like
.BR \-r ,
except it performs an additional
.B "co\ \-l"
for the
deposited revision. Thus, the deposited revision is immediately
checked out again and locked.
This is useful for saving a revision although one wants to continue
editing it after the checkin.
.TP
.BR \-u [\f2rev\fP]
works like
.BR \-l ,
except that the deposited revision is not locked.
This lets one read the working file
immediately after checkin.
.RS
.PP
The
.BR \-l ,
.BR \-r ,
and
.B \-u
options are mutually exclusive and silently override each other.
For example,
.B "ci\ \-u\ \-r"
is equivalent to
.B "ci\ \-r"
because
.B \-r
overrides
.BR \-u .
.RE
.TP
.BR \-f [\f2rev\fP]
forces a deposit; the new revision is deposited even it is not different
from the preceding one.
.TP
.BR \-k [\f2rev\fP]
searches the working file for keyword values to determine its revision number,
creation date, state, and author (see
.BR co (1)),
and assigns these
values to the deposited revision, rather than computing them locally.
It also generates a default login message noting the login of the caller
and the actual checkin date.
This option is useful for software distribution. A revision that is sent to
several sites should be checked in with the
.B \-k
option at these sites to
preserve the original number, date, author, and state.
The extracted keyword values and the default log message may be overridden
with the options
.BR \-d ,
.BR \-m ,
.BR \-s ,
.BR \-w ,
and any option that carries a revision number.
.TP
.BR \-q [\f2rev\fP]
quiet mode; diagnostic output is not printed.
A revision that is not different from the preceding one is not deposited,
unless
.B \-f
is given.
.TP
.BR \-I [\f2rev\fP]
interactive mode;
the user is prompted and questioned
even if the standard input is not a terminal.
.TP
.BR \-d "[\f2date\fP]"
uses
.I date
for the checkin date and time.
The
.I date
is specified in free format as explained in
.BR co (1).
This is useful for lying about the checkin date, and for
.B \-k
if no date is available.
If
.I date
is empty, the working file's time of last modification is used.
.TP
.BR \-M [\f2rev\fP]
Set the modification time on any new working file
to be the date of the retrieved revision.
For example,
.BI "ci\ \-d\ \-M\ \-u" "\ f"
does not alter
.IR f 's
modification time, even if
.IR f 's
contents change due to keyword substitution.
Use this option with care; it can confuse
.BR make (1).
.TP
.BI \-m "msg"
uses the string
.I msg
as the log message for all revisions checked in.
.TP
.BI \-n "name"
assigns the symbolic name
.I name
to the number of the checked-in revision.
.B ci
prints an error message if
.I name
is already assigned to another
number.
.TP
.BI \-N "name"
same as
.BR \-n ,
except that it overrides a previous assignment of
.IR name .
.TP
.BI \-s "state"
sets the state of the checked-in revision to the identifier
.IR state .
The default state is
.BR Exp .
.TP
.BI \-t file
writes descriptive text from the contents of the named
.I file
into the \*r file,
deleting the existing text.
The
.I file
may not begin with
.BR \- .
.TP
.BI \-t\- string
Write descriptive text from the
.I string
into the \*r file, deleting the existing text.
.RS
.PP
The
.B \-t
option, in both its forms, has effect only during an initial checkin;
it is silently ignored otherwise.
.PP
During the initial checkin, if
.B \-t
is not given,
.B ci
obtains the text from standard input,
terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
The user is prompted for the text if interaction is possible; see
.BR \-I .
.PP
For backward compatibility with older versions of \*r, a bare
.B \-t
option is ignored.
.RE
.TP
.BI \-w "login"
uses
.I login
for the author field of the deposited revision.
Useful for lying about the author, and for
.B \-k
if no author is available.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
specifies the suffixes for \*r files.
A nonempty suffix matches any pathname ending in the suffix.
An empty suffix matches any pathname of the form
.BI RCS/ file
or
.IB path /RCS/ file.
The
.B \-x
option can specify a list of suffixes
separated by
.BR / .
For example,
.B \-x,v/
specifies two suffixes:
.B ,v
and the empty suffix.
If two or more suffixes are specified,
they are tried in order when looking for an \*r file;
the first one that works is used for that file.
If no \*r file is found but an \*r file can be created,
the suffixes are tried in order
to determine the new \*r file's name.
The default for
.IR suffixes
is installation-dependent; normally it is
.B ,v/
for hosts like Unix that permit commas in file names,
and is empty (i.e. just the empty suffix) for other hosts.
.SH "FILE NAMING"
Pairs of \*r files and working files may be specified in three ways
(see also the
example section).
.PP
1) Both the \*r file and the working file are given. The \*r pathname is of
the form
.IB path1 / workfileX
and the working pathname is of the form
.IB path2 / workfile
where
.IB path1 /
and
.IB path2 /
are (possibly different or empty) paths,
.I workfile
is a filename, and
.I X
is an \*r suffix.
If
.I X
is empty,
.IB path1 /
must be
.B RCS/
or must end in
.BR /RCS/ .
.PP
2) Only the \*r file is given. Then the working file is created in the current
directory and its name is derived from the name of the \*r file
by removing
.IB path1 /
and the suffix
.IR X .
.PP
3) Only the working file is given.
Then
.B ci
considers each \*r suffix
.I X
in turn, looking for an \*r file of the form
.IB path2 /RCS/ workfileX
or (if the former is not found and
.I X
is nonempty)
.IB path2 / workfileX.
.PP
If the \*r file is specified without a path in 1) and 2),
.B ci
looks for the \*r file first in the directory
.B ./RCS
and then in the current
directory.
.PP
.B ci
reports an error if an attempt to open an \*r file fails for an unusual reason,
even if the \*r file's pathname is just one of several possibilities.
For example, to suppress use of \*r commands in a directory
.IR d ,
create a regular file named
.IB d /RCS
so that casual attempts to use \*r commands in
.I d
fail because
.IB d /RCS
is not a directory.
.SH EXAMPLES
Suppose
.B ,v
is an \*r suffix and the current directory contains a subdirectory
.B RCS
with an \*r file
.BR io.c,v .
Then each of the following commands check in a copy of
.B io.c
into
.B RCS/io.c,v
as the latest revision, removing
.BR io.c .
.LP
.RS
.nf
.ft 3
ci io.c; ci RCS/io.c,v; ci io.c,v;
ci io.c RCS/io.c,v; ci io.c io.c,v;
ci RCS/io.c,v io.c; ci io.c,v io.c;
.ft
.fi
.RE
.PP
Suppose instead that the empty suffix
is an \*r suffix and the current directory contains a subdirectory
.B RCS
with an \*r file
.BR io.c .
The each of the following commands checks in a new revision.
.LP
.RS
.nf
.ft 3
ci io.c; ci RCS/io.c;
ci io.c RCS/io.c;
ci RCS/io.c io.c;
.ft
.fi
.RE
.SH "FILE MODES"
An \*r file created by
.B ci
inherits the read and execute permissions
from the working file. If the \*r file exists already,
.B ci
preserves its read and execute permissions.
.B ci
always turns off all write permissions of \*r files.
.SH FILES
Several temporary files may be created in the directory containing
the working file, and also in the temporary directory (see
.B \s-1TMPDIR\s0
under
.BR \s-1ENVIRONMENT\s0 ).
A semaphore file or files are created in the directory containing the \*r file.
With a nonempty suffix, the semaphore names begin with
the first character of the suffix; therefore, do not specify an suffix
whose first character could be that of a working filename.
With an empty suffix, the semaphore names end with
.B _
so working filenames should not end in
.BR _ .
.PP
.B ci
never changes an \*r or working file.
Normally,
.B ci
unlinks the file and creates a new one;
but instead of breaking a chain of one or more symbolic links to an \*r file,
it unlinks the destination file instead.
Therefore,
.B ci
breaks any hard or symbolic links to any working file it changes;
and hard links to \*r files are ineffective,
but symbolic links to \*r files are preserved.
.PP
The effective user must be able to
search and write the directory containing the \*r file.
Normally, the real user must be able to
read the \*r and working files
and to search and write the directory containing the working file;
however, some older hosts
cannot easily switch between real and effective users,
so on these hosts the effective user is used for all accesses.
The effective user is the same as the real user
unless your copies of
.B ci
and
.B co
have setuid privileges.
As described in the next section,
these privileges yield extra security if
the effective user owns all \*r files and directories,
and if only the effective user can write \*r directories.
.PP
Users can control access to \*r files by setting the permissions
of the directory containing the files; only users with write access
to the directory can use \*r commands to change its \*r files.
For example, in hosts that allow a user to belong to several groups,
one can make a group's \*r directories writable to that group only.
This approach suffices for informal projects,
but it means that any group member can arbitrarily change the group's \*r files,
and can even remove them entirely.
Hence more formal projects sometimes distinguish between an \*r administrator,
who can change the \*r files at will, and other project members,
who can check in new revisions but cannot otherwise change the \*r files.
.SH "SETUID USE"
To prevent anybody but their \*r administrator from deleting revisions,
a set of users can employ setuid privileges as follows.
.nr n \w'\(bu '+1n-1/1n
.IP \(bu \nn
Check that the host supports \*r setuid use.
Consult a trustworthy expert if there are any doubts.
It is best if the
.B seteuid()
system call works as described in Posix 1003.1a Draft 5,
because \*r can switch back and forth easily
between real and effective users, even if the real user is
.BR root .
If not, the second best is if the
.B setuid()
system call supports saved setuid
(the {\s-1_POSIX_SAVED_IDS\s0} behavior of Posix 1003.1-1990);
this fails only if the real user is
.BR root .
If \*r detects any failure in setuid, it quits immediately.
.IP \(bu \nn
Choose a user
.I A
to serve as \*r administrator for the set of users.
Only
.I A
will be able to invoke the
.B rcs
command on the users' \*r files.
.I A
should not be
.B root
or any other user with special powers.
Mutually suspicious sets of users should use different administrators.
.IP \(bu \nn
Choose a path name
.I B
that will be a directory of files to be executed by the users.
.IP \(bu \nn
Have
.I A
set up
.I B
to contain copies of
.B ci
and
.B co
that are setuid to
.I A
by copying the commands from their standard installation directory
.I D
as follows:
.LP
.RS
.nf
.ne 3
\f3mkdir\fP \f2B\fP
\f3cp\fP \f2D\fP\^\f3/c[io]\fP \f2B\fP
\f3chmod go\-w,u+s\fP \f2B\fP\f3/c[io]\fP
.fi
.RE
.IP \(bu \nn
Have each user prepend
.I B
to their path as follows:
.LP
.RS
.nf
.ne 2
\f3PATH=\fP\f2B\fP\f3:$PATH; export PATH\fP # ordinary shell
\f3set path=(\fP\f2B\fP \f3$path)\fP # C shell
.fi
.RE
.IP \(bu \nn
Have
.I A
create each \*r directory
.I R
with write access only to
.I A
as follows:
.LP
.RS
.nf
.ne 2
\f3mkdir\fP \f2R\fP
\f3chmod go\-w\fP \f2R\fP
.fi
.RE
.IP \(bu \nn
If you want to let only certain users read the \*r files,
put the users into a group
.IR G ,
and have
.I A
further protect the \*r directory as follows:
.LP
.RS
.nf
.ne 2
\f3chgrp\fP \f2G R\fP
\f3chmod g\-w,o\-rwx\fP \f2R\fP
.fi
.RE
.IP \(bu \nn
Have
.I A
copy old \*r files (if any) into
.IR R ,
to ensure that
.I A
owns them.
.IP \(bu \nn
An \*r file's access list limits who can check in and lock revisions.
The default access list is empty,
which grants checkin access to anyone who can read the \*r file.
If you want limit checkin access,
have
.I A
invoke
.B "rcs\ \-a"
on the file; see
.BR rcs (1).
In particular,
.BI "rcs\ \-e\ \-a" A
limits access to just
.IR A .
.IP \(bu \nn
Have
.I A
initialize any new \*r files with
.B "rcs\ \-i"
before initial checkin, adding the
.B \-a
option if you want to limit checkin access.
.IP \(bu \nn
Give setuid privileges only to
.BR ci ,
.BR co ,
and
.BR rcsclean ;
do not give them to
.B rcs
or to any other command.
.IP \(bu \nn
Do not use other setuid commands to invoke \*r commands;
setuid is trickier than you think!
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
A backslash escapes spaces within an option.
The
.B \s-1RCSINIT\s0
options are prepended to the argument lists of most \*r commands.
Useful
.B \s-1RCSINIT\s0
options include
.BR \-q ,
.BR \-V ,
and
.BR \-x .
.TP
.B \s-1TMPDIR\s0
Name of the temporary directory.
If not set, the environment variables
.B \s-1TMP\s0
and
.B \s-1TEMP\s0
are inspected instead and the first value found is taken;
if none of them are set,
a host-dependent default is used, typically
.BR /tmp .
.SH DIAGNOSTICS
For each revision,
.B ci
prints the \*r file, the working file, and the number
of both the deposited and the preceding revision.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
co(1), ident(1), make(1), rcs(1), rcsclean(1), rcsdiff(1),
rcsintro(1), rcsmerge(1), rlog(1), rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

1165
gnu/usr.bin/rcs/ci/ci.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
PROG= co
SRCS= co.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

569
gnu/usr.bin/rcs/co/co.1 Normal file
View file

@ -0,0 +1,569 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: co.1,v 5.7 1991/08/19 03:13:55 eggert Exp $
.ds g \&\s-1UTC\s0
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH CO 1 \*(Dt GNU
.SH NAME
co \- check out RCS revisions
.SH SYNOPSIS
.B co
.RI [ options ] " file " .\|.\|.
.SH DESCRIPTION
.B co
retrieves a revision from each \*r file and stores it into
the corresponding working file.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
Revisions of an \*r file may be checked out locked or unlocked. Locking a
revision prevents overlapping updates. A revision checked out for reading or
processing (e.g., compiling) need not be locked. A revision checked out
for editing and later checkin must normally be locked. Checkout with locking
fails if the revision to be checked out is currently locked by another user.
(A lock may be broken with
.BR rcs "(1).)\ \&"
Checkout with locking also requires the caller to be on the access list of
the \*r file, unless he is the owner of the
file or the superuser, or the access list is empty.
Checkout without locking is not subject to accesslist restrictions, and is
not affected by the presence of locks.
.PP
A revision is selected by options for revision or branch number,
checkin date/time, author, or state.
When the selection options
are applied in combination,
.B co
retrieves the latest revision
that satisfies all of them.
If none of the selection options
is specified,
.B co
retrieves the latest revision
on the default branch (normally the trunk, see the
.B \-b
option of
.BR rcs (1)).
A revision or branch number may be attached
to any of the options
.BR \-f ,
.BR \-I ,
.BR \-l ,
.BR \-M ,
.BR \-p ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
The options
.B \-d
(date),
.B \-s
(state), and
.B \-w
(author)
retrieve from a single branch, the
.I selected
branch,
which is either specified by one of
.BR \-f,
\&.\|.\|.,
.BR \-u ,
or the default branch.
.PP
A
.B co
command applied to an \*r
file with no revisions creates a zero-length working file.
.B co
always performs keyword substitution (see below).
.SH OPTIONS
.TP
.BR \-r [\f2rev\fP]
retrieves the latest revision whose number is less than or equal to
.I rev.
If
.I rev
indicates a branch rather than a revision,
the latest revision on that branch is retrieved.
If
.I rev
is omitted, the latest revision on the default branch
(see the
.B \-b
option of
.BR rcs (1))
is retrieved.
If
.I rev
is
.BR $ ,
.B co
determines the revision number from keyword values in the working file.
Otherwise, a revision is composed of one or more numeric or symbolic fields
separated by periods. The numeric equivalent of a symbolic field
is specified with the
.B \-n
option of the commands
.BR ci (1)
and
.BR rcs (1).
.TP
.BR \-l [\f2rev\fP]
same as
.BR \-r ,
except that it also locks the retrieved revision for
the caller.
.TP
.BR \-u [\f2rev\fP]
same as
.BR \-r ,
except that it unlocks the retrieved revision if it was
locked by the caller. If
.I rev
is omitted,
.B \-u
retrieves the revision locked by the caller, if there is one; otherwise,
it retrieves the latest revision on the default branch.
.TP
.BR \-f [\f2rev\fP]
forces the overwriting of the working file;
useful in connection with
.BR \-q .
See also
.SM "FILE MODES"
below.
.TP
.B \-kkv
Generate keyword strings using the default form, e.g.\&
.B "$\&Revision: \*(Rv $"
for the
.B Revision
keyword.
A locker's name is inserted in the value of the
.BR Header ,
.BR Id ,
and
.B Locker
keyword strings
only as a file is being locked,
i.e. by
.B "ci\ \-l"
and
.BR "co\ \-l".
This is the default.
.TP
.B \-kkvl
Like
.BR \-kkv ,
except that a locker's name is always inserted
if the given revision is currently locked.
.TP
.BR \-kk
Generate only keyword names in keyword strings; omit their values.
See
.SM "KEYWORD SUBSTITUTION"
below.
For example, for the
.B Revision
keyword, generate the string
.B $\&Revision$
instead of
.BR "$\&Revision: \*(Rv $".
This option is useful to ignore differences due to keyword substitution
when comparing different revisions of a file.
.TP
.BR \-ko
Generate the old keyword string,
present in the working file just before it was checked in.
For example, for the
.B Revision
keyword, generate the string
.B "$\&Revision: 1.1 $"
instead of
.B "$\&Revision: \*(Rv $"
if that is how the string appeared when the file was checked in.
This can be useful for binary file formats
that cannot tolerate any changes to substrings
that happen to take the form of keyword strings.
.TP
.BR \-kv
Generate only keyword values for keyword strings.
For example, for the
.B Revision
keyword, generate the string
.B \*(Rv
instead of
.BR "$\&Revision: \*(Rv $".
This can help generate files in programming languages where it is hard to
strip keyword delimiters like
.B "$\&Revision:\ $"
from a string.
However, further keyword substitution cannot be performed once the
keyword names are removed, so this option should be used with care.
Because of this danger of losing keywords,
this option cannot be combined with
.BR \-l ,
and the owner write permission of the working file is turned off;
to edit the file later, check it out again without
.BR \-kv .
.TP
.BR \-p [\f2rev\fP]
prints the retrieved revision on the standard output rather than storing it
in the working file.
This option is useful when
.B co
is part of a pipe.
.TP
.BR \-q [\f2rev\fP]
quiet mode; diagnostics are not printed.
.TP
.BR \-I [\f2rev\fP]
interactive mode;
the user is prompted and questioned
even if the standard input is not a terminal.
.TP
.BI \-d date
retrieves the latest revision on the selected branch whose checkin date/time is
less than or equal to
.I date.
The date and time may be given in free format.
The time zone
.B LT
stands for local time;
other common time zone names are understood.
For example, the following
.IR date s
are equivalent
if local time is January 11, 1990, 8pm Pacific Standard Time,
eight hours west of Coordinated Universal Time (\*g):
.RS
.LP
.RS
.nf
.ta \w'\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP 'u
.ne 9
\f38:00 pm lt\fP
\f34:00 AM, Jan. 12, 1990\fP note: default is \*g
\f31990/01/12 04:00:00\fP \*r date format
\f3Thu Jan 11 20:00:00 1990 LT\fP output of \f3ctime\fP(3) + \f3LT\fP
\f3Thu Jan 11 20:00:00 PST 1990\fP output of \f3date\fP(1)
\f3Fri Jan 12 04:00:00 GMT 1990\fP
\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP
\f3Fri-JST, 1990, 1pm Jan 12\fP
\f312-January-1990, 04:00-WET\fP
.ta 4n +4n +4n +4n
.fi
.RE
.LP
Most fields in the date and time may be defaulted.
The default time zone is \*g.
The other defaults are determined in the order year, month, day,
hour, minute, and second (most to least significant). At least one of these
fields must be provided. For omitted fields that are of higher significance
than the highest provided field, the time zone's current values are assumed.
For all other omitted fields,
the lowest possible values are assumed.
For example, the date
.B "20, 10:30"
defaults to
10:30:00 \*g of the 20th of the \*g time zone's current month and year.
The date/time must be quoted if it contains spaces.
.RE
.TP
.BR \-M [\f2rev\fP]
Set the modification time on the new working file
to be the date of the retrieved revision.
Use this option with care; it can confuse
.BR make (1).
.TP
.BI \-s state
retrieves the latest revision on the selected branch whose state is set to
.I state.
.TP
.BR \-w [\f2login\fP]
retrieves the latest revision on the selected branch which was checked in
by the user with login name
.I login.
If the argument
.I login
is
omitted, the caller's login is assumed.
.TP
.BI \-j joinlist
generates a new revision which is the join of the revisions on
.I joinlist.
This option is largely obsoleted by
.BR rcsmerge (1)
but is retained for backwards compatibility.
.RS
.PP
The
.I joinlist
is a comma-separated list of pairs of the form
.IB rev2 : rev3,
where
.I rev2
and
.I rev3
are (symbolic or numeric)
revision numbers.
For the initial such pair,
.I rev1
denotes the revision selected
by the above options
.BR \-f,
\&.\|.\|.,
.BR \-w .
For all other pairs,
.I rev1
denotes the revision generated by the previous pair.
(Thus, the output
of one join becomes the input to the next.)
.PP
For each pair,
.B co
joins revisions
.I rev1
and
.I rev3
with respect to
.I rev2.
This means that all changes that transform
.I rev2
into
.I rev1
are applied to a copy of
.I rev3.
This is particularly useful if
.I rev1
and
.I rev3
are the ends of two branches that have
.I rev2
as a common ancestor. If
.IR rev1 < rev2 < rev3
on the same branch,
joining generates a new revision which is like
.I rev3,
but with all changes that lead from
.I rev1
to
.I rev2
undone.
If changes from
.I rev2
to
.I rev1
overlap with changes from
.I rev2
to
.I rev3,
.B co
reports overlaps as described in
.BR merge (1).
.PP
For the initial pair,
.I rev2
may be omitted. The default is the common
ancestor.
If any of the arguments indicate branches, the latest revisions
on those branches are assumed.
The options
.B \-l
and
.B \-u
lock or unlock
.I rev1.
.RE
.TP
.BI \-V n
Emulate \*r version
.I n,
where
.I n
may be
.BR 3 ,
.BR 4 ,
or
.BR 5 .
This may be useful when interchanging \*r files with others who are
running older versions of \*r.
To see which version of \*r your correspondents are running, have them invoke
.B rlog
on an \*r file;
if none of the first few lines of output contain the string
.B branch:
it is version 3;
if the dates' years have just two digits, it is version 4;
otherwise, it is version 5.
An \*r file generated while emulating version 3 will lose its default branch.
An \*r revision generated while emulating version 4 or earlier will have
a timestamp that is off by up to 13 hours.
A revision extracted while emulating version 4 or earlier will contain
dates of the form
.IB yy / mm / dd
instead of
.IB yyyy / mm / dd
and may also contain different white space in the substitution for
.BR $\&Log$ .
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.SH "KEYWORD SUBSTITUTION"
Strings of the form
.BI $ keyword $
and
.BI $ keyword : .\|.\|. $
embedded in
the text are replaced
with strings of the form
.BI $ keyword : value $
where
.I keyword
and
.I value
are pairs listed below.
Keywords may be embedded in literal strings
or comments to identify a revision.
.PP
Initially, the user enters strings of the form
.BI $ keyword $ .
On checkout,
.B co
replaces these strings with strings of the form
.BI $ keyword : value $ .
If a revision containing strings of the latter form
is checked back in, the value fields will be replaced during the next
checkout.
Thus, the keyword values are automatically updated on checkout.
This automatic substitution can be modified by the
.B \-k
options.
.PP
Keywords and their corresponding values:
.TP
.B $\&Author$
The login name of the user who checked in the revision.
.TP
.B $\&Date$
The date and time (\*g) the revision was checked in.
.TP
.B $\&Header$
A standard header containing the full pathname of the \*r file, the
revision number, the date (\*g), the author, the state,
and the locker (if locked).
.TP
.B $\&Id$
Same as
.BR $\&Header$ ,
except that the \*r filename is without a path.
.TP
.B $\&Locker$
The login name of the user who locked the revision (empty if not locked).
.TP
.B $\&Log$
The log message supplied during checkin, preceded by a header
containing the \*r filename, the revision number, the author, and the date
(\*g).
Existing log messages are
.I not
replaced.
Instead, the new log message is inserted after
.BR $\&Log: .\|.\|. $ .
This is useful for
accumulating a complete change log in a source file.
.TP
.B $\&RCSfile$
The name of the \*r file without a path.
.TP
.B $\&Revision$
The revision number assigned to the revision.
.TP
.B $\&Source$
The full pathname of the \*r file.
.TP
.B $\&State$
The state assigned to the revision with the
.B \-s
option of
.BR rcs (1)
or
.BR ci (1).
.SH "FILE MODES"
The working file inherits the read and execute permissions from the \*r
file. In addition, the owner write permission is turned on, unless
.B \-kv
is set or the file
is checked out unlocked and locking is set to strict (see
.BR rcs (1)).
.PP
If a file with the name of the working file exists already and has write
permission,
.B co
aborts the checkout,
asking beforehand if possible.
If the existing working file is
not writable or
.B \-f
is given, the working file is deleted without asking.
.SH FILES
.B co
accesses files much as
.BR ci (1)
does, except that it does not need to read the working file.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The \*r pathname, the working pathname,
and the revision number retrieved are
written to the diagnostic output.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), ctime(3), date(1), ident(1), make(1),
rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH LIMITS
Links to the \*r and working files are not preserved.
.PP
There is no way to selectively suppress the expansion of keywords, except
by writing them differently. In nroff and troff, this is done by embedding the
null-character
.B \e&
into the keyword.
.SH BUGS
The
.B \-d
option sometimes gets confused, and accepts no date before 1970.
.br

769
gnu/usr.bin/rcs/co/co.c Normal file
View file

@ -0,0 +1,769 @@
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* RCS checkout operation
*/
/*****************************************************************************
* check out revisions from RCS files
*****************************************************************************
*/
/* $Log: co.c,v $
* Revision 5.9 1991/10/07 17:32:46 eggert
* ci -u src/RCS/co.c,v src/co.c <<\.
* -k affects just working file, not RCS file.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Warn before removing somebody else's file.
* Add -M. Fix co -j bugs. Tune.
*
* Revision 5.7 1991/04/21 11:58:15 eggert
* Ensure that working file is newer than RCS file after co -[lu].
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.6 1990/12/04 05:18:38 eggert
* Don't checkaccesslist() unless necessary.
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.5 1990/11/01 05:03:26 eggert
* Fix -j. Add -I.
*
* Revision 5.4 1990/10/04 06:30:11 eggert
* Accumulate exit status across files.
*
* Revision 5.3 1990/09/11 02:41:09 eggert
* co -kv yields a readonly working file.
*
* Revision 5.2 1990/09/04 08:02:13 eggert
* Standardize yes-or-no procedure.
*
* Revision 5.0 1990/08/22 08:10:02 eggert
* Permit multiple locks by same user. Add setuid support.
* Remove compile-time limits; use malloc instead.
* Permit dates past 1999/12/31. Switch to GMT.
* Make lock and temp files faster and safer.
* Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
*
* Revision 4.7 89/05/01 15:11:41 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.6 88/08/09 19:12:15 eggert
* Fix "co -d" core dump; rawdate wasn't always initialized.
* Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
*
* Revision 4.5 87/12/18 11:35:40 narten
* lint cleanups (from Guy Harris)
*
* Revision 4.4 87/10/18 10:20:53 narten
* Updating version numbers changes relative to 1.1, are actually
* relative to 4.2
*
* Revision 1.3 87/09/24 13:58:30 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:21:38 jenkins
* Port to suns
*
* Revision 4.2 83/12/05 13:39:48 wft
* made rewriteflag external.
*
* Revision 4.1 83/05/10 16:52:55 wft
* Added option -u and -f.
* Added handling of default branch.
* Replaced getpwuid() with getcaller().
* Removed calls to stat(); now done by pairfilenames().
* Changed and renamed rmoldfile() to rmworkfile().
* Replaced catchints() calls with restoreints(), unlink()--link() with rename();
*
* Revision 3.7 83/02/15 15:27:07 wft
* Added call to fastcopy() to copy remainder of RCS file.
*
* Revision 3.6 83/01/15 14:37:50 wft
* Added ignoring of interrupts while RCS file is renamed; this avoids
* deletion of RCS files during the unlink/link window.
*
* Revision 3.5 82/12/08 21:40:11 wft
* changed processing of -d to use DATEFORM; removed actual from
* call to preparejoin; re-fixed printing of done at the end.
*
* Revision 3.4 82/12/04 18:40:00 wft
* Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
* Fixed printing of "done".
*
* Revision 3.3 82/11/28 22:23:11 wft
* Replaced getlogin() with getpwuid(), flcose() with ffclose(),
* %02d with %.2d, mode generation for working file with WORKMODE.
* Fixed nil printing. Fixed -j combined with -l and -p, and exit
* for non-existing revisions in preparejoin().
*
* Revision 3.2 82/10/18 20:47:21 wft
* Mode of working file is now maintained even for co -l, but write permission
* is removed.
* The working file inherits its mode from the RCS file, plus write permission
* for the owner. The write permission is not given if locking is strict and
* co does not lock.
* An existing working file without write permission is deleted automatically.
* Otherwise, co asks (empty answer: abort co).
* Call to getfullRCSname() added, check for write error added, call
* for getlogin() fixed.
*
* Revision 3.1 82/10/13 16:01:30 wft
* fixed type of variables receiving from getc() (char -> int).
* removed unused variables.
*/
#include "rcsbase.h"
static char const *getancestor P((char const*,char const*));
static int buildjoin P((char const*));
static int preparejoin P((void));
static int rmlock P((struct hshentry const*));
static int rmworkfile P((void));
static void cleanup P((void));
static char const quietarg[] = "-q";
static char const *expandarg, *join, *suffixarg, *versionarg;
static char const *joinlist[joinlength]; /* revisions to be joined */
static FILE *neworkptr;
static int exitstatus;
static int forceflag;
static int lastjoin; /* index of last element in joinlist */
static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
static int mtimeflag;
static struct hshentries *gendeltas; /* deltas to be generated */
static struct hshentry *targetdelta; /* final delta to be generated */
static struct stat workstat;
mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $")
{
static char const cmdusage[] =
"\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ...";
char *a, **newargv;
char const *author, *date, *rev, *state;
char const *joinfilename, *newdate, *neworkfilename;
int changelock; /* 1 if a lock has been changed, -1 if error */
int expmode, r, tostdout, workstatstat;
struct buf numericrev; /* expanded revision number */
char finaldate[datesize];
setrid();
author = date = rev = state = nil;
bufautobegin(&numericrev);
expmode = -1;
suffixes = X_DEFAULT;
tostdout = false;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
switch (*a++) {
case 'r':
revno:
if (*a) {
if (rev) warn("redefinition of revision number");
rev = a;
}
break;
case 'f':
forceflag=true;
goto revno;
case 'l':
if (lockflag < 0) {
warn("-l overrides -u.");
}
lockflag = 1;
goto revno;
case 'u':
if (0 < lockflag) {
warn("-l overrides -u.");
}
lockflag = -1;
goto revno;
case 'p':
tostdout = true;
goto revno;
case 'I':
interactiveflag = true;
goto revno;
case 'q':
quietflag=true;
goto revno;
case 'd':
if (date)
redefined('d');
str2date(a, finaldate);
date=finaldate;
break;
case 'j':
if (*a) {
if (join) redefined('j');
join = a;
}
break;
case 'M':
mtimeflag = true;
goto revno;
case 's':
if (*a) {
if (state) redefined('s');
state = a;
}
break;
case 'w':
if (author) redefined('w');
if (*a)
author = a;
else
author = getcaller();
break;
case 'x':
suffixarg = *argv;
suffixes = a;
break;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
break;
case 'k': /* set keyword expand mode */
expandarg = *argv;
if (0 <= expmode) redefined('k');
if (0 <= (expmode = str2expmode(a)))
break;
/* fall into */
default:
faterror("unknown option: %s%s", *argv, cmdusage);
};
} /* end of option processing */
if (argc<1) faterror("no input file%s", cmdusage);
if (tostdout)
# if text_equals_binary_stdio || text_work_stdio
workstdout = stdout;
# else
if (!(workstdout = fdopen(STDOUT_FILENO, FOPEN_W_WORK)))
efaterror("stdout");
# endif
/* now handle all filenames */
do {
ffree();
if (pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0)
continue;
/* now RCSfilename contains the name of the RCS file, and finptr
* points at it. workfilename contains the name of the working file.
* Also, RCSstat has been set.
*/
diagnose("%s --> %s\n", RCSfilename,tostdout?"stdout":workfilename);
workstatstat = -1;
if (tostdout) {
neworkfilename = 0;
neworkptr = workstdout;
} else {
workstatstat = stat(workfilename, &workstat);
neworkfilename = makedirtemp(workfilename, 1);
if (!(neworkptr = fopen(neworkfilename, FOPEN_W_WORK))) {
if (errno == EACCES)
error("%s: parent directory isn't writable",
workfilename
);
else
eerror(neworkfilename);
continue;
}
}
gettree(); /* reads in the delta tree */
if (Head==nil) {
/* no revisions; create empty file */
diagnose("no revisions present; generating empty revision 0.0\n");
Ozclose(&fcopy);
if (workstatstat == 0)
if (!rmworkfile()) continue;
changelock = 0;
newdate = 0;
/* Can't reserve a delta, so don't call addlock */
} else {
if (rev!=nil) {
/* expand symbolic revision number */
if (!expandsym(rev, &numericrev))
continue;
} else
switch (lockflag<0 ? findlock(false,&targetdelta) : 0) {
default:
continue;
case 0:
bufscpy(&numericrev, Dbranch?Dbranch:"");
break;
case 1:
bufscpy(&numericrev, targetdelta->num);
break;
}
/* get numbers of deltas to be generated */
if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
continue;
/* check reservations */
changelock =
lockflag < 0 ?
rmlock(targetdelta)
: lockflag == 0 ?
0
:
addlock(targetdelta);
if (
changelock < 0 ||
changelock && !checkaccesslist() ||
!dorewrite(lockflag, changelock)
)
continue;
if (0 <= expmode)
Expand = expmode;
if (0 < lockflag && Expand == VAL_EXPAND) {
error("cannot combine -kv and -l");
continue;
}
if (join && !preparejoin()) continue;
diagnose("revision %s%s\n",targetdelta->num,
0<lockflag ? " (locked)" :
lockflag<0 ? " (unlocked)" : "");
/* Prepare to remove old working file if necessary. */
if (workstatstat == 0)
if (!rmworkfile()) continue;
/* skip description */
getdesc(false); /* don't echo*/
locker_expansion = 0 < lockflag;
joinfilename = buildrevision(
gendeltas, targetdelta,
join&&tostdout ? (FILE*)0 : neworkptr,
Expand!=OLD_EXPAND
);
# if !large_memory
if (fcopy == neworkptr)
fcopy = 0; /* Don't close it twice. */
# endif
if_advise_access(changelock && gendeltas->first!=targetdelta,
finptr, MADV_SEQUENTIAL
);
if (!donerewrite(changelock))
continue;
newdate = targetdelta->date;
if (join) {
newdate = 0;
if (!joinfilename) {
aflush(neworkptr);
joinfilename = neworkfilename;
}
if (!buildjoin(joinfilename))
continue;
}
}
if (!tostdout) {
r = 0;
if (mtimeflag && newdate) {
if (!join)
aflush(neworkptr);
r = setfiledate(neworkfilename, newdate);
}
if (r == 0) {
ignoreints();
r = chnamemod(&neworkptr, neworkfilename, workfilename,
WORKMODE(RCSstat.st_mode,
!(Expand==VAL_EXPAND || lockflag<=0&&StrictLocks)
)
);
keepdirtemp(neworkfilename);
restoreints();
}
if (r != 0) {
eerror(workfilename);
error("see %s", neworkfilename);
continue;
}
diagnose("done\n");
}
} while (cleanup(),
++argv, --argc >=1);
tempunlink();
Ofclose(workstdout);
exitmain(exitstatus);
} /* end of main (co) */
static void
cleanup()
{
if (nerror) exitstatus = EXIT_FAILURE;
Izclose(&finptr);
Ozclose(&frewrite);
# if !large_memory
if (fcopy!=workstdout) Ozclose(&fcopy);
# endif
if (neworkptr!=workstdout) Ozclose(&neworkptr);
dirtempunlink();
}
#if lint
# define exiterr coExit
#endif
exiting void
exiterr()
{
dirtempunlink();
tempunlink();
_exit(EXIT_FAILURE);
}
/*****************************************************************
* The following routines are auxiliary routines
*****************************************************************/
static int
rmworkfile()
/* Function: prepares to remove workfilename, if it exists, and if
* it is read-only.
* Otherwise (file writable):
* if !quietmode asks the user whether to really delete it (default: fail);
* otherwise failure.
* Returns true if permission is gotten.
*/
{
if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
/* File is writable */
if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ",
workfilename,
myself(workstat.st_uid) ? "" : ", and you do not own it"
)) {
error(!quietflag && ttystdin()
? "checkout aborted"
: "writable %s exists; checkout aborted", workfilename);
return false;
}
}
/* Actual unlink is done later by caller. */
return true;
}
static int
rmlock(delta)
struct hshentry const *delta;
/* Function: removes the lock held by caller on delta.
* Returns -1 if someone else holds the lock,
* 0 if there is no lock on delta,
* and 1 if a lock was found and removed.
*/
{ register struct lock * next, * trail;
char const *num;
struct lock dummy;
int whomatch, nummatch;
num=delta->num;
dummy.nextlock=next=Locks;
trail = &dummy;
while (next!=nil) {
whomatch = strcmp(getcaller(), next->login);
nummatch=strcmp(num,next->delta->num);
if ((whomatch==0) && (nummatch==0)) break;
/*found a lock on delta by caller*/
if ((whomatch!=0)&&(nummatch==0)) {
error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
return -1;
}
trail=next;
next=next->nextlock;
}
if (next!=nil) {
/*found one; delete it */
trail->nextlock=next->nextlock;
Locks=dummy.nextlock;
next->delta->lockedby=nil; /* reset locked-by */
return 1; /*success*/
} else return 0; /*no lock on delta*/
}
/*****************************************************************
* The rest of the routines are for handling joins
*****************************************************************/
static char const *
addjoin(joinrev)
char *joinrev;
/* Add joinrev's number to joinlist, yielding address of char past joinrev,
* or nil if no such revision exists.
*/
{
register char *j;
register struct hshentry const *d;
char terminator;
struct buf numrev;
struct hshentries *joindeltas;
j = joinrev;
for (;;) {
switch (*j++) {
default:
continue;
case 0:
case ' ': case '\t': case '\n':
case ':': case ',': case ';':
break;
}
break;
}
terminator = *--j;
*j = 0;
bufautobegin(&numrev);
d = 0;
if (expandsym(joinrev, &numrev))
d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas);
bufautoend(&numrev);
*j = terminator;
if (d) {
joinlist[++lastjoin] = d->num;
return j;
}
return nil;
}
static int
preparejoin()
/* Function: Parses a join list pointed to by join and places pointers to the
* revision numbers into joinlist.
*/
{
register char const *j;
j=join;
lastjoin= -1;
for (;;) {
while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
if (*j=='\0') break;
if (lastjoin>=joinlength-2) {
error("too many joins");
return(false);
}
if (!(j = addjoin(j))) return false;
while ((*j==' ') || (*j=='\t')) j++;
if (*j == ':') {
j++;
while((*j==' ') || (*j=='\t')) j++;
if (*j!='\0') {
if (!(j = addjoin(j))) return false;
} else {
error("join pair incomplete");
return false;
}
} else {
if (lastjoin==0) { /* first pair */
/* common ancestor missing */
joinlist[1]=joinlist[0];
lastjoin=1;
/*derive common ancestor*/
if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
return false;
} else {
error("join pair incomplete");
return false;
}
}
}
if (lastjoin<1) {
error("empty join");
return false;
} else return true;
}
static char const *
getancestor(r1, r2)
char const *r1, *r2;
/* Yield the common ancestor of r1 and r2 if successful, nil otherwise.
* Work reliably only if r1 and r2 are not branch numbers.
*/
{
static struct buf t1, t2;
unsigned l1, l2, l3;
char const *r;
l1 = countnumflds(r1);
l2 = countnumflds(r2);
if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) {
/* not on main trunk or identical */
l3 = 0;
while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
l3 += 2;
/* This will terminate since r1 and r2 are not the same; see above. */
if (l3==0) {
/* no common prefix; common ancestor on main trunk */
VOID partialno(&t1, r1, l1>2 ? (unsigned)2 : l1);
VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2);
r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
return r;
} else if (cmpnumfld(r1, r2, l3+1)!=0)
return partialno(&t1,r1,l3);
}
error("common ancestor of %s and %s undefined", r1, r2);
return nil;
}
static int
buildjoin(initialfile)
char const *initialfile;
/* Function: merge pairs of elements in joinlist into initialfile
* If workstdout is set, copy result to stdout.
* All unlinking of initialfile, rev2, and rev3 should be done by tempunlink().
*/
{
struct buf commarg;
struct buf subs;
char const *rev2, *rev3;
int i;
char const *cov[10], *mergev[12];
char const **p;
bufautobegin(&commarg);
bufautobegin(&subs);
rev2 = maketemp(0);
rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
cov[0] = nil;
/* cov[1] setup below */
cov[2] = CO;
/* cov[3] setup below */
p = &cov[4];
if (expandarg) *p++ = expandarg;
if (suffixarg) *p++ = suffixarg;
if (versionarg) *p++ = versionarg;
*p++ = quietarg;
*p++ = RCSfilename;
*p = nil;
mergev[0] = nil;
mergev[1] = nil;
mergev[2] = MERGE;
mergev[3] = mergev[5] = "-L";
/* rest of mergev setup below */
i=0;
while (i<lastjoin) {
/*prepare marker for merge*/
if (i==0)
bufscpy(&subs, targetdelta->num);
else {
bufscat(&subs, ",");
bufscat(&subs, joinlist[i-2]);
bufscat(&subs, ":");
bufscat(&subs, joinlist[i-1]);
}
diagnose("revision %s\n",joinlist[i]);
bufscpy(&commarg, "-p");
bufscat(&commarg, joinlist[i]);
cov[1] = rev2;
cov[3] = commarg.string;
if (runv(cov))
goto badmerge;
diagnose("revision %s\n",joinlist[i+1]);
bufscpy(&commarg, "-p");
bufscat(&commarg, joinlist[i+1]);
cov[1] = rev3;
cov[3] = commarg.string;
if (runv(cov))
goto badmerge;
diagnose("merging...\n");
mergev[4] = subs.string;
mergev[6] = joinlist[i+1];
p = &mergev[7];
if (quietflag) *p++ = quietarg;
if (lastjoin<=i+2 && workstdout) *p++ = "-p";
*p++ = initialfile;
*p++ = rev2;
*p++ = rev3;
*p = nil;
switch (runv(mergev)) {
case DIFF_FAILURE: case DIFF_SUCCESS:
break;
default:
goto badmerge;
}
i=i+2;
}
bufautoend(&commarg);
bufautoend(&subs);
return true;
badmerge:
nerror++;
bufautoend(&commarg);
bufautoend(&subs);
return false;
}

1524
gnu/usr.bin/rcs/doc/rcs.ms Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
.SH
Functions of RCS (Revision Control System)
.PP
RCS manages software libraries. It greatly increases programmer productivity
by providing the following functions.
.IP 1.
RCS stores and retrieves multiple revisions of program and other text.
Thus, one can maintain one or more releases while developing the next
release, with a minimum of space overhead. Changes no longer destroy the
original -- previous revisions remain accessible.
.RS
.IP a.
Maintains each module as a tree of revisions.
.IP b.
Project libraries can
be organized centrally, decentralized, or any way you like.
.IP c.
RCS works for any type of text: programs, documentation, memos, papers,
graphics, VLSI layouts, form letters, etc.
.RE
.IP 2.
RCS maintains a complete history of changes.
Thus, one can find out what happened to a module easily
and quickly, without having to compare source listings or
having to track down colleagues.
.RS
.IP a.
RCS performs automatic record keeping.
.IP b.
RCS logs all changes automatically.
.IP c.
RCS guarantees project continuity.
.RE
.IP 3.
RCS manages multiple lines of development.
.IP 4.
RCS can merge multiple lines of development.
Thus, when several parallel lines of development must be consolidated
into one line, the merging of changes is automatic.
.IP 5.
RCS flags coding conflicts.
If two or more lines of development modify the same section of code,
RCS can alert programmers about overlapping changes.
.IP 6.
RCS resolves access conflicts.
When two or more programmers wish to modify the same revision,
RCS alerts the programmers and makes sure that one modification won't wipe
out the other one.
.IP 7.
RCS provides high-level retrieval functions.
Revisions can be retrieved according to ranges of revision numbers,
symbolic names, dates, authors, and states.
.IP 8.
RCS provides release and configuration control.
Revisions can be marked as released, stable, experimental, etc.
Configurations of modules can be described simply and directly.
.IP 9.
RCS performs automatic identification of modules with name, revision
number, creation time, author, etc.
Thus, it is always possible to determine which revisions of which
modules make up a given configuration.
.IP 10.
Provides high-level management visibility.
Thus, it is easy to track the status of a software project.
.RS
.IP a.
RCS provides a complete change history.
.IP b.
RCS records who did what when to which revision of which module.
.RE
.IP 11.
RCS is fully compatible with existing software development tools.
RCS is unobtrusive -- its interface to the file system is such that
all your existing software tools can be used as before.
.IP 12.
RCS' basic user interface is extremely simple. The novice need to learn
only two commands. Its more sophisticated features have been
tuned towards advanced software development environments and the
experienced software professional.
.IP 13.
RCS simplifies software distribution if customers
maintain sources with RCS also. This technique assures proper
identification of versions and configurations, and tracking of customer
modifications. Customer modifications can be merged into distributed
versions locally or by the development group.
.IP 14.
RCS needs little extra space for the revisions (only the differences).
If intermediate revisions are deleted, the corresponding
differences are compressed into the shortest possible form.
.IP 15.
RCS is implemented with reverse deltas. This means that
the latest revision, which is the one that is accessed most often,
is stored intact. All others are regenerated from the latest one
by applying reverse deltas (backward differences). This
results in fast access time for the revision needed most often.

View file

@ -0,0 +1,7 @@
PROG= ident
SRCS= ident.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

View file

@ -0,0 +1,76 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
.ds iD \\$3 \\$4 \\$5 \\$6 \\$7
..
.Id $Id: ident.1,v 5.0 1990/08/22 09:09:36 eggert Exp $
.ds r \s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH IDENT 1 \*(Dt GNU
.SH NAME
ident \- identify files
.SH SYNOPSIS
.B ident
[
.B \-q
] [
.I file
\&.\|.\|. ]
.SH DESCRIPTION
.B ident
searches for all occurrences of the pattern
.BI $ keyword : .\|.\|. $
in the named files or, if no file name appears, the standard input.
.PP
These patterns are normally inserted automatically by the \*r command
.BR co (1),
but can also be inserted manually.
The option
.B \-q
suppresses
the warning given if there are no patterns in a file.
.PP
.B ident
works on text files as well as object files and dumps.
For example, if the C program in
.B f.c
contains
.IP
\f3char rcsid[] = \&"$\&Id: f.c,v \*(iD $\&";\fP
.LP
and
.B f.c
is compiled into
.BR f.o ,
then the command
.IP
.B "ident f.c f.o"
.LP
will output
.nf
.IP
.ft 3
f.c:
$\&Id: f.c,v \*(iD $
f.o:
$\&Id: f.c,v \*(iD $
.ft
.fi
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.

View file

@ -0,0 +1,214 @@
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* RCS identification operation
*/
/* $Log: ident.c,v $
* Revision 5.3 1991/09/10 22:15:46 eggert
* Open files with FOPEN_R, not FOPEN_R_WORK,
* because they might be executables, not working files.
*
* Revision 5.2 1991/08/19 03:13:55 eggert
* Report read errors immediately.
*
* Revision 5.1 1991/02/25 07:12:37 eggert
* Don't report empty keywords. Check for I/O errors.
*
* Revision 5.0 1990/08/22 08:12:37 eggert
* Don't limit output to known keywords.
* Remove arbitrary limits and lint. Ansify and Posixate.
*
* Revision 4.5 89/05/01 15:11:54 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 87/10/23 17:09:57 narten
* added exit(0) so exit return code would be non random
*
* Revision 4.3 87/10/18 10:23:55 narten
* Updating version numbers. Changes relative to 1.1 are actually relative
* to 4.1
*
* Revision 1.3 87/07/09 09:20:52 trinkle
* Added check to make sure there is at least one arg before comparing argv[1]
* with "-q". This necessary on machines that don't allow dereferncing null
* pointers (i.e. Suns).
*
* Revision 1.2 87/03/27 14:21:47 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:31:02 wft
* Added option -q and input from reading stdin.
* Marker matching is now done with trymatch() (independent of keywords).
*
* Revision 3.4 83/02/18 17:37:49 wft
* removed printing of new line after last file.
*
* Revision 3.3 82/12/04 12:48:55 wft
* Added LOCKER.
*
* Revision 3.2 82/11/28 18:24:17 wft
* removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
*
* Revision 3.1 82/10/13 15:58:51 wft
* fixed type of variables receiving from getc() (char-->int).
*/
#include "rcsbase.h"
static int match P((FILE*));
static void scanfile P((FILE*,char const*,int));
mainProg(identId, "ident", "$Id: ident.c,v 5.3 1991/09/10 22:15:46 eggert Exp $")
/* Ident searches the named files for all occurrences
* of the pattern $keyword:...$, where the keywords are
* Author, Date, Header, Id, Log, RCSfile, Revision, Source, and State.
*/
{
FILE *fp;
int quiet;
int status = EXIT_SUCCESS;
if ((quiet = argc > 1 && strcmp("-q",argv[1])==0)) {
argc--; argv++;
}
if (argc<2)
scanfile(stdin, (char*)0, quiet);
while ( --argc > 0 ) {
if (!(fp = fopen(*++argv, FOPEN_R))) {
VOID fprintf(stderr, "%s error: can't open %s\n", cmdid, *argv);
status = EXIT_FAILURE;
} else {
scanfile(fp, *argv, quiet);
if (argc>1) VOID putchar('\n');
}
}
if (ferror(stdout) || fclose(stdout)!=0) {
VOID fprintf(stderr, "%s error: write error\n", cmdid);
status = EXIT_FAILURE;
}
exitmain(status);
}
#if lint
exiting void identExit() { _exit(EXIT_FAILURE); }
#endif
static void
scanfile(file, name, quiet)
register FILE *file;
char const *name;
int quiet;
/* Function: scan an open file with descriptor file for keywords.
* Return false if there's a read error.
*/
{
register int c;
if (name)
VOID printf("%s:\n", name);
else
name = "input";
c = 0;
for (;;) {
if (c < 0) {
if (feof(file))
break;
if (ferror(file))
goto read_error;
}
if (c == KDELIM) {
if ((c = match(file)))
continue;
quiet = true;
}
c = getc(file);
}
if (!quiet)
VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
if (fclose(file) == 0)
return;
read_error:
VOID fprintf(stderr, "%s error: %s: read error\n", cmdid, name);
exit(EXIT_FAILURE);
}
static int
match(fp) /* group substring between two KDELIM's; then do pattern match */
register FILE *fp;
{
char line[BUFSIZ];
register int c;
register char * tp;
tp = line;
while ((c = getc(fp)) != VDELIM) {
if (c < 0)
return c;
switch (ctab[c]) {
case LETTER: case Letter:
*tp++ = c;
if (tp < line+sizeof(line)-4)
break;
/* fall into */
default:
return c ? c : '\n'/* anything but 0 or KDELIM or EOF */;
}
}
if (tp == line)
return c;
*tp++ = c;
if ((c = getc(fp)) != ' ')
return c ? c : '\n';
*tp++ = c;
while( (c = getc(fp)) != KDELIM ) {
if (c < 0 && feof(fp) | ferror(fp))
return c;
switch (ctab[c]) {
default:
*tp++ = c;
if (tp < line+sizeof(line)-2)
break;
/* fall into */
case NEWLN: case UNKN:
return c ? c : '\n';
}
}
if (tp[-1] != ' ')
return c;
*tp++ = c; /*append trailing KDELIM*/
*tp = '\0';
VOID fprintf(stdout, " %c%s\n", KDELIM, line);
return 0;
}

View file

@ -0,0 +1,5 @@
LIB= rcs
SRCS= maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c rcskeep.c \
rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcsutil.c merger.c
.include <bsd.lib.mk>

495
gnu/usr.bin/rcs/lib/conf.h Normal file
View file

@ -0,0 +1,495 @@
/* RCS compile-time configuration */
/* $Id: conf.sh,v 5.14 1991/11/20 18:21:10 eggert Exp $ */
/*
* This file is generated automatically.
* If you edit it by hand your changes may be lost.
* Instead, please try to fix conf.sh,
* and send your fixes to rcs-bugs@cs.purdue.edu.
*/
#define exitmain(n) return n /* how to exit from main() */
/* #define _POSIX_SOURCE */ /* Define this if Posix + strict Standard C. */
#include <errno.h>
#include <stdio.h>
#include <time.h>
/* Comment out #include lines below that do not work. */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utime.h>
/* #include <vfork.h> */
/* Define the following symbols to be 1 or 0. */
#define has_sys_dir_h 1 /* Does #include <sys/dir.h> work? */
#define has_sys_param_h 1 /* Does #include <sys/param.h> work? */
#define has_readlink 1 /* Does readlink() work? */
/* #undef NAME_MAX */ /* Uncomment this if NAME_MAX is broken. */
#if !defined(NAME_MAX) && !defined(_POSIX_NAME_MAX)
# if has_sys_dir_h
# include <sys/dir.h>
# endif
# ifndef NAME_MAX
# ifndef MAXNAMLEN
# define MAXNAMLEN 14
# endif
# define NAME_MAX MAXNAMLEN
# endif
#endif
#if !defined(PATH_MAX) && !defined(_POSIX_PATH_MAX)
# if has_sys_param_h
# include <sys/param.h>
# define included_sys_param_h 1
# endif
# ifndef PATH_MAX
# ifndef MAXPATHLEN
# define MAXPATHLEN 1024
# endif
# define PATH_MAX (MAXPATHLEN-1)
# endif
#endif
#if has_readlink && !defined(MAXSYMLINKS)
# if has_sys_param_h && !included_sys_param_h
# include <sys/param.h>
# endif
# ifndef MAXSYMLINKS
# define MAXSYMLINKS 20 /* BSD; not standard yet */
# endif
#endif
/* Comment out the keyword definitions below if the keywords work. */
/* #define const */
/* #define volatile */
/* Comment out the typedefs below if the types are already declared. */
/* Fix any uncommented typedefs that are wrong. */
/* typedef int mode_t; */
/* typedef int pid_t; */
typedef int sig_atomic_t;
/* typedef unsigned size_t; */
/* typedef int ssize_t; */
/* typedef long time_t; */
/* typedef int uid_t; */
/* Define the following symbols to be 1 or 0. */
#define has_prototypes 1 /* Do function prototypes work? */
#define has_stdarg 1 /* Does <stdarg.h> work? */
#define has_varargs 0 /* Does <varargs.h> work? */
#define va_start_args 2 /* How many args does va_start() take? */
#if has_prototypes
# define P(params) params
#else
# define P(params) ()
#endif
#if has_stdarg
# include <stdarg.h>
#else
# if has_varargs
# include <varargs.h>
# else
typedef char *va_list;
# define va_dcl int va_alist;
# define va_start(ap) ((ap) = (va_list)&va_alist)
# define va_arg(ap,t) (((t*) ((ap)+=sizeof(t))) [-1])
# define va_end(ap)
# endif
#endif
#if va_start_args == 2
# define vararg_start va_start
#else
# define vararg_start(ap,p) va_start(ap)
#endif
#define text_equals_binary_stdio 1 /* Does stdio treat text like binary? */
#define text_work_stdio 0 /* Text i/o for working file, binary for RCS file? */
#if text_equals_binary_stdio
/* Text and binary i/o behave the same, or binary i/o does not work. */
# define FOPEN_R "r"
# define FOPEN_W "w"
# define FOPEN_WPLUS "w+"
#else
/* Text and binary i/o behave differently. */
/* This is incompatible with Posix and Unix. */
# define FOPEN_R "rb"
# define FOPEN_W "wb"
# define FOPEN_WPLUS "w+b"
#endif
#if text_work_stdio
# define FOPEN_R_WORK "r"
# define FOPEN_W_WORK "w"
# define FOPEN_WPLUS_WORK "w+"
#else
# define FOPEN_R_WORK FOPEN_R
# define FOPEN_W_WORK FOPEN_W
# define FOPEN_WPLUS_WORK FOPEN_WPLUS
#endif
/* Define or comment out the following symbols as needed. */
#define bad_fopen_wplus 0 /* Does fopen(f,FOPEN_WPLUS) fail to truncate f? */
#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */
#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */
#define has_fchmod 0 /* Does fchmod() work? */
#define has_fputs 0 /* Does fputs() work? */
#define has_ftruncate 1 /* Does ftruncate() work? */
#define has_getuid 1 /* Does getuid() work? */
#define has_getpwuid 1 /* Does getpwuid() work? */
#define has_link 1 /* Does link() work? */
#define has_memcmp 1 /* Does memcmp() work? */
#define has_memcpy 1 /* Does memcpy() work? */
#define has_memmove 1 /* Does memmove() work? */
#define has_madvise 0 /* Does madvise() work? */
#define has_mmap 0 /* Does mmap() work on regular files? */
#define has_rename 1 /* Does rename() work? */
#define bad_a_rename 0 /* Does rename(A,B) fail if A is unwritable? */
#define bad_b_rename 0 /* Does rename(A,B) fail if B is unwritable? */
#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */
#define has_seteuid 0 /* Does seteuid() work? See README. */
#define has_setuid 1 /* Does setuid() exist? */
#define has_signal 1 /* Does signal() work? */
#define signal_args P((int)) /* arguments of signal handlers */
#define signal_type void /* type returned by signal handlers */
#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */
#define has_sigaction 1 /* Does struct sigaction work? */
/* #define has_sigblock ? */ /* Does sigblock() work? */
/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */
#define has_sys_siglist 0 /* Does sys_siglist[] work? */
typedef ssize_t fread_type; /* type returned by fread() and fwrite() */
typedef size_t freadarg_type; /* type of their size arguments */
typedef void *malloc_type; /* type returned by malloc() */
#define has_getcwd 1 /* Does getcwd() work? */
/* #define has_getwd ? */ /* Does getwd() work? */
#define has_mktemp 1 /* Does mktemp() work? */
#define has_NFS 1 /* Might NFS be used? */
/* #define strchr index */ /* Use old-fashioned name for strchr()? */
/* #define strrchr rindex */ /* Use old-fashioned name for strrchr()? */
#define bad_unlink 0 /* Does unlink() fail on unwritable files? */
#define has_vfork 0 /* Does vfork() work? */
#define has_fork 1 /* Does fork() work? */
#define has_spawn 0 /* Does spawn*() work? */
#define has_wait 1 /* Does wait() work? */
#define has_waitpid 0 /* Does waitpid() work? */
#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */
#define has_vfprintf 1 /* Does vfprintf() work? */
/* #define has__doprintf ? */ /* Does _doprintf() work? */
/* #define has__doprnt ? */ /* Does _doprnt() work? */
/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */
#define large_memory 0 /* Can main memory hold entire RCS files? */
/* #undef ULONG_MAX */ /* Uncomment this if ULONG_MAX is broken (e.g. < 0). */
/* struct utimbuf { time_t actime, modtime; }; */ /* Uncomment this if needed. */
#define CO "/usr/bin/co" /* name of 'co' program */
#define COMPAT2 0 /* Are version 2 files supported? */
#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */
#define DIFF "/usr/bin/diff" /* name of 'diff' program */
#define DIFF3 "/usr/bin/diff3" /* name of 'diff3' program */
#define DIFF3_BIN 1 /* Is diff3 user-visible (not the /usr/lib auxiliary)? */
#define DIFF_FLAGS , "-an" /* Make diff output suitable for RCS. */
#define DIFF_L 1 /* Does diff -L work? */
#define DIFF_SUCCESS 0 /* DIFF status if no differences are found */
#define DIFF_FAILURE 1 /* DIFF status if differences are found */
#define DIFF_TROUBLE 2 /* DIFF status if trouble */
#define ED "/bin/ed" /* name of 'ed' program (used only if !DIFF3_BIN) */
#define MERGE "/usr/bin/merge" /* name of 'merge' program */
#define TMPDIR "/tmp" /* default directory for temporary files */
#define SLASH '/' /* principal pathname separator */
#define SLASHes '/' /* `case SLASHes:' labels all pathname separators */
#define isSLASH(c) ((c) == SLASH) /* Is arg a pathname separator? */
#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */
#define X_DEFAULT ",v/" /* default value for -x option */
#define DIFF_ABSOLUTE 1 /* Is ROOTPATH(DIFF) true? */
#define ALL_ABSOLUTE 1 /* Are all subprograms absolute pathnames? */
#define SENDMAIL "/usr/bin/mail" /* how to send mail */
#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */
/* Adjust the following declarations as needed. */
#if __GNUC__ && !__STRICT_ANSI__
# define exiting volatile /* GCC extension: function cannot return */
#else
# define exiting
#endif
#if has_ftruncate
int ftruncate P((int,off_t));
#endif
/* <sys/mman.h> */
#if has_madvise
int madvise P((caddr_t,size_t,int));
#endif
#if has_mmap
caddr_t mmap P((caddr_t,size_t,int,int,int,off_t));
int munmap P((caddr_t,size_t));
#endif
/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */
/* These definitions are for the benefit of non-Posix hosts, and */
/* Posix hosts that have Standard C compilers but traditional include files. */
/* Unfortunately, mixed-up hosts are all too common. */
/* <fcntl.h> */
#ifdef F_DUPFD
int fcntl P((int,int,...));
#else
int dup2 P((int,int));
#endif
#ifndef O_BINARY /* some non-Posix hosts need O_BINARY */
# define O_BINARY 0 /* no effect on Posix */
#endif
#ifdef O_CREAT
# define open_can_creat 1
#else
# define open_can_creat 0
# define O_RDONLY 0
# define O_WRONLY 1
# define O_RDWR 2
# define O_CREAT 01000
# define O_TRUNC 02000
int creat P((char const*,mode_t));
#endif
#ifndef O_EXCL
# define O_EXCL 0
#endif
/* <pwd.h> */
#if has_getpwuid
struct passwd *getpwuid P((uid_t));
#endif
/* <signal.h> */
#if has_sigaction
int sigaction P((int,struct sigaction const*,struct sigaction*));
int sigaddset P((sigset_t*,int));
int sigemptyset P((sigset_t*));
#else
#if has_sigblock
/* BSD */
int sigblock P((int));
int sigmask P((int));
int sigsetmask P((int));
#endif
#endif
/* <stdio.h> */
FILE *fdopen P((int,char const*));
int fileno P((FILE*));
/* <sys/stat.h> */
int chmod P((char const*,mode_t));
int fstat P((int,struct stat*));
int stat P((char const*,struct stat*));
mode_t umask P((mode_t));
#if has_fchmod
int fchmod P((int,mode_t));
#endif
#ifndef S_IRUSR
# ifdef S_IREAD
# define S_IRUSR S_IREAD
# else
# define S_IRUSR 0400
# endif
# ifdef S_IWRITE
# define S_IWUSR S_IWRITE
# else
# define S_IWUSR (S_IRUSR/2)
# endif
#endif
#ifndef S_IRGRP
# if has_getuid
# define S_IRGRP (S_IRUSR / 0010)
# define S_IWGRP (S_IWUSR / 0010)
# define S_IROTH (S_IRUSR / 0100)
# define S_IWOTH (S_IWUSR / 0100)
# else
/* single user OS -- not Posix or Unix */
# define S_IRGRP 0
# define S_IWGRP 0
# define S_IROTH 0
# define S_IWOTH 0
# endif
#endif
#ifndef S_ISREG
# define S_ISREG(n) (((n) & S_IFMT) == S_IFREG)
#endif
/* <sys/wait.h> */
#if has_wait
pid_t wait P((int*));
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
# undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (!((stat_val) & 255))
#endif
/* <unistd.h> */
char *getlogin P((void));
int close P((int));
int isatty P((int));
int link P((char const*,char const*));
int open P((char const*,int,...));
int unlink P((char const*));
int _filbuf P((FILE*)); /* keeps lint quiet in traditional C */
int _flsbuf P((int,FILE*)); /* keeps lint quiet in traditional C */
long pathconf P((char const*,int));
ssize_t write P((int,void const*,size_t));
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
#endif
#if has_fork
# if !has_vfork
# undef vfork
# define vfork fork
# endif
pid_t vfork P((void)); /* vfork is nonstandard but faster */
#endif
#if has_getcwd || !has_getwd
char *getcwd P((char*,size_t));
#else
char *getwd P((char*));
#endif
#if has_getuid
uid_t getuid P((void));
#endif
#if has_readlink
/* ssize_t readlink P((char const*,char*,size_t)); *//* BSD; not standard yet */
#endif
#if has_setuid
# if !has_seteuid
# undef seteuid
# define seteuid setuid
# endif
int seteuid P((uid_t));
uid_t geteuid P((void));
#endif
#if has_spawn
int spawnv P((int,char const*,char*const*));
# if ALL_ABSOLUTE
# define spawn_RCS spawnv
# else
# define spawn_RCS spawnvp
int spawnvp P((int,char const*,char*const*));
# endif
#else
int execv P((char const*,char*const*));
# if ALL_ABSOLUTE
# define exec_RCS execv
# else
# define exec_RCS execvp
int execvp P((char const*,char*const*));
# endif
#endif
/* utime.h */
int utime P((char const*,struct utimbuf const*));
/* Standard C library */
/* These definitions are for the benefit of hosts that have */
/* traditional C include files, possibly with Standard C compilers. */
/* Unfortunately, mixed-up hosts are all too common. */
/* <errno.h> */
extern int errno;
/* <limits.h> */
#ifndef ULONG_MAX
/* This does not work in #ifs, but it's good enough for us. */
# define ULONG_MAX ((unsigned long)-1)
#endif
/* <signal.h> */
#if has_signal
signal_type (*signal P((int,signal_type(*)signal_args)))signal_args;
#endif
/* <stdio.h> */
FILE *fopen P((char const*,char const*));
fread_type fread P((void*,freadarg_type,freadarg_type,FILE*));
fread_type fwrite P((void const*,freadarg_type,freadarg_type,FILE*));
int fclose P((FILE*));
int feof P((FILE*));
int ferror P((FILE*));
int fflush P((FILE*));
int fprintf P((FILE*,char const*,...));
int fputs P((char const*,FILE*));
int fseek P((FILE*,long,int));
int printf P((char const*,...));
int rename P((char const*,char const*));
int sprintf P((char*,char const*,...));
/* long ftell P((FILE*)); */
void clearerr P((FILE*));
void perror P((char const*));
#ifndef L_tmpnam
# define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */
#endif
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#if has_mktemp
char *mktemp P((char*)); /* traditional */
#else
char *tmpnam P((char*));
#endif
#if has_vfprintf
int vfprintf P((FILE*,char const*,va_list));
#else
#if has__doprintf
void _doprintf P((FILE*,char const*,va_list)); /* Minix */
#else
void _doprnt P((char const*,va_list,FILE*)); /* BSD */
#endif
#endif
/* <stdlib.h> */
char *getenv P((char const*));
exiting void _exit P((int));
exiting void exit P((int));
malloc_type malloc P((size_t));
malloc_type realloc P((malloc_type,size_t));
void free P((malloc_type));
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#if !has_fork && !has_spawn
int system P((char const*));
#endif
/* <string.h> */
char *strcpy P((char*,char const*));
char *strchr P((char const*,int));
char *strrchr P((char const*,int));
int memcmp P((void const*,void const*,size_t));
int strcmp P((char const*,char const*));
size_t strlen P((char const*));
void *memcpy P((void*,void const*,size_t));
#if has_memmove
void *memmove P((void*,void const*,size_t));
#endif
/* <time.h> */
time_t time P((time_t*));

View file

@ -0,0 +1,344 @@
#
/*
* MAKETIME derive 32-bit time value from TM structure.
*
* Usage:
* int zone; Minutes west of GMT, or
* 48*60 for localtime
* time_t t;
* struct tm *tp; Pointer to TM structure from <time.h>
* t = maketime(tp,zone);
*
* Returns:
* -1 if failure; parameter out of range or nonsensical.
* else time-value.
* Notes:
* This code is quasi-public; it may be used freely in like software.
* It is not to be sold, nor used in licensed software without
* permission of the author.
* For everyone's benefit, please report bugs and improvements!
* Copyright 1981 by Ken Harrenstien, SRI International.
* (ARPANET: KLH @ SRI)
*/
/* $Log: maketime.c,v $
* Revision 5.3 1991/08/19 03:13:55 eggert
* Add setfiledate, str2time, TZ_must_be_set.
*
* Revision 5.2 1990/11/01 05:03:30 eggert
* Remove lint.
*
* Revision 5.1 1990/10/04 06:30:13 eggert
* Calculate the GMT offset of 'xxx LT' as of xxx, not as of now.
* Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time.
*
* Revision 5.0 1990/08/22 08:12:38 eggert
* Switch to GMT and fix the bugs exposed thereby.
* Permit dates past 1999/12/31. Ansify and Posixate.
*
* Revision 1.8 88/11/08 13:54:53 narten
* allow negative timezones (-24h <= x <= 24h)
*
* Revision 1.7 88/08/28 14:47:52 eggert
* Allow cc -R. Remove unportable "#endif XXX"s.
*
* Revision 1.6 87/12/18 17:05:58 narten
* include rcsparam.h
*
* Revision 1.5 87/12/18 11:35:51 narten
* maketime.c: fixed USG code - you have tgo call "tzset" in order to have
* "timezone" set. ("localtime" calls it, but it's probably better not to
* count on "localtime" having been called.)
*
* Revision 1.4 87/10/18 10:26:57 narten
* Updating version numbers. Changes relative to 1.0 are actually
* relative to 1.2
*
* Revision 1.3 87/09/24 13:58:45 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:21:48 jenkins
* Port to suns
*
* Revision 1.2 83/12/05 10:12:56 wft
* added cond. compilation for USG Unix; long timezone;
*
* Revision 1.1 82/05/06 11:38:00 wft
* Initial revision
*
*/
#include "rcsbase.h"
libId(maketId, "$Id: maketime.c,v 5.3 1991/08/19 03:13:55 eggert Exp $")
static struct tm const *time2tm P((time_t));
#define given(v) (0 <= (v)) /* Negative values are unspecified. */
static int const daytb[] = {
/* # days in year thus far, indexed by month (0-12!!) */
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
static time_t
maketime(atm,zone)
struct tm const *atm;
int zone;
{
register struct tm const *tp;
register int i;
int year, yday, mon, day, hour, min, sec, leap, localzone;
int attempts;
time_t t, tres;
attempts = 2;
localzone = zone==48*60;
tres = -1;
year = mon = day = 0; /* Keep lint happy. */
do {
if (localzone || !given(atm->tm_year)) {
if (tres == -1)
if ((tres = time((time_t*)0)) == -1)
return -1;
tp = time2tm(tres);
/* Get breakdowns of default time, adjusting to zone. */
year = tp->tm_year; /* Use to set up defaults */
yday = tp->tm_yday;
mon = tp->tm_mon;
day = tp->tm_mday;
hour = tp->tm_hour;
min = tp->tm_min;
if (localzone) {
tp = localtime(&tres);
zone =
min - tp->tm_min + 60*(
hour - tp->tm_hour + 24*(
/* If years differ, it's by one day. */
year - tp->tm_year
? year - tp->tm_year
: yday - tp->tm_yday));
}
/* Adjust the default day, month and year according to zone. */
if ((min -= zone) < 0) {
if (hour-(59-min)/60 < 0 && --day <= 0) {
if (--mon < 0) {
--year;
mon = 11;
}
day = daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3));
}
} else
if (
24 <= hour+min/60 &&
daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)) < ++day
) {
if (11 < ++mon) {
++year;
mon = 0;
}
day = 1;
}
}
if (zone < -24*60 || 24*60 < zone)
return -1;
#ifdef DEBUG
printf("first YMD: %d %d %d\n",year,mon,day);
#endif
tp = atm;
/* First must find date, using specified year, month, day.
* If one of these is unspecified, it defaults either to the
* current date (if no more global spec was given) or to the
* zero-value for that spec (i.e. a more global spec was seen).
* Reject times that do not fit in time_t,
* without assuming that time_t is 32 bits or is signed.
*/
if (given(tp->tm_year))
{
year = tp->tm_year;
mon = 0; /* Since year was given, default */
day = 1; /* for remaining specs is zero */
}
if (year < 69) /* 1969/12/31 OK in some timezones. */
return -1; /* ERR: year out of range */
leap = !(year&3) && (year%100 || !((year+300)%400));
year -= 70; /* UNIX time starts at 1970 */
/*
* Find day of year.
*/
{
if (given(tp->tm_mon))
{ mon = tp->tm_mon; /* Month was specified */
day = 1; /* so set remaining default */
}
if (11 < (unsigned)mon)
return -1; /* ERR: bad month */
if (given(tp->tm_mday)) day = tp->tm_mday;
if(day < 1
|| (((daytb[mon+1]-daytb[mon]) < day)
&& (day!=29 || mon!=1 || !leap) ))
return -1; /* ERR: bad day */
yday = daytb[mon] /* Add # of days in months so far */
+ ((leap /* Leap year, and past Feb? If */
&& mon>1)? 1:0) /* so, add leap day for this year */
+ day-1; /* And finally add # days this mon */
}
if (leap+365 <= (unsigned)yday)
return -1; /* ERR: bad YDAY */
if (year < 0) {
if (yday != 364)
return -1; /* ERR: too early */
t = -1;
} else {
tres = year*365; /* Get # days of years so far */
if (tres/365 != year)
return -1; /* ERR: overflow */
t = tres
+ ((year+1)>>2) /* plus # of leap days since 1970 */
+ yday; /* and finally add # days this year */
if (t+4 < tres)
return -1; /* ERR: overflow */
}
tres = t;
if (given(i = tp->tm_wday)) /* Check WDAY if present */
if (i != (tres+4)%7) /* 1970/01/01 was Thu = 4 */
return -1; /* ERR: bad WDAY */
#ifdef DEBUG
printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
#endif
/*
* Now determine time. If not given, default to zeros
* (since time is always the least global spec)
*/
tres *= 86400L; /* Get # seconds (24*60*60) */
if (tres/86400L != t)
return -1; /* ERR: overflow */
hour = min = sec = 0;
if (given(tp->tm_hour)) hour = tp->tm_hour;
if (given(tp->tm_min )) min = tp->tm_min;
if (given(tp->tm_sec )) sec = tp->tm_sec;
if (60 <= (unsigned)min || 60 < (unsigned)sec)
return -1; /* ERR: MS out of range */
if (24 <= (unsigned)hour)
if(hour != 24 || (min+sec) !=0) /* Allow 24:00 */
return -1; /* ERR: H out of range */
t = tres;
tres += sec + 60L*(zone + min + 60*hour);
#ifdef DEBUG
printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres);
#endif
if (!localzone) /* check for overflow */
return (year<0 ? (tres<0||86400L<=tres) : tres<t) ? -1 : tres;
/* Check results; LT may have had a different GMT offset back then. */
tp = localtime(&tres);
if (given(atm->tm_sec) && atm->tm_sec != tp->tm_sec)
return -1; /* If seconds don't match, we're in trouble. */
if (!(
given(atm->tm_min) && atm->tm_min != tp->tm_min ||
given(atm->tm_hour) && atm->tm_hour != tp->tm_hour ||
given(atm->tm_mday) && atm->tm_mday != tp->tm_mday ||
given(atm->tm_mon) && atm->tm_mon != tp->tm_mon ||
given(atm->tm_year) && atm->tm_year != tp->tm_year
))
return tres; /* Everything matches. */
} while (--attempts);
return -1;
}
/*
* Convert Unix time to struct tm format.
* Use Coordinated Universal Time (UTC) if version 5 or newer;
* use local time otherwise.
*/
static struct tm const *
time2tm(unixtime)
time_t unixtime;
{
struct tm const *tm;
# if TZ_must_be_set
static char const *TZ;
if (!TZ && !(TZ = getenv("TZ")))
faterror("TZ is not set");
# endif
if (!(tm = (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime)))
faterror("UTC is not available; perhaps TZ is not set?");
return tm;
}
/*
* Convert Unix time to RCS format.
* For compatibility with older versions of RCS,
* dates before AD 2000 are stored without the leading "19".
*/
void
time2date(unixtime,date)
time_t unixtime;
char date[datesize];
{
register struct tm const *tm = time2tm(unixtime);
VOID sprintf(date, DATEFORM,
tm->tm_year + (tm->tm_year<100 ? 0 : 1900),
tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec
);
}
static time_t
str2time(source)
char const *source;
/* Parse a free-format date in SOURCE, yielding a Unix format time. */
{
int zone;
time_t unixtime;
struct tm parseddate;
if (!partime(source, &parseddate, &zone))
faterror("can't parse date/time: %s", source);
if ((unixtime = maketime(&parseddate, zone)) == -1)
faterror("bad date/time: %s", source);
return unixtime;
}
void
str2date(source, target)
char const *source;
char target[datesize];
/* Parse a free-format date in SOURCE, convert it
* into RCS internal format, and store the result into TARGET.
*/
{
time2date(str2time(source), target);
}
int
setfiledate(file, date)
char const *file, date[datesize];
/* Set the access and modification time of FILE to DATE. */
{
static struct utimbuf times; /* static so unused fields are zero */
char datebuf[datesize];
if (!date)
return 0;
times.actime = times.modtime = str2time(date2str(date, datebuf));
return utime(file, &times);
}

View file

@ -0,0 +1,139 @@
/* merger - three-way file merge internals */
/* Copyright 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
libId(mergerId, "$Id: merger.c,v 1.3 1991/08/20 23:05:00 eggert Exp $")
static char const *
normalize_arg(s, b)
char const *s;
char **b;
/*
* If S looks like an option, prepend ./ to it. Yield the result.
* Set *B to the address of any storage that was allocated..
*/
{
char *t;
switch (*s) {
case '-': case '+':
*b = t = testalloc(strlen(s) + 3);
VOID sprintf(t, ".%c%s", SLASH, s);
return t;
default:
*b = 0;
return s;
}
}
int
merge(tostdout, label, argv)
int tostdout;
char const *const label[2];
char const *const argv[3];
/*
* Do `merge [-p] -L l0 -L l1 a0 a1 a2',
* where TOSTDOUT specifies whether -p is present,
* LABEL gives l0 and l1, and ARGV gives a0, a1, and a2.
* Yield DIFF_SUCCESS or DIFF_FAILURE.
*/
{
register int i;
FILE *f;
RILE *rt;
char const *a[3], *t;
char *b[3];
int s;
#if !DIFF3_BIN
char const *d[2];
#endif
for (i=3; 0<=--i; )
a[i] = normalize_arg(argv[i], &b[i]);
#if DIFF3_BIN
t = 0;
if (!tostdout)
t = maketemp(0);
s = run(
(char*)0, t,
DIFF3, "-am", "-L", label[0], "-L", label[1],
a[0], a[1], a[2], (char*)0
);
switch (s) {
case DIFF_SUCCESS:
break;
case DIFF_FAILURE:
if (!quietflag)
warn("overlaps during merge");
break;
default:
exiterr();
}
if (t) {
if (!(f = fopen(argv[0], FOPEN_W)))
efaterror(argv[0]);
if (!(rt = Iopen(t, FOPEN_R, (struct stat*)0)))
efaterror(t);
fastcopy(rt, f);
Ifclose(rt);
Ofclose(f);
}
#else
for (i=0; i<2; i++)
switch (run(
(char*)0, d[i]=maketemp(i),
DIFF, a[i], a[2], (char*)0
)) {
case DIFF_FAILURE: case DIFF_SUCCESS: break;
default: exiterr();
}
t = maketemp(2);
s = run(
(char*)0, t,
DIFF3, "-E", d[0], d[1], a[0], a[1], a[2],
label[0], label[1], (char*)0
);
if (s != DIFF_SUCCESS) {
s = DIFF_FAILURE;
if (!quietflag)
warn("overlaps or other problems during merge");
}
if (!(f = fopen(t, "a")))
efaterror(t);
aputs(tostdout ? "1,$p\n" : "w\n", f);
Ofclose(f);
if (run(t, (char*)0, ED, "-", a[0], (char*)0))
exiterr();
#endif
tempunlink();
for (i=3; 0<=--i; )
if (b[i])
tfree(b[i]);
return s;
}

View file

@ -0,0 +1,639 @@
/*
* PARTIME parse date/time string into a TM structure
*
* Returns:
* 0 if parsing failed
* else time values in specified TM structure and zone (unspecified values
* set to TMNULL)
* Notes:
* This code is quasi-public; it may be used freely in like software.
* It is not to be sold, nor used in licensed software without
* permission of the author.
* For everyone's benefit, please report bugs and improvements!
* Copyright 1980 by Ken Harrenstien, SRI International.
* (ARPANET: KLH @ SRI)
*/
/* Hacknotes:
* If parsing changed so that no backup needed, could perhaps modify
* to use a FILE input stream. Need terminator, though.
* Perhaps should return 0 on success, else a non-zero error val?
*/
/* $Log: partime.c,v $
* Revision 5.6 1991/08/19 03:13:55 eggert
* Update timezones.
*
* Revision 5.5 1991/04/21 11:58:18 eggert
* Don't put , just before } in initializer.
*
* Revision 5.4 1990/10/04 06:30:15 eggert
* Remove date vs time heuristics that fail between 2000 and 2400.
* Check for overflow when lexing an integer.
* Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
*
* Revision 5.3 1990/09/24 18:56:31 eggert
* Update timezones.
*
* Revision 5.2 1990/09/04 08:02:16 eggert
* Don't parse two-digit years, because it won't work after 1999/12/31.
* Don't permit 'Aug Aug'.
*
* Revision 5.1 1990/08/29 07:13:49 eggert
* Be able to parse our own date format. Don't assume year<10000.
*
* Revision 5.0 1990/08/22 08:12:40 eggert
* Switch to GMT and fix the bugs exposed thereby. Update timezones.
* Ansify and Posixate. Fix peekahead and int-size bugs.
*
* Revision 1.4 89/05/01 14:48:46 narten
* fixed #ifdef DEBUG construct
*
* Revision 1.3 88/08/28 14:53:40 eggert
* Remove unportable "#endif XXX"s.
*
* Revision 1.2 87/03/27 14:21:53 jenkins
* Port to suns
*
* Revision 1.1 82/05/06 11:38:26 wft
* Initial revision
*
*/
#include "rcsbase.h"
libId(partId, "$Id: partime.c,v 5.6 1991/08/19 03:13:55 eggert Exp $")
#define given(v) (0 <= (v))
#define TMNULL (-1) /* Items not given are given this value */
#define TZ_OFFSET (24*60) /* TMNULL < zone_offset - TZ_OFFSET */
struct tmwent {
char const *went;
short wval;
char wflgs;
char wtype;
};
/* wflgs */
#define TWTIME 02 /* Word is a time value (absence implies date) */
#define TWDST 04 /* Word is a DST-type timezone */
/* wtype */
#define TM_MON 1 /* month name */
#define TM_WDAY 2 /* weekday name */
#define TM_ZON 3 /* time zone name */
#define TM_LT 4 /* local time */
#define TM_DST 5 /* daylight savings time */
#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */
/* wval (for wtype==TM_12) */
#define T12_AM 1
#define T12_PM 2
#define T12_NOON 12
#define T12_MIDNIGHT 0
static struct tmwent const tmwords [] = {
{"january", 0, 0, TM_MON},
{"february", 1, 0, TM_MON},
{"march", 2, 0, TM_MON},
{"april", 3, 0, TM_MON},
{"may", 4, 0, TM_MON},
{"june", 5, 0, TM_MON},
{"july", 6, 0, TM_MON},
{"august", 7, 0, TM_MON},
{"september", 8, 0, TM_MON},
{"october", 9, 0, TM_MON},
{"november", 10, 0, TM_MON},
{"december", 11, 0, TM_MON},
{"sunday", 0, 0, TM_WDAY},
{"monday", 1, 0, TM_WDAY},
{"tuesday", 2, 0, TM_WDAY},
{"wednesday", 3, 0, TM_WDAY},
{"thursday", 4, 0, TM_WDAY},
{"friday", 5, 0, TM_WDAY},
{"saturday", 6, 0, TM_WDAY},
{"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
{"utc", 0*60, TWTIME, TM_ZON},
{"ut", 0*60, TWTIME, TM_ZON},
{"cut", 0*60, TWTIME, TM_ZON},
{"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */
{"jst", -9*60, TWTIME, TM_ZON}, /* Japan */
{"kst", -9*60, TWTIME, TM_ZON}, /* Korea */
{"ist", -5*60-30, TWTIME, TM_ZON},/* India */
{"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */
{"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */
{"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */
{"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */
{"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */
{"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */
{"est", 5*60, TWTIME, TM_ZON}, /* Eastern */
{"cst", 6*60, TWTIME, TM_ZON}, /* Central */
{"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */
{"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */
{"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */
{"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */
{"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */
{"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */
{"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */
{"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */
{"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */
{"ndt", 3*60+30, TWTIME+TWDST, TM_ZON}, /* Newfoundland */
{"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */
{"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */
{"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */
{"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */
{"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */
{"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */
{"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */
#if 0
/*
* The following names are duplicates or are not well attested.
* A standard is needed.
*/
{"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */
{"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */
{"cst", -8*60, TWTIME, TM_ZON}, /* China */
{"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */
{"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */
{"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */
{"?", -6*60-30, TWTIME, TM_ZON},/* Burma */
{"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
{"it", -3*60-30, TWTIME, TM_ZON},/* Iran */
{"ist", -2*60, TWTIME, TM_ZON}, /* Israel */
{"mez", -1*60, TWTIME, TM_ZON}, /* Mittel-Europaeische Zeit */
{"ast", 1*60, TWTIME, TM_ZON}, /* Azores */
{"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */
{"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */
{"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */
{"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */
{"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */
{"?", 12*60, TWTIME, TM_ZON}, /* Kwajalein */
{"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */
{"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */
{"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */
{"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */
{"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */
{"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */
{"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */
{"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */
{"mesz", -1*60, TWTIME+TWDST, TM_ZON}, /* Mittel-Europaeische Sommerzeit */
{"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */
{"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */
{"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */
{"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */
{"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */
{"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */
#endif
{"lt", 0, TWTIME, TM_LT}, /* local time */
{"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */
{"ddst", 2*60, TWTIME, TM_DST}, /* double dst */
{"am", T12_AM, TWTIME, TM_12},
{"pm", T12_PM, TWTIME, TM_12},
{"noon", T12_NOON, TWTIME, TM_12},
{"midnight", T12_MIDNIGHT, TWTIME, TM_12},
{0, 0, 0, 0} /* Zero entry to terminate searches */
};
struct token {
char const *tcp;/* pointer to string */
int tcnt; /* # chars */
char tbrk; /* "break" char */
char tbrkl; /* last break char */
char tflg; /* 0 = alpha, 1 = numeric */
union { /* Resulting value; */
int tnum;/* either a #, or */
struct tmwent const *ttmw;/* a ptr to a tmwent. */
} tval;
};
static struct tmwent const*ptmatchstr P((char const*,int,struct tmwent const*));
static int pt12hack P((struct tm *,int));
static int ptitoken P((struct token *));
static int ptstash P((int *,int));
static int pttoken P((struct token *));
static int
goodzone(t, offset, am)
register struct token const *t;
int offset;
int *am;
{
register int m;
if (
t->tflg &&
t->tcnt == 4+offset &&
(m = t->tval.tnum) <= 2400 &&
isdigit(t->tcp[offset]) &&
(m%=100) < 60
) {
m += t->tval.tnum/100 * 60;
if (t->tcp[offset-1]=='+')
m = -m;
*am = m;
return 1;
}
return 0;
}
int
partime(astr, atm, zone)
char const *astr;
register struct tm *atm;
int *zone;
{
register int i;
struct token btoken, atoken;
int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
register char const *cp;
register char ch;
int ord, midnoon;
int *atmfield, dst, m;
int got1 = 0;
atm->tm_sec = TMNULL;
atm->tm_min = TMNULL;
atm->tm_hour = TMNULL;
atm->tm_mday = TMNULL;
atm->tm_mon = TMNULL;
atm->tm_year = TMNULL;
atm->tm_wday = TMNULL;
atm->tm_yday = TMNULL;
midnoon = TMNULL; /* and our own temp stuff */
zone_offset = TMNULL;
dst = TMNULL;
btoken.tcnt = btoken.tbrk = 0;
btoken.tcp = astr;
for (;; got1=1) {
if (!ptitoken(&btoken)) /* Get a token */
{ if(btoken.tval.tnum) return(0); /* Read error? */
if (given(midnoon)) /* EOF, wrap up */
if (!pt12hack(atm, midnoon))
return 0;
if (!given(atm->tm_min))
atm->tm_min = 0;
*zone =
(given(zone_offset) ? zone_offset-TZ_OFFSET : 0)
- (given(dst) ? dst : 0);
return got1;
}
if(btoken.tflg == 0) /* Alpha? */
{ i = btoken.tval.ttmw->wval;
switch (btoken.tval.ttmw->wtype) {
default:
return 0;
case TM_MON:
atmfield = &atm->tm_mon;
break;
case TM_WDAY:
atmfield = &atm->tm_wday;
break;
case TM_DST:
atmfield = &dst;
break;
case TM_LT:
if (ptstash(&dst, 0))
return 0;
i = 48*60; /* local time magic number -- see maketime() */
/* fall into */
case TM_ZON:
i += TZ_OFFSET;
if (btoken.tval.ttmw->wflgs & TWDST)
if (ptstash(&dst, 60))
return 0;
/* Peek ahead for offset immediately afterwards. */
if (
(btoken.tbrk=='-' || btoken.tbrk=='+') &&
(atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
goodzone(&atoken, 0, &m)
) {
i += m;
btoken = atoken;
}
atmfield = &zone_offset;
break;
case TM_12:
atmfield = &midnoon;
}
if (ptstash(atmfield, i))
return(0); /* ERR: val already set */
continue;
}
/* Token is number. Lots of hairy heuristics. */
if (!isdigit(*btoken.tcp)) {
if (!goodzone(&btoken, 1, &m))
return 0;
zone_offset = TZ_OFFSET + m;
continue;
}
i = btoken.tval.tnum; /* Value now known to be valid; get it. */
if (btoken.tcnt == 3) /* 3 digits = HMM */
{
hhmm4: if (ptstash(&atm->tm_min, i%100))
return(0); /* ERR: min conflict */
i /= 100;
hh2: if (ptstash(&atm->tm_hour, i))
return(0); /* ERR: hour conflict */
continue;
}
if (4 < btoken.tcnt)
goto year4; /* far in the future */
if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */
{ if (given(atm->tm_year)) goto hhmm4; /* Already got yr? */
if (given(atm->tm_hour)) goto year4; /* Already got hr? */
if(btoken.tbrk == ':') /* HHMM:SS ? */
if ( ptstash(&atm->tm_hour, i/100)
|| ptstash(&atm->tm_min, i%100))
return(0); /* ERR: hr/min clash */
else goto coltm2; /* Go handle SS */
if(btoken.tbrk != ',' && btoken.tbrk != '/'
&& (atoken=btoken, ptitoken(&atoken)) /* Peek */
&& ( atoken.tflg
? !isdigit(*atoken.tcp)
: atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
goto hhmm4;
goto year4; /* Give up, assume year. */
}
/* From this point on, assume tcnt == 1 or 2 */
/* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
if(btoken.tbrk == ':') /* HH:MM[:SS] */
goto coltime; /* must be part of time. */
if (31 < i)
return 0;
/* Check for numerical-format date */
for (cp = "/-."; ch = *cp++;)
{ ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */
if(btoken.tbrk == ch) /* "NN-" */
{ if(btoken.tbrkl != ch)
{
atoken = btoken;
atoken.tcnt++;
if (ptitoken(&atoken)
&& atoken.tflg == 0
&& atoken.tval.ttmw->wtype == TM_MON)
goto dd2;
if(ord)goto mm2; else goto dd2; /* "NN-" */
} /* "-NN-" */
if (!given(atm->tm_mday)
&& given(atm->tm_year)) /* If "YYYY-NN-" */
goto mm2; /* then always MM */
if(ord)goto dd2; else goto mm2;
}
if(btoken.tbrkl == ch /* "-NN" */
&& given(ord ? atm->tm_mon : atm->tm_mday))
if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
if(ord)goto dd2; else goto mm2;
}
/* Now reduced to choice between HH and DD */
if (given(atm->tm_hour)) goto dd2; /* Have hour? Assume day. */
if (given(atm->tm_mday)) goto hh2; /* Have day? Assume hour. */
if (given(atm->tm_mon)) goto dd2; /* Have month? Assume day. */
if(i > 24) goto dd2; /* Impossible HH means DD */
atoken = btoken;
if (!ptitoken(&atoken)) /* Read ahead! */
if(atoken.tval.tnum) return(0); /* ERR: bad token */
else goto dd2; /* EOF, assume day. */
if ( atoken.tflg
? !isdigit(*atoken.tcp)
: atoken.tval.ttmw->wflgs & TWTIME)
/* If next token is a time spec, assume hour */
goto hh2; /* e.g. "3 PM", "11-EDT" */
dd2: if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */
return(0);
continue;
mm2: if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */
return(0);
continue;
year4: if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */
return(0); /* ERR: year conflict */
continue;
/* Hack HH:MM[[:]SS] */
coltime:
if (ptstash(&atm->tm_hour, i)) return 0;
if (!ptitoken(&btoken))
return(!btoken.tval.tnum);
if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */
if(btoken.tcnt == 4) /* MMSS */
if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
|| ptstash(&atm->tm_sec, btoken.tval.tnum%100))
return(0);
else continue;
if(btoken.tcnt != 2
|| ptstash(&atm->tm_min, btoken.tval.tnum))
return(0); /* ERR: MM bad */
if (btoken.tbrk != ':') continue; /* Seconds follow? */
coltm2: if (!ptitoken(&btoken))
return(!btoken.tval.tnum);
if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */
|| ptstash(&atm->tm_sec, btoken.tval.tnum))
return(0); /* ERR: SS bad */
}
}
/* Store date/time value, return 0 if successful.
* Fail if entry is already set.
*/
static int
ptstash(adr,val)
int *adr;
int val;
{ register int *a;
if (given(*(a=adr)))
return 1;
*a = val;
return(0);
}
/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
* just prior to returning from partime.
*/
static int
pt12hack(tm, aval)
register struct tm *tm;
register int aval;
{ register int h = tm->tm_hour;
switch (aval) {
case T12_AM:
case T12_PM:
if (h > 12)
return 0;
if (h == 12)
tm->tm_hour = 0;
if (aval == T12_PM)
tm->tm_hour += 12;
break;
default:
if (0 < tm->tm_min || 0 < tm->tm_sec)
return 0;
if (!given(h) || h==12)
tm->tm_hour = aval;
else if (aval==T12_MIDNIGHT && (h==0 || h==24))
return 0;
}
return 1;
}
/* Get a token and identify it to some degree.
* Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
* hit error of some sort
*/
static int
ptitoken(tkp)
register struct token *tkp;
{
register char const *cp;
register int i, j, k;
if (!pttoken(tkp))
#ifdef DEBUG
{
VOID printf("EOF\n");
return(0);
}
#else
return(0);
#endif
cp = tkp->tcp;
#ifdef DEBUG
VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
#endif
if (tkp->tflg) {
i = tkp->tcnt;
if (*cp == '+' || *cp == '-') {
cp++;
i--;
}
while (0 <= --i) {
j = tkp->tval.tnum*10;
k = j + (*cp++ - '0');
if (j/10 != tkp->tval.tnum || k < j) {
/* arithmetic overflow */
tkp->tval.tnum = 1;
return 0;
}
tkp->tval.tnum = k;
}
} else if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tcnt, tmwords)))
{
#ifdef DEBUG
VOID printf("Not found!\n");
#endif
tkp->tval.tnum = 1;
return 0;
}
#ifdef DEBUG
if(tkp->tflg)
VOID printf("Val: %d.\n",tkp->tval.tnum);
else VOID printf("Found: \"%s\", val: %d, type %d\n",
tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
#endif
return(1);
}
/* Read token from input string into token structure */
static int
pttoken(tkp)
register struct token *tkp;
{
register char const *cp;
register int c;
char const *astr;
tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
tkp->tbrkl = tkp->tbrk; /* Set "last break" */
tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
tkp->tval.tnum = 0;
while(c = *cp++)
{ switch(c)
{ case ' ': case '\t': /* Flush all whitespace */
case '\r': case '\n':
case '\v': case '\f':
if (!tkp->tcnt) { /* If no token yet */
tkp->tcp = cp; /* ignore the brk */
continue; /* and go on. */
}
/* fall into */
case '(': case ')': /* Perhaps any non-alphanum */
case '-': case ',': /* shd qualify as break? */
case '+':
case '/': case ':': case '.': /* Break chars */
if(tkp->tcnt == 0) /* If no token yet */
{ tkp->tcp = cp; /* ignore the brk */
tkp->tbrkl = c;
continue; /* and go on. */
}
tkp->tbrk = c;
return(tkp->tcnt);
}
if (!tkp->tcnt++) { /* If first char of token, */
if (isdigit(c)) {
tkp->tflg = 1;
if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
/* timezone is break+sign+digit */
tkp->tcp--;
tkp->tcnt++;
}
}
} else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
tkp->tbrk = c;
return --tkp->tcnt; /* Wrong type, back up */
}
}
return(tkp->tcnt); /* When hit EOF */
}
static struct tmwent const *
ptmatchstr(astr,cnt,astruc)
char const *astr;
int cnt;
struct tmwent const *astruc;
{
register char const *cp, *mp;
register int c;
struct tmwent const *lastptr;
int i;
lastptr = 0;
for(;mp = astruc->went; astruc += 1)
{ cp = astr;
for(i = cnt; i > 0; i--)
{
switch (*cp++ - (c = *mp++))
{ case 0: continue; /* Exact match */
case 'A'-'a':
if (ctab[c] == Letter)
continue;
}
break;
}
if(i==0)
if (!*mp) return astruc; /* Exact match */
else if(lastptr) return(0); /* Ambiguous */
else lastptr = astruc; /* 1st ambig */
}
return lastptr;
}

View file

@ -0,0 +1,677 @@
/*
* RCS common definitions and data structures
*/
#define RCSBASE "$Id: rcsbase.h,v 5.11 1991/10/07 17:32:46 eggert Exp $"
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*****************************************************************************
* INSTRUCTIONS:
* =============
* See the Makefile for how to define C preprocessor symbols.
* If you need to change the comment leaders, update the table comtable[]
* in rcsfnms.c. (This can wait until you know what a comment leader is.)
*****************************************************************************
*/
/* $Log: rcsbase.h,v $
* Revision 5.11 1991/10/07 17:32:46 eggert
* Support piece tables even if !has_mmap.
*
* Revision 5.10 1991/09/24 00:28:39 eggert
* Remove unexported functions.
*
* Revision 5.9 1991/08/19 03:13:55 eggert
* Add piece tables and other tuneups, and NFS workarounds.
*
* Revision 5.8 1991/04/21 11:58:20 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.7 1991/02/28 19:18:50 eggert
* Try setuid() if seteuid() doesn't work.
*
* Revision 5.6 1991/02/26 17:48:37 eggert
* Support new link behavior. Move ANSI C / Posix declarations into conf.sh.
*
* Revision 5.5 1990/12/04 05:18:43 eggert
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/01 05:03:35 eggert
* Don't assume that builtins are functions; they may be macros.
* Permit arbitrary data in logs.
*
* Revision 5.3 1990/09/26 23:36:58 eggert
* Port wait() to non-Posix ANSI C hosts.
*
* Revision 5.2 1990/09/04 08:02:20 eggert
* Don't redefine NAME_MAX, PATH_MAX.
* Improve incomplete line handling. Standardize yes-or-no procedure.
*
* Revision 5.1 1990/08/29 07:13:53 eggert
* Add -kkvl. Fix type typos exposed by porting. Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:44 eggert
* Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access().
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate. Add support for ISO 8859.
* Remove snoop and v2 support.
*
* Revision 4.9 89/05/01 15:17:14 narten
* botched previous USG fix
*
* Revision 4.8 89/05/01 14:53:05 narten
* changed #include <strings.h> -> string.h for USG systems.
*
* Revision 4.7 88/11/08 15:58:45 narten
* removed defs for functions loaded from libraries
*
* Revision 4.6 88/08/09 19:12:36 eggert
* Shrink stdio code size; remove lint; permit -Dhshsize=nn.
*
* Revision 4.5 87/12/18 17:06:41 narten
* made removed BSD ifdef, now uses V4_2BSD
*
* Revision 4.4 87/10/18 10:29:49 narten
* Updating version numbers
* Changes relative to 1.1 are actually relative to 4.2
*
* Revision 1.3 87/09/24 14:02:25 narten
* changes for lint
*
* Revision 1.2 87/03/27 14:22:02 jenkins
* Port to suns
*
* Revision 4.2 83/12/20 16:04:20 wft
* merged 3.6.1.1 and 4.1 (SMALLOG, logsize).
* moved setting of STRICT_LOCKING to Makefile.
* changed DOLLAR to UNKN (conflict with KDELIM).
*
* Revision 4.1 83/05/04 09:12:41 wft
* Added markers Id and RCSfile.
* Added Dbranch for default branches.
*
* Revision 3.6.1.1 83/12/02 21:56:22 wft
* Increased logsize, added macro SMALLOG.
*
* Revision 3.6 83/01/15 16:43:28 wft
* 4.2 prerelease
*
* Revision 3.6 83/01/15 16:43:28 wft
* Replaced dbm.h with BYTESIZ, fixed definition of rindex().
* Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD.
* Added macro DELNUMFORM to have uniform format for printing delta text nodes.
* Added macro DELETE to mark deleted deltas.
*
* Revision 3.5 82/12/10 12:16:56 wft
* Added two forms of DATEFORM, one using %02d, the other %.2d.
*
* Revision 3.4 82/12/04 20:01:25 wft
* added LOCKER, Locker, and USG (redefinition of rindex).
*
* Revision 3.3 82/12/03 12:22:04 wft
* Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3,
* PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength
* using NCPPN. Changed putc() to abort on write error.
*
* Revision 3.2 82/10/18 15:03:52 wft
* added macro STRICT_LOCKING, removed RCSUMASK.
* renamed JOINFILE[1,2] to JOINFIL[1,2].
*
* Revision 3.1 82/10/11 19:41:17 wft
* removed NBPW, NBPC, NCPW.
* added typdef int void to aid compiling
*/
#include "conf.h"
#define EXIT_TROUBLE DIFF_TROUBLE
#ifdef PATH_MAX
# define SIZEABLE_PATH PATH_MAX /* size of a large path; not a hard limit */
#else
# define SIZEABLE_PATH _POSIX_PATH_MAX
#endif
/* for traditional C hosts with unusual size arguments */
#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f)
#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f)
/*
* Parameters
*/
/* backwards compatibility with old versions of RCS */
#define VERSION_min 3 /* old output RCS format supported */
#define VERSION_max 5 /* newest output RCS format supported */
#ifndef VERSION_DEFAULT /* default RCS output format */
# define VERSION_DEFAULT VERSION_max
#endif
#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */
#ifndef STRICT_LOCKING
#define STRICT_LOCKING 1
#endif
/* 0 sets the default locking to non-strict; */
/* used in experimental environments. */
/* 1 sets the default locking to strict; */
/* used in production environments. */
#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */
#define datesize (yearlength+16) /* size of output of DATEFORM */
#define joinlength 20 /* number of joined revisions permitted */
#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */
#define KDELIM '$' /* delimiter for keywords */
#define VDELIM ':' /* separates keywords from values */
#define DEFAULTSTATE "Exp" /* default state of revisions */
#define true 1
#define false 0
#define nil 0
/*
* RILE - readonly file
* declarecache; - declares local cache for RILE variable(s)
* setupcache - sets up the local RILE cache, but does not initialize it
* cache, uncache - caches and uncaches the local RILE;
* (uncache,cache) is needed around functions that advance the RILE pointer
* Igeteof(f,c,s) - get a char c from f, executing statement s at EOF
* cachegeteof(c,s) - Igeteof applied to the local RILE
* Iget(f,c) - like Igeteof, except EOF is an error
* cacheget(c) - Iget applied to the local RILE
* Ifileno, Irewind, Iseek, Itell - analogs to stdio routines
*/
#if large_memory
typedef unsigned char const *Iptr_type;
typedef struct {
Iptr_type ptr, lim;
unsigned char *base; /* for lint, not Iptr_type even if has_mmap */
# if has_mmap
# define Ifileno(f) ((f)->fd)
int fd;
# else
# define Ifileno(f) fileno((f)->stream)
FILE *stream;
unsigned char *readlim;
# endif
} RILE;
# if has_mmap
# define declarecache register Iptr_type ptr, lim
# define setupcache(f) (lim = (f)->lim)
# define Igeteof(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++
# define cachegeteof(c,s) if (ptr==lim) s else (c)= *ptr++
# else
# define declarecache register Iptr_type ptr; register RILE *rRILE
# define setupcache(f) (rRILE = (f))
# define Igeteof(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++
# define cachegeteof(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++
# endif
# define uncache(f) ((f)->ptr = ptr)
# define cache(f) (ptr = (f)->ptr)
# define Iget(f,c) Igeteof(f,c,Ieof();)
# define cacheget(c) cachegeteof(c,Ieof();)
# define Itell(f) ((f)->ptr)
# define Iseek(f,p) ((f)->ptr = (p))
# define Irewind(f) Iseek(f, (f)->base)
# define cachetell() ptr
#else
# define RILE FILE
# define declarecache register FILE *ptr
# define setupcache(f) (ptr = (f))
# define uncache(f)
# define cache(f)
# define Igeteof(f,c,s) if(((c)=getc(f))<0){testIerror(f);if(feof(f))s}else
# define cachegeteof(c,s) Igeteof(ptr,c,s)
# define Iget(f,c) if (((c)=getc(f))<0) testIeof(f); else
# define cacheget(c) Iget(ptr,c)
# define Ifileno(f) fileno(f)
#endif
/* Print a char, but abort on write error. */
#define aputc(c,o) if (putc(c,o)<0) testOerror(o); else
/* Get a character from an RCS file, perhaps copying to a new RCS file. */
#define GETCeof(o,c,s) { cachegeteof(c,s); if (o) aputc(c,o); }
#define GETC(o,c) { cacheget(c); if (o) aputc(c,o); }
#define WORKMODE(RCSmode, writable) ((RCSmode)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0))
/* computes mode of working file: same as RCSmode, but write permission */
/* determined by writable */
/* character classes and token codes */
enum tokens {
/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter,
PERIOD, SBEGIN, SPACE, UNKN,
/* tokens */ COLON, ID, NUM, SEMI, STRING
};
#define SDELIM '@' /* the actual character is needed for string handling*/
/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN.
* there should be no overlap among SDELIM, KDELIM, and VDELIM
*/
#define isdigit(c) ((unsigned)((c)-'0') <= 9) /* faster than ctab[c]==DIGIT */
/***************************************
* Data structures for the symbol table
***************************************/
/* Buffer of arbitrary data */
struct buf {
char *string;
size_t size;
};
struct cbuf {
char const *string;
size_t size;
};
/* Hash table entry */
struct hshentry {
char const * num; /* pointer to revision number (ASCIZ) */
char const * date; /* pointer to date of checkin */
char const * author; /* login of person checking in */
char const * lockedby; /* who locks the revision */
char const * state; /* state of revision (Exp by default) */
struct cbuf log; /* log message requested at checkin */
struct branchhead * branches; /* list of first revisions on branches*/
struct cbuf ig; /* ignored phrases of revision */
struct hshentry * next; /* next revision on same branch */
struct hshentry * nexthsh; /* next revision with same hash value */
unsigned long insertlns;/* lines inserted (computed by rlog) */
unsigned long deletelns;/* lines deleted (computed by rlog) */
char selector; /* true if selected, false if deleted */
};
/* list of hash entries */
struct hshentries {
struct hshentries *rest;
struct hshentry *first;
};
/* list element for branch lists */
struct branchhead {
struct hshentry * hsh;
struct branchhead * nextbranch;
};
/* accesslist element */
struct access {
char const * login;
struct access * nextaccess;
};
/* list element for locks */
struct lock {
char const * login;
struct hshentry * delta;
struct lock * nextlock;
};
/* list element for symbolic names */
struct assoc {
char const * symbol;
char const * num;
struct assoc * nextassoc;
};
#define mainArgs (argc,argv) int argc; char **argv;
#if lint
# define libId(name,rcsid)
# define mainProg(name,cmd,rcsid) int name mainArgs
#else
# define libId(name,rcsid) char const name[] = rcsid;
# define mainProg(name,cmd,rcsid) char const copyright[] = "Copyright 1982,1988,1989 by Walter F. Tichy\nPurdue CS\nCopyright 1990,1991 by Paul Eggert", rcsbaseId[] = RCSBASE, cmdid[] = cmd; libId(name,rcsid) int main mainArgs
#endif
/*
* Markers for keyword expansion (used in co and ident)
* Every byte must have class LETTER or Letter.
*/
#define AUTHOR "Author"
#define DATE "Date"
#define HEADER "Header"
#define IDH "Id"
#define LOCKER "Locker"
#define LOG "Log"
#define RCSFILE "RCSfile"
#define REVISION "Revision"
#define SOURCE "Source"
#define STATE "State"
#define keylength 8 /* max length of any of the above keywords */
enum markers { Nomatch, Author, Date, Header, Id,
Locker, Log, RCSfile, Revision, Source, State };
/* This must be in the same order as rcskeys.c's Keyword[] array. */
#define DELNUMFORM "\n\n%s\n%s\n"
/* used by putdtext and scanlogtext */
#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */
/* main program */
extern char const cmdid[];
exiting void exiterr P((void));
/* maketime */
int setfiledate P((char const*,char const[datesize]));
void str2date P((char const*,char[datesize]));
void time2date P((time_t,char[datesize]));
/* merge */
int merge P((int,char const*const[2],char const*const[3]));
/* partime */
int partime P((char const*,struct tm*,int*));
/* rcsedit */
#define ciklogsize 23 /* sizeof("checked in with -k by ") */
extern FILE *fcopy;
extern char const *resultfile;
extern char const ciklog[ciklogsize];
extern int locker_expansion;
extern struct buf dirtfname[];
#define newRCSfilename (dirtfname[0].string)
RILE *rcswriteopen P((struct buf*,struct stat*,int));
char const *makedirtemp P((char const*,int));
char const *getcaller P((void));
int addlock P((struct hshentry*));
int addsymbol P((char const*,char const*,int));
int checkaccesslist P((void));
int chnamemod P((FILE**,char const*,char const*,mode_t));
int donerewrite P((int));
int dorewrite P((int,int));
int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*));
int findlock P((int,struct hshentry**));
void aflush P((FILE*));
void copystring P((void));
void dirtempunlink P((void));
void enterstring P((void));
void finishedit P((struct hshentry const*,FILE*,int));
void keepdirtemp P((char const*));
void openfcopy P((FILE*));
void snapshotedit P((FILE*));
void xpandstring P((struct hshentry const*));
#if has_NFS || bad_unlink
int un_link P((char const*));
#else
# define un_link(s) unlink(s)
#endif
#if large_memory
void edit_string P((void));
# define editstring(delta) edit_string()
#else
void editstring P((struct hshentry const*));
#endif
/* rcsfcmp */
int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*));
/* rcsfnms */
#define bufautobegin(b) ((void) ((b)->string = 0, (b)->size = 0))
extern FILE *workstdout;
extern char *workfilename;
extern char const *RCSfilename;
extern char const *suffixes;
extern struct stat RCSstat;
RILE *rcsreadopen P((struct buf*,struct stat*,int));
char *bufenlarge P((struct buf*,char const**));
char const *basename P((char const*));
char const *getfullRCSname P((void));
char const *maketemp P((int));
char const *rcssuffix P((char const*));
int pairfilenames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int));
size_t dirlen P((char const*));
struct cbuf bufremember P((struct buf*,size_t));
void bufalloc P((struct buf*,size_t));
void bufautoend P((struct buf*));
void bufrealloc P((struct buf*,size_t));
void bufscat P((struct buf*,char const*));
void bufscpy P((struct buf*,char const*));
void tempunlink P((void));
/* rcsgen */
extern int interactiveflag;
extern struct buf curlogbuf;
char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int));
int getcstdin P((void));
int ttystdin P((void));
int yesorno P((int,char const*,...));
struct cbuf cleanlogmsg P((char*,size_t));
struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*));
void putdesc P((int,char*));
/* rcskeep */
extern int prevkeys;
extern struct buf prevauthor, prevdate, prevrev, prevstate;
int getoldkeys P((RILE*));
/* rcskeys */
extern char const *const Keyword[];
enum markers trymatch P((char const*));
/* rcslex */
extern FILE *foutptr;
extern FILE *frewrite;
extern RILE *finptr;
extern char const *NextString;
extern enum tokens nexttok;
extern int hshenter;
extern int nerror;
extern int nextc;
extern int quietflag;
extern unsigned long rcsline;
char const *getid P((void));
exiting void efaterror P((char const*));
exiting void enfaterror P((int,char const*));
exiting void faterror P((char const*,...));
exiting void fatserror P((char const*,...));
exiting void Ieof P((void));
exiting void Ierror P((void));
exiting void Oerror P((void));
char *checkid P((char*,int));
int eoflex P((void));
int getkeyopt P((char const*));
int getlex P((enum tokens));
struct cbuf getphrases P((char const*));
struct cbuf savestring P((struct buf*));
struct hshentry *getnum P((void));
void Ifclose P((RILE*));
void Izclose P((RILE**));
void Lexinit P((void));
void Ofclose P((FILE*));
void Ozclose P((FILE**));
void afputc P((int,FILE*));
void aprintf P((FILE*,char const*,...));
void aputs P((char const*,FILE*));
void checksid P((char*));
void diagnose P((char const*,...));
void eerror P((char const*));
void eflush P((void));
void enerror P((int,char const*));
void error P((char const*,...));
void fvfprintf P((FILE*,char const*,va_list));
void getkey P((char const*));
void getkeystring P((char const*));
void nextlex P((void));
void oflush P((void));
void printstring P((void));
void readstring P((void));
void redefined P((int));
void testIerror P((FILE*));
void testOerror P((FILE*));
void warn P((char const*,...));
void warnignore P((void));
#if has_madvise && has_mmap && large_memory
void advise_access P((RILE*,int));
# define if_advise_access(p,f,advice) if (p) advise_access(f,advice)
#else
# define advise_access(f,advice)
# define if_advise_access(p,f,advice)
#endif
#if has_mmap && large_memory
RILE *I_open P((char const*,struct stat*));
# define Iopen(f,m,s) I_open(f,s)
#else
RILE *Iopen P((char const*,char const*,struct stat*));
#endif
#if !large_memory
void testIeof P((FILE*));
void Irewind P((RILE*));
#endif
/* rcsmap */
extern const enum tokens ctab[];
/* rcsrev */
char *partialno P((struct buf*,char const*,unsigned));
char const *tiprev P((void));
int cmpnum P((char const*,char const*));
int cmpnumfld P((char const*,char const*,unsigned));
int compartial P((char const*,char const*,unsigned));
int expandsym P((char const*,struct buf*));
int fexpandsym P((char const*,struct buf*,RILE*));
struct hshentry *genrevs P((char const*,char const*,char const*,char const*,struct hshentries**));
unsigned countnumflds P((char const*));
void getbranchno P((char const*,struct buf*));
/* rcssyn */
/* These expand modes must agree with Expand_names[] in rcssyn.c. */
#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */
#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */
#define KEY_EXPAND 2 /* -kk `$Keyword$' */
#define VAL_EXPAND 3 /* -kv `value' */
#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */
struct diffcmd {
unsigned long
line1, /* number of first line */
nlines, /* number of lines affected */
adprev, /* previous 'a' line1+1 or 'd' line1 */
dafter; /* sum of previous 'd' line1 and previous 'd' nlines */
};
extern char const * Dbranch;
extern struct access * AccessList;
extern struct assoc * Symbols;
extern struct cbuf Comment;
extern struct lock * Locks;
extern struct hshentry * Head;
extern int Expand;
extern int StrictLocks;
extern unsigned TotalDeltas;
extern char const *const expand_names[];
extern char const Kdesc[];
extern char const Klog[];
extern char const Ktext[];
int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*));
int putdftext P((char const*,struct cbuf,RILE*,FILE*,int));
int putdtext P((char const*,struct cbuf,char const*,FILE*,int));
int str2expmode P((char const*));
void getadmin P((void));
void getdesc P((int));
void gettree P((void));
void ignorephrase P((void));
void initdiffcmd P((struct diffcmd*));
void putadmin P((FILE*));
void putstring P((FILE*,int,struct cbuf,int));
void puttree P((struct hshentry const*,FILE*));
/* rcsutil */
extern int RCSversion;
char *cgetenv P((char const*));
char *fstr_save P((char const*));
char *str_save P((char const*));
char const *date2str P((char const[datesize],char[datesize]));
char const *getusername P((int));
int getRCSINIT P((int,char**,char***));
int run P((char const*,char const*,...));
int runv P((char const**));
malloc_type fremember P((malloc_type));
malloc_type ftestalloc P((size_t));
malloc_type testalloc P((size_t));
malloc_type testrealloc P((malloc_type,size_t));
#define ftalloc(T) ftnalloc(T,1)
#define talloc(T) tnalloc(T,1)
#if lint
extern malloc_type lintalloc;
# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0)
# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0)
# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p)
# define tfree(p)
#else
# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n)))
# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n)))
# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n)))
# define tfree(p) free((malloc_type)(p))
#endif
void awrite P((char const*,size_t,FILE*));
void fastcopy P((RILE*,FILE*));
void ffree P((void));
void ffree1 P((char const*));
void setRCSversion P((char const*));
#if has_signal
void catchints P((void));
void ignoreints P((void));
void restoreints P((void));
#else
# define catchints()
# define ignoreints()
# define restoreints()
#endif
#if has_getuid
uid_t ruid P((void));
# define myself(u) ((u) == ruid())
#else
# define myself(u) true
#endif
#if has_setuid
uid_t euid P((void));
void nosetid P((void));
void seteid P((void));
void setrid P((void));
#else
# define nosetid()
# define seteid()
# define setrid()
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,321 @@
/*
* RCS file comparison
*/
/*****************************************************************************
* rcsfcmp()
* Testprogram: define FCMPTEST
*****************************************************************************
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsfcmp.c,v $
* Revision 5.9 1991/10/07 17:32:46 eggert
* Count log lines correctly.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.7 1991/04/21 11:58:22 eggert
* Fix errno bug. Add MS-DOS support.
*
* Revision 5.6 1991/02/28 19:18:47 eggert
* Open work file at most once.
*
* Revision 5.5 1990/11/27 09:26:05 eggert
* Fix comment leader bug.
*
* Revision 5.4 1990/11/01 05:03:42 eggert
* Permit arbitrary data in logs and comment leaders.
*
* Revision 5.3 1990/09/11 02:41:15 eggert
* Don't ignore differences inside keyword strings if -ko is set.
*
* Revision 5.1 1990/08/29 07:13:58 eggert
* Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:49 eggert
* Don't append "checked in with -k by " log to logs,
* so that checking in a program with -k doesn't change it.
* Ansify and Posixate. Remove lint.
*
* Revision 4.5 89/05/01 15:12:42 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 88/08/09 19:12:50 eggert
* Shrink stdio code size.
*
* Revision 4.3 87/12/18 11:40:02 narten
* lint cleanups (Guy Harris)
*
* Revision 4.2 87/10/18 10:33:06 narten
* updting version number. Changes relative to 1.1 actually relative to
* 4.1
*
* Revision 1.2 87/03/27 14:22:19 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:24:04 wft
* Marker matching now uses trymatch(). Marker pattern is now
* checked precisely.
*
* Revision 3.1 82/12/04 13:21:40 wft
* Initial revision.
*
*/
/*
#define FCMPTEST
*/
/* Testprogram; prints out whether two files are identical,
* except for keywords
*/
#include "rcsbase.h"
libId(fcmpId, "$Id: rcsfcmp.c,v 5.9 1991/10/07 17:32:46 eggert Exp $")
static int
discardkeyval(c, f)
register int c;
register RILE *f;
{
for (;;)
switch (c) {
case KDELIM:
case '\n':
return c;
default:
Igeteof(f, c, return EOF;);
break;
}
}
int
rcsfcmp(xfp, xstatp, ufname, delta)
register RILE *xfp;
struct stat const *xstatp;
char const *ufname;
struct hshentry const *delta;
/* Compare the files xfp and ufname. Return zero
* if xfp has the same contents as ufname and neither has keywords,
* otherwise -1 if they are the same ignoring keyword values,
* and 1 if they differ even ignoring
* keyword values. For the LOG-keyword, rcsfcmp skips the log message
* given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive
* if xfp contains the same as ufname, with the keywords expanded.
* Implementation: character-by-character comparison until $ is found.
* If a $ is found, read in the marker keywords; if they are real keywords
* and identical, read in keyword value. If value is terminated properly,
* disregard it and optionally skip log message; otherwise, compare value.
*/
{
register int xc, uc;
char xkeyword[keylength+2];
int eqkeyvals;
register RILE *ufp;
register int xeof, ueof;
register char * tp;
register char const *sp;
int result;
enum markers match1;
struct stat ustat;
if (!(ufp = Iopen(ufname, FOPEN_R_WORK, &ustat))) {
efaterror(ufname);
}
xeof = ueof = false;
if (Expand==OLD_EXPAND) {
if (!(result = xstatp->st_size!=ustat.st_size)) {
# if has_mmap && large_memory
result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
# else
for (;;) {
/* get the next characters */
Igeteof(xfp, xc, xeof=true;);
Igeteof(ufp, uc, ueof=true;);
if (xeof | ueof)
goto eof;
if (xc != uc)
goto return1;
}
# endif
}
} else {
xc = 0;
uc = 0; /* Keep lint happy. */
result = 0;
for (;;) {
if (xc != KDELIM) {
/* get the next characters */
Igeteof(xfp, xc, xeof=true;);
Igeteof(ufp, uc, ueof=true;);
if (xeof | ueof)
goto eof;
} else {
/* try to get both keywords */
tp = xkeyword;
for (;;) {
Igeteof(xfp, xc, xeof=true;);
Igeteof(ufp, uc, ueof=true;);
if (xeof | ueof)
goto eof;
if (xc != uc)
break;
switch (xc) {
default:
if (xkeyword+keylength <= tp)
break;
*tp++ = xc;
continue;
case '\n': case KDELIM: case VDELIM:
break;
}
break;
}
if (
(xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) &&
(*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch)
) {
#ifdef FCMPTEST
VOID printf("found common keyword %s\n",xkeyword);
#endif
result = -1;
for (;;) {
if (xc != uc) {
xc = discardkeyval(xc, xfp);
uc = discardkeyval(uc, ufp);
if ((xeof = xc==EOF) | (ueof = uc==EOF))
goto eof;
eqkeyvals = false;
break;
}
switch (xc) {
default:
Igeteof(xfp, xc, xeof=true;);
Igeteof(ufp, uc, ueof=true;);
if (xeof | ueof)
goto eof;
continue;
case '\n': case KDELIM:
eqkeyvals = true;
break;
}
break;
}
if (xc != uc)
goto return1;
if (xc==KDELIM) {
/* Skip closing KDELIM. */
Igeteof(xfp, xc, xeof=true;);
Igeteof(ufp, uc, ueof=true;);
if (xeof | ueof)
goto eof;
/* if the keyword is LOG, also skip the log message in xfp*/
if (match1==Log) {
/* first, compute the number of line feeds in log msg */
unsigned lncnt;
size_t ls, ccnt;
sp = delta->log.string;
ls = delta->log.size;
if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
/* This log message was inserted. */
lncnt = 3;
while (ls--) if (*sp++=='\n') lncnt++;
for (;;) {
if (xc=='\n')
if(--lncnt==0) break;
Igeteof(xfp, xc, goto returnresult;);
}
/* skip last comment leader */
/* Can't just skip another line here, because there may be */
/* additional characters on the line (after the Log....$) */
for (ccnt=Comment.size; ccnt--; ) {
Igeteof(xfp, xc, goto returnresult;);
if(xc=='\n') break;
/*
* Read to the end of the comment leader or '\n',
* whatever comes first. Some editors strip
* trailing white space from a leader like " * ".
*/
}
}
}
} else {
/* both end in the same character, but not a KDELIM */
/* must compare string values.*/
#ifdef FCMPTEST
VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
#endif
if (!eqkeyvals)
goto return1;
}
}
}
if (xc != uc)
goto return1;
}
}
eof:
if (xeof==ueof)
goto returnresult;
return1:
result = 1;
returnresult:
Ifclose(ufp);
return result;
}
#ifdef FCMPTEST
char const cmdid[] = "rcsfcmp";
main(argc, argv)
int argc; char *argv[];
/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
* 4th: unexpanded file
*/
{ struct hshentry delta;
Comment.string = argv[1];
Comment.size = strlen(argv[1]);
delta.log.string = argv[2];
delta.log.size = strlen(argv[2]);
if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
VOID printf("files are the same\n");
else VOID printf("files are different\n");
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,432 @@
/*
* RCS revision generation
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsgen.c,v $
* Revision 5.10 1991/10/07 17:32:46 eggert
* Fix log bugs, e.g. ci -t/dev/null when has_mmap.
*
* Revision 5.9 1991/09/10 22:15:46 eggert
* Fix test for redirected stdin.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Add piece tables. Tune.
*
* Revision 5.7 1991/04/21 11:58:24 eggert
* Add MS-DOS support.
*
* Revision 5.6 1990/12/27 19:54:26 eggert
* Fix bug: rcs -t inserted \n, making RCS file grow.
*
* Revision 5.5 1990/12/04 05:18:45 eggert
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/01 05:03:47 eggert
* Add -I and new -t behavior. Permit arbitrary data in logs.
*
* Revision 5.3 1990/09/21 06:12:43 hammer
* made putdesc() treat stdin the same whether or not it was from a terminal
* by making it recognize that a single '.' was then end of the
* description always
*
* Revision 5.2 1990/09/04 08:02:25 eggert
* Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure.
*
* Revision 5.1 1990/08/29 07:14:01 eggert
* Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:52 eggert
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate.
*
* Revision 4.7 89/05/01 15:12:49 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.6 88/08/28 14:59:10 eggert
* Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
*
* Revision 4.5 87/12/18 11:43:25 narten
* additional lint cleanups, and a bug fix from the 4.3BSD version that
* keeps "ci" from sticking a '\377' into the description if you run it
* with a zero-length file as the description. (Guy Harris)
*
* Revision 4.4 87/10/18 10:35:10 narten
* Updating version numbers. Changes relative to 1.1 actually relative to
* 4.2
*
* Revision 1.3 87/09/24 13:59:51 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:27 jenkins
* Port to suns
*
* Revision 4.2 83/12/02 23:01:39 wft
* merged 4.1 and 3.3.1.1 (clearerr(stdin)).
*
* Revision 4.1 83/05/10 16:03:33 wft
* Changed putamin() to abort if trying to reread redirected stdin.
* Fixed getdesc() to output a prompt on initial newline.
*
* Revision 3.3.1.1 83/10/19 04:21:51 lepreau
* Added clearerr(stdin) for re-reading description from stdin.
*
* Revision 3.3 82/11/28 21:36:49 wft
* 4.2 prerelease
*
* Revision 3.3 82/11/28 21:36:49 wft
* Replaced ferror() followed by fclose() with ffclose().
* Putdesc() now suppresses the prompts if stdin
* is not a terminal. A pointer to the current log message is now
* inserted into the corresponding delta, rather than leaving it in a
* global variable.
*
* Revision 3.2 82/10/18 21:11:26 wft
* I added checks for write errors during editing, and improved
* the prompt on putdesc().
*
* Revision 3.1 82/10/13 15:55:09 wft
* corrected type of variables assigned to by getc (char --> int)
*/
#include "rcsbase.h"
libId(genId, "$Id: rcsgen.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
int interactiveflag; /* Should we act as if stdin is a tty? */
struct buf curlogbuf; /* buffer for current log message */
enum stringwork { enter, copy, edit, expand, edit_expand };
static void scandeltatext P((struct hshentry*,enum stringwork,int));
char const *
buildrevision(deltas, target, outfile, expandflag)
struct hshentries const *deltas;
struct hshentry *target;
FILE *outfile;
int expandflag;
/* Function: Generates the revision given by target
* by retrieving all deltas given by parameter deltas and combining them.
* If outfile is set, the revision is output to it,
* otherwise written into a temporary file.
* Temporary files are allocated by maketemp().
* if expandflag is set, keyword expansion is performed.
* Return nil if outfile is set, the name of the temporary file otherwise.
*
* Algorithm: Copy initial revision unchanged. Then edit all revisions but
* the last one into it, alternating input and output files (resultfile and
* editfile). The last revision is then edited in, performing simultaneous
* keyword substitution (this saves one extra pass).
* All this simplifies if only one revision needs to be generated,
* or no keyword expansion is necessary, or if output goes to stdout.
*/
{
if (deltas->first == target) {
/* only latest revision to generate */
openfcopy(outfile);
scandeltatext(target, expandflag?expand:copy, true);
if (outfile)
return 0;
else {
Ozclose(&fcopy);
return(resultfile);
}
} else {
/* several revisions to generate */
/* Get initial revision without keyword expansion. */
scandeltatext(deltas->first, enter, false);
while ((deltas=deltas->rest)->rest) {
/* do all deltas except last one */
scandeltatext(deltas->first, edit, false);
}
if (expandflag || outfile) {
/* first, get to beginning of file*/
finishedit((struct hshentry *)nil, outfile, false);
}
scandeltatext(deltas->first, expandflag?edit_expand:edit, true);
finishedit(
expandflag ? deltas->first : (struct hshentry*)nil,
outfile, true
);
if (outfile)
return 0;
Ozclose(&fcopy);
return resultfile;
}
}
static void
scandeltatext(delta, func, needlog)
struct hshentry * delta;
enum stringwork func;
int needlog;
/* Function: Scans delta text nodes up to and including the one given
* by delta. For the one given by delta, the log message is saved into
* delta->log if needlog is set; func specifies how to handle the text.
* Assumes the initial lexeme must be read in first.
* Does not advance nexttok after it is finished.
*/
{
struct hshentry const *nextdelta;
struct cbuf cb;
for (;;) {
if (eoflex())
fatserror("can't find delta for revision %s", delta->num);
nextlex();
if (!(nextdelta=getnum())) {
fatserror("delta number corrupted");
}
getkeystring(Klog);
if (needlog && delta==nextdelta) {
cb = savestring(&curlogbuf);
delta->log = cleanlogmsg(curlogbuf.string, cb.size);
} else {readstring();
}
nextlex();
while (nexttok==ID && strcmp(NextString,Ktext)!=0)
ignorephrase();
getkeystring(Ktext);
if (delta==nextdelta)
break;
readstring(); /* skip over it */
}
switch (func) {
case enter: enterstring(); break;
case copy: copystring(); break;
case expand: xpandstring(delta); break;
case edit: editstring((struct hshentry *)nil); break;
case edit_expand: editstring(delta); break;
}
}
struct cbuf
cleanlogmsg(m, s)
char *m;
size_t s;
{
register char *t = m;
register char const *f = t;
struct cbuf r;
while (s) {
--s;
if ((*t++ = *f++) == '\n')
while (m < --t)
if (t[-1]!=' ' && t[-1]!='\t') {
*t++ = '\n';
break;
}
}
while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n'))
--t;
r.string = m;
r.size = t - m;
return r;
}
int ttystdin()
{
static int initialized;
if (!initialized) {
if (!interactiveflag)
interactiveflag = isatty(STDIN_FILENO);
initialized = true;
}
return interactiveflag;
}
int
getcstdin()
{
register FILE *in;
register int c;
in = stdin;
if (feof(in) && ttystdin())
clearerr(in);
c = getc(in);
if (c < 0) {
testIerror(in);
if (feof(in) && ttystdin())
afputc('\n',stderr);
}
return c;
}
#if has_prototypes
int
yesorno(int default_answer, char const *question, ...)
#else
/*VARARGS2*/ int
yesorno(default_answer, question, va_alist)
int default_answer; char const *question; va_dcl
#endif
{
va_list args;
register int c, r;
if (!quietflag && ttystdin()) {
oflush();
vararg_start(args, question);
fvfprintf(stderr, question, args);
va_end(args);
eflush();
r = c = getcstdin();
while (c!='\n' && !feof(stdin))
c = getcstdin();
if (r=='y' || r=='Y')
return true;
if (r=='n' || r=='N')
return false;
}
return default_answer;
}
void
putdesc(textflag, textfile)
int textflag;
char *textfile;
/* Function: puts the descriptive text into file frewrite.
* if finptr && !textflag, the text is copied from the old description.
* Otherwise, if the textfile!=nil, the text is read from that
* file, or from stdin, if textfile==nil.
* A textfile with a leading '-' is treated as a string, not a file name.
* If finptr, the old descriptive text is discarded.
* Always clears foutptr.
*/
{
static struct buf desc;
static struct cbuf desclean;
register FILE *txt;
register int c;
register FILE * frew;
register char *p;
register size_t s;
char const *plim;
frew = frewrite;
if (finptr && !textflag) {
/* copy old description */
aprintf(frew, "\n\n%s%c", Kdesc, nextc);
foutptr = frewrite;
getdesc(false);
foutptr = 0;
} else {
foutptr = 0;
/* get new description */
if (finptr) {
/*skip old description*/
getdesc(false);
}
aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
if (!textfile)
desclean = getsstdin(
"t-", "description",
"NOTE: This is NOT the log message!\n", &desc
);
else if (!desclean.string) {
if (*textfile == '-') {
p = textfile + 1;
s = strlen(p);
} else {
if (!(txt = fopen(textfile, "r")))
efaterror(textfile);
bufalloc(&desc, 1);
p = desc.string;
plim = p + desc.size;
for (;;) {
if ((c=getc(txt)) < 0) {
testIerror(txt);
if (feof(txt))
break;
}
if (plim <= p)
p = bufenlarge(&desc, &plim);
*p++ = c;
}
if (fclose(txt) != 0)
Ierror();
s = p - desc.string;
p = desc.string;
}
desclean = cleanlogmsg(p, s);
}
putstring(frew, false, desclean, true);
aputc('\n', frew);
}
}
struct cbuf
getsstdin(option, name, note, buf)
char const *option, *name, *note;
struct buf *buf;
{
register int c;
register char *p;
register size_t i;
register int tty = ttystdin();
if (tty)
aprintf(stderr,
"enter %s, terminated with single '.' or end of file:\n%s>> ",
name, note
);
else if (feof(stdin))
faterror("can't reread redirected stdin for %s; use -%s<%s>",
name, option, name
);
for (
i = 0, p = 0;
c = getcstdin(), !feof(stdin);
bufrealloc(buf, i+1), p = buf->string, p[i++] = c
)
if (c == '\n')
if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) {
/* Remove trailing '.'. */
--i;
break;
} else if (tty)
aputs(">> ", stderr);
return cleanlogmsg(p, i);
}

View file

@ -0,0 +1,422 @@
/*
* RCS keyword extraction
*/
/*****************************************************************************
* main routine: getoldkeys()
* Testprogram: define KEEPTEST
*****************************************************************************
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcskeep.c,v $
* Revision 5.4 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.3 1991/04/21 11:58:25 eggert
* Shorten names to keep them distinct on shortname hosts.
*
* Revision 5.2 1990/10/04 06:30:20 eggert
* Parse time zone offsets; future RCS versions may output them.
*
* Revision 5.1 1990/09/20 02:38:56 eggert
* ci -k now checks dates more thoroughly.
*
* Revision 5.0 1990/08/22 08:12:53 eggert
* Retrieve old log message if there is one.
* Don't require final newline.
* Remove compile-time limits; use malloc instead. Tune.
* Permit dates past 1999/12/31. Ansify and Posixate.
*
* Revision 4.6 89/05/01 15:12:56 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:13:03 eggert
* Remove lint and speed up by making FILE *fp local, not global.
*
* Revision 4.4 87/12/18 11:44:21 narten
* more lint cleanups (Guy Harris)
*
* Revision 4.3 87/10/18 10:35:50 narten
* Updating version numbers. Changes relative to 1.1 actually relative
* to 4.1
*
* Revision 1.3 87/09/24 14:00:00 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:29 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:26:44 wft
* Added new markers Id and RCSfile; extraction added.
* Marker matching with trymatch().
*
* Revision 3.2 82/12/24 12:08:26 wft
* added missing #endif.
*
* Revision 3.1 82/12/04 13:22:41 wft
* Initial revision.
*
*/
/*
#define KEEPTEST
*/
/* Testprogram; prints out the keyword values found. */
#include "rcsbase.h"
libId(keepId, "$Id: rcskeep.c,v 5.4 1991/08/19 03:13:55 eggert Exp $")
static int checknum P((char const*,int));
static int getval P((RILE*,struct buf*,int));
static int get0val P((int,RILE*,struct buf*,int));
static int keepdate P((RILE*));
static int keepid P((int,RILE*,struct buf*));
static int keeprev P((RILE*));
int prevkeys;
struct buf prevauthor, prevdate, prevrev, prevstate;
int
getoldkeys(fp)
register RILE *fp;
/* Function: Tries to read keyword values for author, date,
* revision number, and state out of the file fp.
* If FNAME is nonnull, it is opened and closed instead of using FP.
* The results are placed into
* prevauthor, prevdate, prevrev, prevstate.
* Aborts immediately if it finds an error and returns false.
* If it returns true, it doesn't mean that any of the
* values were found; instead, check to see whether the corresponding arrays
* contain the empty string.
*/
{
register int c;
char keyword[keylength+1];
register char * tp;
int needs_closing;
if (prevkeys)
return true;
needs_closing = false;
if (!fp) {
if (!(fp = Iopen(workfilename, FOPEN_R_WORK, (struct stat*)0))) {
eerror(workfilename);
return false;
}
needs_closing = true;
}
/* initialize to empty */
bufscpy(&prevauthor, "");
bufscpy(&prevdate, "");
bufscpy(&prevrev, "");
bufscpy(&prevstate, "");
c = '\0'; /* anything but KDELIM */
for (;;) {
if ( c==KDELIM) {
do {
/* try to get keyword */
tp = keyword;
for (;;) {
Igeteof(fp, c, goto ok;);
switch (c) {
default:
if (keyword+keylength <= tp)
break;
*tp++ = c;
continue;
case '\n': case KDELIM: case VDELIM:
break;
}
break;
}
} while (c==KDELIM);
if (c!=VDELIM) continue;
*tp = c;
Igeteof(fp, c, break;);
switch (c) {
case ' ': case '\t': break;
default: continue;
}
switch (trymatch(keyword)) {
case Author:
if (!keepid(0, fp, &prevauthor))
return false;
c = 0;
break;
case Date:
if (!(c = keepdate(fp)))
return false;
break;
case Header:
case Id:
if (!(
getval(fp, (struct buf*)nil, false) &&
keeprev(fp) &&
(c = keepdate(fp)) &&
keepid(c, fp, &prevauthor) &&
keepid(0, fp, &prevstate)
))
return false;
/* Skip either ``who'' (new form) or ``Locker: who'' (old). */
if (getval(fp, (struct buf*)nil, true) &&
getval(fp, (struct buf*)nil, true))
c = 0;
else if (nerror)
return false;
else
c = KDELIM;
break;
case Locker:
case Log:
case RCSfile:
case Source:
if (!getval(fp, (struct buf*)nil, false))
return false;
c = 0;
break;
case Revision:
if (!keeprev(fp))
return false;
c = 0;
break;
case State:
if (!keepid(0, fp, &prevstate))
return false;
c = 0;
break;
default:
continue;
}
if (!c)
Igeteof(fp, c, c=0;);
if (c != KDELIM) {
error("closing %c missing on keyword", KDELIM);
return false;
}
if (*prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string) {
break;
}
}
Igeteof(fp, c, break;);
}
ok:
if (needs_closing)
Ifclose(fp);
else
Irewind(fp);
prevkeys = true;
return true;
}
static int
badly_terminated()
{
error("badly terminated keyword value");
return false;
}
static int
getval(fp, target, optional)
register RILE *fp;
struct buf *target;
int optional;
/* Reads a keyword value from FP into TARGET.
* Returns true if one is found, false otherwise.
* Does not modify target if it is nil.
* Do not report an error if OPTIONAL is set and KDELIM is found instead.
*/
{
int c;
Igeteof(fp, c, return badly_terminated(););
return get0val(c, fp, target, optional);
}
static int
get0val(c, fp, target, optional)
register int c;
register RILE *fp;
struct buf *target;
int optional;
/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
* Same as getval, except C is the lookahead character.
*/
{ register char * tp;
char const *tlim;
register int got1;
if (target) {
bufalloc(target, 1);
tp = target->string;
tlim = tp + target->size;
} else
tlim = tp = 0;
got1 = false;
for (;;) {
switch (c) {
default:
got1 = true;
if (tp) {
*tp++ = c;
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
}
break;
case ' ':
case '\t':
if (tp) {
*tp = 0;
# ifdef KEEPTEST
VOID printf("getval: %s\n", target);
# endif
}
if (!got1)
error("too much white space in keyword value");
return got1;
case KDELIM:
if (!got1 && optional)
return false;
/* fall into */
case '\n':
case 0:
return badly_terminated();
}
Igeteof(fp, c, return badly_terminated(););
}
}
static int
keepdate(fp)
RILE *fp;
/* Function: reads a date prevdate; checks format
* Return 0 on error, lookahead character otherwise.
*/
{
struct buf prevday, prevtime, prevzone;
register char const *p;
register int c;
c = 0;
bufautobegin(&prevday);
if (getval(fp,&prevday,false)) {
bufautobegin(&prevtime);
if (getval(fp,&prevtime,false)) {
bufautobegin(&prevzone);
bufscpy(&prevzone, "");
Igeteof(fp, c, c=0;);
if (c=='-' || c=='+')
if (!get0val(c,fp,&prevzone,false))
c = 0;
else
Igeteof(fp, c, c=0;);
if (c) {
p = prevday.string;
bufalloc(&prevdate, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5);
VOID sprintf(prevdate.string, "%s%s %s %s",
/* Parse dates put out by old versions of RCS. */
isdigit(p[0]) && isdigit(p[1]) && p[2]=='/' ? "19" : "",
p, prevtime.string, prevzone.string
);
}
bufautoend(&prevzone);
}
bufautoend(&prevtime);
}
bufautoend(&prevday);
return c;
}
static int
keepid(c, fp, b)
int c;
RILE *fp;
struct buf *b;
/* Get previous identifier from C+FP into B. */
{
if (!c)
Igeteof(fp, c, return false;);
if (!get0val(c, fp, b, false))
return false;
checksid(b->string);
return true;
}
static int
keeprev(fp)
RILE *fp;
/* Get previous revision from FP into prevrev. */
{
return getval(fp,&prevrev,false) && checknum(prevrev.string,-1);
}
static int
checknum(sp,fields)
register char const *sp;
int fields;
{ register int dotcount;
dotcount=0;
while(*sp) {
if (*sp=='.') dotcount++;
else if (!isdigit(*sp)) return false;
sp++;
}
return fields<0 ? dotcount&1 : dotcount==fields;
}
#ifdef KEEPTEST
char const cmdid[] ="keeptest";
int
main(argc, argv)
int argc; char *argv[];
{
while (*(++argv)) {
workfilename = *argv;
getoldkeys((RILE*)0);
VOID printf("%s: revision: %s, date: %s, author: %s, state: %s\n",
*argv, prevrev.string, prevdate.string, prevauthor.string, prevstate.string);
}
exitmain(EXIT_SUCCESS);
}
#endif

View file

@ -0,0 +1,102 @@
/*
* RCS keyword table and match operation
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcskeys.c,v $
* Revision 5.2 1991/08/19 03:13:55 eggert
* Say `T const' instead of `const T'; it's less confusing for pointer types.
* (This change was made in other source files too.)
*
* Revision 5.1 1991/04/21 11:58:25 eggert
* Don't put , just before } in initializer.
*
* Revision 5.0 1990/08/22 08:12:54 eggert
* Add -k. Ansify and Posixate.
*
* Revision 4.3 89/05/01 15:13:02 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.2 87/10/18 10:36:33 narten
* Updating version numbers. Changes relative to 1.1 actuallyt
* relative to 4.1
*
* Revision 1.2 87/09/24 14:00:10 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 4.1 83/05/04 10:06:53 wft
* Initial revision.
*
*/
#include "rcsbase.h"
libId(keysId, "$Id: rcskeys.c,v 5.2 1991/08/19 03:13:55 eggert Exp $")
char const *const Keyword[] = {
/* This must be in the same order as rcsbase.h's enum markers type. */
nil,
AUTHOR, DATE, HEADER, IDH,
LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE
};
enum markers
trymatch(string)
char const *string;
/* function: Checks whether string starts with a keyword followed
* by a KDELIM or a VDELIM.
* If successful, returns the appropriate marker, otherwise Nomatch.
*/
{
register int j;
register char const *p, *s;
for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
/* try next keyword */
p = Keyword[j];
s = string;
while (*p++ == *s++) {
if (!*p)
switch (*s) {
case KDELIM:
case VDELIM:
return (enum markers)j;
default:
return Nomatch;
}
}
}
return(Nomatch);
}

1241
gnu/usr.bin/rcs/lib/rcslex.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
/* RCS map of character types */
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
libId(mapId, "$Id: rcsmap.c,v 5.2 1991/08/19 03:13:55 eggert Exp $")
/* map of character types */
/* ISO 8859/1 (Latin-1) */
enum tokens const ctab[] = {
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR,
DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter
};

View file

@ -0,0 +1,790 @@
/*
* RCS revision number handling
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsrev.c,v $
* Revision 5.3 1991/08/19 03:13:55 eggert
* Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune.
*
* Revision 5.2 1991/04/21 11:58:28 eggert
* Add tiprev().
*
* Revision 5.1 1991/02/25 07:12:43 eggert
* Avoid overflow when comparing revision numbers.
*
* Revision 5.0 1990/08/22 08:13:43 eggert
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate. Tune.
* Remove possibility of an internal error. Remove lint.
*
* Revision 4.5 89/05/01 15:13:22 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 87/12/18 11:45:22 narten
* more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
* since there's now a return value there with a value. (Guy Harris)
*
* Revision 4.3 87/10/18 10:38:42 narten
* Updating version numbers. Changes relative to version 1.1 actually
* relative to 4.1
*
* Revision 1.3 87/09/24 14:00:37 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:37 jenkins
* Port to suns
*
* Revision 4.1 83/03/25 21:10:45 wft
* Only changed $Header to $Id.
*
* Revision 3.4 82/12/04 13:24:08 wft
* Replaced getdelta() with gettree().
*
* Revision 3.3 82/11/28 21:33:15 wft
* fixed compartial() and compnum() for nil-parameters; fixed nils
* in error messages. Testprogram output shortenend.
*
* Revision 3.2 82/10/18 21:19:47 wft
* renamed compnum->cmpnum, compnumfld->cmpnumfld,
* numericrevno->numricrevno.
*
* Revision 3.1 82/10/11 19:46:09 wft
* changed expandsym() to check for source==nil; returns zero length string
* in that case.
*/
/*
#define REVTEST
*/
/* version REVTEST is for testing the routines that generate a sequence
* of delta numbers needed to regenerate a given delta.
*/
#include "rcsbase.h"
libId(revId, "$Id: rcsrev.c,v 5.3 1991/08/19 03:13:55 eggert Exp $")
static char const *branchtip P((char const*));
static struct hshentry *genbranch P((struct hshentry const*,char const*,unsigned,char const*,char const*,char const*,struct hshentries**));
unsigned
countnumflds(s)
char const *s;
/* Given a pointer s to a dotted number (date or revision number),
* countnumflds returns the number of digitfields in s.
*/
{
register char const *sp;
register unsigned count;
if ((sp=s)==nil) return(0);
if (*sp == '\0') return(0);
count = 1;
do {
if (*sp++ == '.') count++;
} while (*sp);
return(count);
}
void
getbranchno(revno,branchno)
char const *revno;
struct buf *branchno;
/* Given a non-nil revision number revno, getbranchno copies the number of the branch
* on which revno is into branchno. If revno itself is a branch number,
* it is copied unchanged.
*/
{
register unsigned numflds;
register char *tp;
bufscpy(branchno, revno);
numflds=countnumflds(revno);
if (!(numflds & 1)) {
tp = branchno->string;
while (--numflds)
while (*tp++ != '.')
;
*(tp-1)='\0';
}
}
int cmpnum(num1, num2)
char const *num1, *num2;
/* compares the two dotted numbers num1 and num2 lexicographically
* by field. Individual fields are compared numerically.
* returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
* omitted fields are assumed to be higher than the existing ones.
*/
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1=num1==nil?"":num1;
s2=num2==nil?"":num2;
for (;;) {
/* Give precedence to shorter one. */
if (!*s1)
return (unsigned char)*s2;
if (!*s2)
return -1;
/* Strip leading zeros, then find number of digits. */
while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ;
while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ;
/* Do not convert to integer; it might overflow! */
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
s1 += d1;
s2 += d1;
/* skip '.' */
if (*s1) s1++;
if (*s2) s2++;
}
}
int cmpnumfld(num1, num2, fld)
char const *num1, *num2;
unsigned fld;
/* Compare the two dotted numbers at field fld.
* num1 and num2 must have at least fld fields.
* fld must be positive.
*/
{
register char const *s1, *s2;
register size_t d1, d2;
s1 = num1;
s2 = num2;
/* skip fld-1 fields */
while (--fld) {
while (*s1++ != '.')
;
while (*s2++ != '.')
;
}
/* Now s1 and s2 point to the beginning of the respective fields */
while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ;
while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ;
return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1;
}
static void
cantfindbranch(revno, date, author, state)
char const *revno, date[datesize], *author, *state;
{
char datebuf[datesize];
error("No revision on branch %s has%s%s%s%s%s%s.",
revno,
date ? " a date before " : "",
date ? date2str(date,datebuf) : "",
author ? " and author "+(date?0:4) : "",
author ? author : "",
state ? " and state "+(date||author?0:4) : "",
state ? state : ""
);
}
static void
absent(revno, field)
char const *revno;
unsigned field;
{
struct buf t;
bufautobegin(&t);
error("%s %s absent", field&1?"revision":"branch",
partialno(&t,revno,field)
);
bufautoend(&t);
}
int
compartial(num1, num2, length)
char const *num1, *num2;
unsigned length;
/* compare the first "length" fields of two dot numbers;
the omitted field is considered to be larger than any number */
/* restriction: at least one number has length or more fields */
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1 = num1; s2 = num2;
if (!s1) return 1;
if (!s2) return -1;
for (;;) {
if (!*s1) return 1;
if (!*s2) return -1;
while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ;
while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ;
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
s1 += d1;
s2 += d1;
if (*s1 == '.') s1++;
if (*s2 == '.') s2++;
if ( --length == 0 ) return 0;
}
}
char * partialno(rev1,rev2,length)
struct buf *rev1;
char const *rev2;
register unsigned length;
/* Function: Copies length fields of revision number rev2 into rev1.
* Return rev1's string.
*/
{
register char *r1;
bufscpy(rev1, rev2);
r1 = rev1->string;
while (length) {
while (*r1!='.' && *r1)
++r1;
++r1;
length--;
}
/* eliminate last '.'*/
*(r1-1)='\0';
return rev1->string;
}
static void
store1(store, next)
struct hshentries ***store;
struct hshentry *next;
/*
* Allocate a new list node that addresses NEXT.
* Append it to the list that **STORE is the end pointer of.
*/
{
register struct hshentries *p;
p = ftalloc(struct hshentries);
p->first = next;
**store = p;
*store = &p->rest;
}
struct hshentry * genrevs(revno,date,author,state,store)
char const *revno, *date, *author, *state;
struct hshentries **store;
/* Function: finds the deltas needed for reconstructing the
* revision given by revno, date, author, and state, and stores pointers
* to these deltas into a list whose starting address is given by store.
* The last delta (target delta) is returned.
* If the proper delta could not be found, nil is returned.
*/
{
unsigned length;
register struct hshentry * next;
int result;
char const *branchnum;
struct buf t;
char datebuf[datesize];
bufautobegin(&t);
if (!(next = Head)) {
error("RCS file empty");
goto norev;
}
length = countnumflds(revno);
if (length >= 1) {
/* at least one field; find branch exactly */
while ((result=cmpnumfld(revno,next->num,1)) < 0) {
store1(&store, next);
next = next->next;
if (!next) {
error("branch number %s too low", partialno(&t,revno,1));
goto norev;
}
}
if (result>0) {
absent(revno, 1);
goto norev;
}
}
if (length<=1){
/* pick latest one on given branch */
branchnum = next->num; /* works even for empty revno*/
while ((next!=nil) &&
(cmpnumfld(branchnum,next->num,1)==0) &&
!(
(date==nil?1:(cmpnum(date,next->date)>=0)) &&
(author==nil?1:(strcmp(author,next->author)==0)) &&
(state ==nil?1:(strcmp(state, next->state) ==0))
)
)
{
store1(&store, next);
next=next->next;
}
if ((next==nil) ||
(cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
cantfindbranch(
length ? revno : partialno(&t,branchnum,1),
date, author, state
);
goto norev;
} else {
store1(&store, next);
}
*store = nil;
return next;
}
/* length >=2 */
/* find revision; may go low if length==2*/
while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
(cmpnumfld(revno,next->num,1)==0) ) {
store1(&store, next);
next = next->next;
if (!next)
break;
}
if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
error("revision number %s too low", partialno(&t,revno,2));
goto norev;
}
if ((length>2) && (result!=0)) {
absent(revno, 2);
goto norev;
}
/* print last one */
store1(&store, next);
if (length>2)
return genbranch(next,revno,length,date,author,state,store);
else { /* length == 2*/
if ((date!=nil) && (cmpnum(date,next->date)<0)){
error("Revision %s has date %s.",
next->num,
date2str(next->date, datebuf)
);
return nil;
}
if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
error("Revision %s has author %s.",next->num,next->author);
return nil;
}
if ((state!=nil)&&(strcmp(state,next->state)!=0)) {
error("Revision %s has state %s.",next->num,
next->state==nil?"<empty>":next->state);
return nil;
}
*store=nil;
return next;
}
norev:
bufautoend(&t);
return nil;
}
static struct hshentry *
genbranch(bpoint, revno, length, date, author, state, store)
struct hshentry const *bpoint;
char const *revno;
unsigned length;
char const *date, *author, *state;
struct hshentries **store;
/* Function: given a branchpoint, a revision number, date, author, and state,
* genbranch finds the deltas necessary to reconstruct the given revision
* from the branch point on.
* Pointers to the found deltas are stored in a list beginning with store.
* revno must be on a side branch.
* return nil on error
*/
{
unsigned field;
register struct hshentry * next, * trail;
register struct branchhead const *bhead;
int result;
struct buf t;
char datebuf[datesize];
field = 3;
bhead = bpoint->branches;
do {
if (!bhead) {
bufautobegin(&t);
error("no side branches present for %s", partialno(&t,revno,field-1));
bufautoend(&t);
return nil;
}
/*find branch head*/
/*branches are arranged in increasing order*/
while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
bhead = bhead->nextbranch;
if (!bhead) {
bufautobegin(&t);
error("branch number %s too high",partialno(&t,revno,field));
bufautoend(&t);
return nil;
}
}
if (result<0) {
absent(revno, field);
return nil;
}
next = bhead->hsh;
if (length==field) {
/* pick latest one on that branch */
trail=nil;
do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) &&
(author==nil?1:(strcmp(author,next->author)==0)) &&
(state ==nil?1:(strcmp(state, next->state) ==0))
) trail = next;
next=next->next;
} while (next!=nil);
if (trail==nil) {
cantfindbranch(revno, date, author, state);
return nil;
} else { /* print up to last one suitable */
next = bhead->hsh;
while (next!=trail) {
store1(&store, next);
next=next->next;
}
store1(&store, next);
}
*store = nil;
return next;
}
/* length > field */
/* find revision */
/* check low */
if (cmpnumfld(revno,next->num,field+1)<0) {
bufautobegin(&t);
error("revision number %s too low", partialno(&t,revno,field+1));
bufautoend(&t);
return(nil);
}
do {
store1(&store, next);
trail = next;
next = next->next;
} while ((next!=nil) &&
(cmpnumfld(revno,next->num,field+1) >=0));
if ((length>field+1) && /*need exact hit */
(cmpnumfld(revno,trail->num,field+1) !=0)){
absent(revno, field+1);
return(nil);
}
if (length == field+1) {
if ((date!=nil) && (cmpnum(date,trail->date)<0)){
error("Revision %s has date %s.",
trail->num,
date2str(trail->date, datebuf)
);
return nil;
}
if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
error("Revision %s has author %s.",trail->num,trail->author);
return nil;
}
if ((state!=nil)&&(strcmp(state,trail->state)!=0)) {
error("Revision %s has state %s.",trail->num,
trail->state==nil?"<empty>":trail->state);
return nil;
}
}
bhead = trail->branches;
} while ((field+=2) <= length);
* store = nil;
return trail;
}
static char const *
lookupsym(id)
char const *id;
/* Function: looks up id in the list of symbolic names starting
* with pointer SYMBOLS, and returns a pointer to the corresponding
* revision number. Returns nil if not present.
*/
{
register struct assoc const *next;
next = Symbols;
while (next!=nil) {
if (strcmp(id, next->symbol)==0)
return next->num;
else next=next->nextassoc;
}
return nil;
}
int expandsym(source, target)
char const *source;
struct buf *target;
/* Function: Source points to a revision number. Expandsym copies
* the number to target, but replaces all symbolic fields in the
* source number with their numeric values.
* Expand a branch followed by `.' to the latest revision on that branch.
* Ignore `.' after a revision. Remove leading zeros.
* returns false on error;
*/
{
return fexpandsym(source, target, (RILE*)0);
}
int
fexpandsym(source, target, fp)
char const *source;
struct buf *target;
RILE *fp;
/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */
{
register char const *sp, *bp;
register char *tp;
char const *tlim;
register enum tokens d;
unsigned dots;
sp = source;
bufalloc(target, 1);
tp = target->string;
if (!sp || !*sp) { /*accept nil pointer as a legal value*/
*tp='\0';
return true;
}
if (sp[0] == KDELIM && !sp[1]) {
if (!getoldkeys(fp))
return false;
if (!*prevrev.string) {
error("working file lacks revision number");
return false;
}
bufscpy(target, prevrev.string);
return true;
}
tlim = tp + target->size;
dots = 0;
for (;;) {
switch (ctab[(unsigned char)*sp]) {
case DIGIT:
while (*sp=='0' && isdigit(sp[1]))
/* skip leading zeroes */
sp++;
do {
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
} while (isdigit(*tp++ = *sp++));
--sp;
tp[-1] = '\0';
break;
case LETTER:
case Letter:
{
register char *p = tp;
register size_t s = tp - target->string;
do {
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p++ = *sp++;
} while ((d=ctab[(unsigned char)*sp])==LETTER ||
d==Letter || d==DIGIT ||
(d==IDCHAR));
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p = 0;
tp = target->string + s;
}
bp = lookupsym(tp);
if (bp==nil) {
error("Symbolic number %s is undefined.", tp);
return false;
}
do {
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
} while ((*tp++ = *bp++));
break;
default:
goto improper;
}
switch (*sp++) {
case '\0': return true;
case '.': break;
default: goto improper;
}
if (!*sp) {
if (dots & 1)
goto improper;
if (!(bp = branchtip(target->string)))
return false;
bufscpy(target, bp);
return true;
}
++dots;
tp[-1] = '.';
}
improper:
error("improper revision number: %s", source);
return false;
}
static char const *
branchtip(branch)
char const *branch;
{
struct hshentry *h;
struct hshentries *hs;
h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs);
return h ? h->num : (char const*)0;
}
char const *
tiprev()
{
return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0;
}
#ifdef REVTEST
char const cmdid[] = "revtest";
int
main(argc,argv)
int argc; char * argv[];
{
static struct buf numricrevno;
char symrevno[100]; /* used for input of revision numbers */
char author[20];
char state[20];
char date[20];
struct hshentries *gendeltas;
struct hshentry * target;
int i;
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s", argv[1]);
}
Lexinit();
getadmin();
gettree();
getdesc(false);
do {
/* all output goes to stderr, to have diagnostics and */
/* errors in sequence. */
aputs("\nEnter revision number or <return> or '.': ",stderr);
if (!gets(symrevno)) break;
if (*symrevno == '.') break;
aprintf(stderr,"%s;\n",symrevno);
expandsym(symrevno,&numricrevno);
aprintf(stderr,"expanded number: %s; ",numricrevno.string);
aprintf(stderr,"Date: ");
gets(date); aprintf(stderr,"%s; ",date);
aprintf(stderr,"Author: ");
gets(author); aprintf(stderr,"%s; ",author);
aprintf(stderr,"State: ");
gets(state); aprintf(stderr, "%s;\n", state);
target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil,
*state?state:(char*)nil, &gendeltas);
if (target!=nil) {
while (gendeltas) {
aprintf(stderr,"%s\n",gendeltas->first->num);
gendeltas = gendeltas->next;
}
}
} while (true);
aprintf(stderr,"done\n");
exitmain(EXIT_SUCCESS);
}
exiting void exiterr() { _exit(EXIT_FAILURE); }
#endif

View file

@ -0,0 +1,857 @@
/*
* RCS file input
*/
/*********************************************************************************
* Syntax Analysis.
* Keyword table
* Testprogram: define SYNTEST
* Compatibility with Release 2: define COMPAT2=1
*********************************************************************************
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcssyn.c,v $
* Revision 5.8 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.7 1991/04/21 11:58:29 eggert
* Disambiguate names on shortname hosts.
* Fix errno bug. Add MS-DOS support.
*
* Revision 5.6 1991/02/28 19:18:51 eggert
* Fix null termination bug in reporting keyword expansion.
*
* Revision 5.5 1991/02/25 07:12:44 eggert
* Check diff output more carefully; avoid overflow.
*
* Revision 5.4 1990/11/01 05:28:48 eggert
* When ignoring unknown phrases, copy them to the output RCS file.
* Permit arbitrary data in logs and comment leaders.
* Don't check for nontext on initial checkin.
*
* Revision 5.3 1990/09/20 07:58:32 eggert
* Remove the test for non-text bytes; it caused more pain than it cured.
*
* Revision 5.2 1990/09/04 08:02:30 eggert
* Parse RCS files with no revisions.
* Don't strip leading white space from diff commands. Count RCS lines better.
*
* Revision 5.1 1990/08/29 07:14:06 eggert
* Add -kkvl. Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:13:44 eggert
* Try to parse future RCS formats without barfing.
* Add -k. Don't require final newline.
* Remove compile-time limits; use malloc instead.
* Don't output branch keyword if there's no default branch,
* because RCS version 3 doesn't understand it.
* Tune. Remove lint.
* Add support for ISO 8859. Ansify and Posixate.
* Check that a newly checked-in file is acceptable as input to 'diff'.
* Check diff's output.
*
* Revision 4.6 89/05/01 15:13:32 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:13:21 eggert
* Allow cc -R; remove lint.
*
* Revision 4.4 87/12/18 11:46:16 narten
* more lint cleanups (Guy Harris)
*
* Revision 4.3 87/10/18 10:39:36 narten
* Updating version numbers. Changes relative to 1.1 actually relative to
* 4.1
*
* Revision 1.3 87/09/24 14:00:49 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:40 jenkins
* Port to suns
*
* Revision 4.1 83/03/28 11:38:49 wft
* Added parsing and printing of default branch.
*
* Revision 3.6 83/01/15 17:46:50 wft
* Changed readdelta() to initialize selector and log-pointer.
* Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
*
* Revision 3.5 82/12/08 21:58:58 wft
* renamed Commentleader to Commleader.
*
* Revision 3.4 82/12/04 13:24:40 wft
* Added routine gettree(), which updates keeplock after reading the
* delta tree.
*
* Revision 3.3 82/11/28 21:30:11 wft
* Reading and printing of Suffix removed; version COMPAT2 skips the
* Suffix for files of release 2 format. Fixed problems with printing nil.
*
* Revision 3.2 82/10/18 21:18:25 wft
* renamed putdeltatext to putdtext.
*
* Revision 3.1 82/10/11 19:45:11 wft
* made sure getc() returns into an integer.
*/
/* version COMPAT2 reads files of the format of release 2 and 3, but
* generates files of release 3 format. Need not be defined if no
* old RCS files generated with release 2 exist.
*/
/* version SYNTEST inputs a RCS file and then prints out its internal
* data structures.
*/
#include "rcsbase.h"
libId(synId, "$Id: rcssyn.c,v 5.8 1991/08/19 03:13:55 eggert Exp $")
/* forward */
static char const *getkeyval P((char const*,enum tokens,int));
static int strn2expmode P((char const*,size_t));
/* keyword table */
char const
Kdesc[] = "desc",
Klog[] = "log",
Ktext[] = "text";
static char const
Kaccess[] = "access",
Kauthor[] = "author",
Kbranch[] = "branch",
K_branches[]= "branches",
Kcomment[] = "comment",
Kdate[] = "date",
Kexpand[] = "expand",
Khead[] = "head",
Klocks[] = "locks",
Knext[] = "next",
Kstate[] = "state",
Kstrict[] = "strict",
#if COMPAT2
Ksuffix[] = "suffix",
#endif
Ksymbols[] = "symbols";
static struct buf Commleader;
static struct cbuf Ignored;
struct cbuf Comment;
struct access * AccessList;
struct assoc * Symbols;
struct lock * Locks;
int Expand;
int StrictLocks;
struct hshentry * Head;
char const * Dbranch;
unsigned TotalDeltas;
static void
getsemi(key)
char const *key;
/* Get a semicolon to finish off a phrase started by KEY. */
{
if (!getlex(SEMI))
fatserror("missing ';' after '%s'", key);
}
static struct hshentry *
getdnum()
/* Get a delta number. */
{
register struct hshentry *delta = getnum();
if (delta && countnumflds(delta->num)&1)
fatserror("%s isn't a delta number", delta->num);
return delta;
}
void
getadmin()
/* Read an <admin> and initialize the appropriate global variables. */
{
register char const *id;
struct access * newaccess;
struct assoc * newassoc;
struct lock * newlock;
struct hshentry * delta;
struct access **LastAccess;
struct assoc **LastSymbol;
struct lock **LastLock;
struct buf b;
struct cbuf cb;
TotalDeltas=0;
getkey(Khead);
Head = getdnum();
getsemi(Khead);
Dbranch = nil;
if (getkeyopt(Kbranch)) {
if ((delta = getnum()))
Dbranch = delta->num;
getsemi(Kbranch);
}
#if COMPAT2
/* read suffix. Only in release 2 format */
if (getkeyopt(Ksuffix)) {
if (nexttok==STRING) {
readstring(); nextlex(); /* Throw away the suffix. */
} else if (nexttok==ID) {
nextlex();
}
getsemi(Ksuffix);
}
#endif
getkey(Kaccess);
LastAccess = &AccessList;
while (id=getid()) {
newaccess = ftalloc(struct access);
newaccess->login = id;
*LastAccess = newaccess;
LastAccess = &newaccess->nextaccess;
}
*LastAccess = nil;
getsemi(Kaccess);
getkey(Ksymbols);
LastSymbol = &Symbols;
while (id = getid()) {
if (!getlex(COLON))
fatserror("missing ':' in symbolic name definition");
if (!(delta=getnum())) {
fatserror("missing number in symbolic name definition");
} else { /*add new pair to association list*/
newassoc = ftalloc(struct assoc);
newassoc->symbol=id;
newassoc->num = delta->num;
*LastSymbol = newassoc;
LastSymbol = &newassoc->nextassoc;
}
}
*LastSymbol = nil;
getsemi(Ksymbols);
getkey(Klocks);
LastLock = &Locks;
while (id = getid()) {
if (!getlex(COLON))
fatserror("missing ':' in lock");
if (!(delta=getdnum())) {
fatserror("missing number in lock");
} else { /*add new pair to lock list*/
newlock = ftalloc(struct lock);
newlock->login=id;
newlock->delta=delta;
*LastLock = newlock;
LastLock = &newlock->nextlock;
}
}
*LastLock = nil;
getsemi(Klocks);
if ((StrictLocks = getkeyopt(Kstrict)))
getsemi(Kstrict);
Comment.size = 0;
if (getkeyopt(Kcomment)) {
if (nexttok==STRING) {
Comment = savestring(&Commleader);
nextlex();
}
getsemi(Kcomment);
}
Expand = KEYVAL_EXPAND;
if (getkeyopt(Kexpand)) {
if (nexttok==STRING) {
bufautobegin(&b);
cb = savestring(&b);
if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
fatserror("unknown expand mode %.*s",
(int)cb.size, cb.string
);
bufautoend(&b);
nextlex();
}
getsemi(Kexpand);
}
Ignored = getphrases(Kdesc);
}
char const *const expand_names[] = {
/* These must agree with *_EXPAND in rcsbase.h. */
"kv","kvl","k","v","o",
0
};
int
str2expmode(s)
char const *s;
/* Yield expand mode corresponding to S, or -1 if bad. */
{
return strn2expmode(s, strlen(s));
}
static int
strn2expmode(s, n)
char const *s;
size_t n;
{
char const *const *p;
for (p = expand_names; *p; ++p)
if (memcmp(*p,s,n) == 0 && !(*p)[n])
return p - expand_names;
return -1;
}
void
ignorephrase()
/* Ignore a phrase introduced by a later version of RCS. */
{
warnignore();
hshenter=false;
for (;;) {
switch (nexttok) {
case SEMI: hshenter=true; nextlex(); return;
case ID:
case NUM: ffree1(NextString); break;
case STRING: readstring(); break;
default: break;
}
nextlex();
}
}
static int
getdelta()
/* Function: reads a delta block.
* returns false if the current block does not start with a number.
*/
{
register struct hshentry * Delta, * num;
struct branchhead **LastBranch, *NewBranch;
if (!(Delta = getdnum()))
return false;
hshenter = false; /*Don't enter dates into hashtable*/
Delta->date = getkeyval(Kdate, NUM, false);
hshenter=true; /*reset hshenter for revision numbers.*/
Delta->author = getkeyval(Kauthor, ID, false);
Delta->state = getkeyval(Kstate, ID, true);
getkey(K_branches);
LastBranch = &Delta->branches;
while ((num = getdnum())) {
NewBranch = ftalloc(struct branchhead);
NewBranch->hsh = num;
*LastBranch = NewBranch;
LastBranch = &NewBranch->nextbranch;
}
*LastBranch = nil;
getsemi(K_branches);
getkey(Knext);
Delta->next = num = getdnum();
getsemi(Knext);
Delta->lockedby = nil;
Delta->log.string = 0;
Delta->selector = true;
Delta->ig = getphrases(Kdesc);
TotalDeltas++;
return (true);
}
void
gettree()
/* Function: Reads in the delta tree with getdelta(), then
* updates the lockedby fields.
*/
{
struct lock const *currlock;
while (getdelta());
currlock=Locks;
while (currlock) {
currlock->delta->lockedby = currlock->login;
currlock = currlock->nextlock;
}
}
void
getdesc(prdesc)
int prdesc;
/* Function: read in descriptive text
* nexttok is not advanced afterwards.
* If prdesc is set, the text is printed to stdout.
*/
{
getkeystring(Kdesc);
if (prdesc)
printstring(); /*echo string*/
else readstring(); /*skip string*/
}
static char const *
getkeyval(keyword, token, optional)
char const *keyword;
enum tokens token;
int optional;
/* reads a pair of the form
* <keyword> <token> ;
* where token is one of <id> or <num>. optional indicates whether
* <token> is optional. A pointer to
* the actual character string of <id> or <num> is returned.
*/
{
register char const *val = nil;
getkey(keyword);
if (nexttok==token) {
val = NextString;
nextlex();
} else {
if (!optional)
fatserror("missing %s", keyword);
}
getsemi(keyword);
return(val);
}
void
putadmin(fout)
register FILE * fout;
/* Function: Print the <admin> node read with getadmin() to file fout.
* Assumption: Variables AccessList, Symbols, Locks, StrictLocks,
* and Head have been set.
*/
{
struct assoc const *curassoc;
struct lock const *curlock;
struct access const *curaccess;
aprintf(fout, "%s\t%s;\n", Khead, Head?Head->num:"");
if (Dbranch && VERSION(4)<=RCSversion)
aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
aputs(Kaccess, fout);
curaccess = AccessList;
while (curaccess) {
aprintf(fout, "\n\t%s", curaccess->login);
curaccess = curaccess->nextaccess;
}
aprintf(fout, ";\n%s", Ksymbols);
curassoc = Symbols;
while (curassoc) {
aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
curassoc = curassoc->nextassoc;
}
aprintf(fout, ";\n%s", Klocks);
curlock = Locks;
while (curlock) {
aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
curlock = curlock->nextlock;
}
if (StrictLocks) aprintf(fout, "; %s", Kstrict);
aprintf(fout, ";\n");
if (Comment.size) {
aprintf(fout, "%s\t", Kcomment);
putstring(fout, true, Comment, false);
aprintf(fout, ";\n");
}
if (Expand != KEYVAL_EXPAND)
aprintf(fout, "%s\t%c%s%c;\n",
Kexpand, SDELIM, expand_names[Expand], SDELIM
);
awrite(Ignored.string, Ignored.size, fout);
aputc('\n', fout);
}
static void
putdelta(node,fout)
register struct hshentry const *node;
register FILE * fout;
/* Function: prints a <delta> node to fout;
*/
{
struct branchhead const *nextbranch;
if (node == nil) return;
aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
node->num,
Kdate, node->date,
Kauthor, node->author,
Kstate, node->state?node->state:""
);
nextbranch = node->branches;
while (nextbranch) {
aprintf(fout, "\n\t%s", nextbranch->hsh->num);
nextbranch = nextbranch->nextbranch;
}
aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
awrite(node->ig.string, node->ig.size, fout);
}
void
puttree(root,fout)
struct hshentry const *root;
register FILE * fout;
/* Function: prints the delta tree in preorder to fout, starting with root.
*/
{
struct branchhead const *nextbranch;
if (root==nil) return;
if (root->selector)
putdelta(root,fout);
puttree(root->next,fout);
nextbranch = root->branches;
while (nextbranch) {
puttree(nextbranch->hsh,fout);
nextbranch = nextbranch->nextbranch;
}
}
static exiting void
unexpected_EOF()
{
faterror("unexpected EOF in diff output");
}
int putdtext(num,log,srcfilename,fout,diffmt)
char const *num, *srcfilename;
struct cbuf log;
FILE *fout;
int diffmt;
/* Function: write a deltatext-node to fout.
* num points to the deltanumber, log to the logmessage, and
* sourcefile contains the text. Doubles up all SDELIMs in both the
* log and the text; Makes sure the log message ends in \n.
* returns false on error.
* If diffmt is true, also checks that text is valid diff -n output.
*/
{
RILE *fin;
int result;
if (!(fin = Iopen(srcfilename, "r", (struct stat*)0))) {
eerror(srcfilename);
return false;
}
result = putdftext(num,log,fin,fout,diffmt);
Ifclose(fin);
return result;
}
void
putstring(out, delim, s, log)
register FILE *out;
struct cbuf s;
int delim, log;
/*
* Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
* If LOG is set then S is a log string; append a newline if S is nonempty.
*/
{
register char const *sp;
register size_t ss;
if (delim)
aputc(SDELIM, out);
sp = s.string;
for (ss = s.size; ss; --ss) {
if (*sp == SDELIM)
aputc(SDELIM, out);
aputc(*sp++, out);
}
if (s.size && log)
aputc('\n', out);
aputc(SDELIM, out);
}
int
putdftext(num,log,finfile,foutfile,diffmt)
char const *num;
struct cbuf log;
RILE *finfile;
FILE *foutfile;
int diffmt;
/* like putdtext(), except the source file is already open */
{
declarecache;
register FILE *fout;
register int c;
register RILE *fin;
int ed;
struct diffcmd dc;
fout = foutfile;
aprintf(fout,DELNUMFORM,num,Klog);
/* put log */
putstring(fout, true, log, true);
/* put text */
aprintf(fout, "\n%s\n%c", Ktext, SDELIM);
fin = finfile;
setupcache(fin);
if (!diffmt) {
/* Copy the file */
cache(fin);
for (;;) {
cachegeteof(c, break;);
if (c==SDELIM) aputc(SDELIM,fout); /*double up SDELIM*/
aputc(c,fout);
}
} else {
initdiffcmd(&dc);
while (0 <= (ed = getdiffcmd(fin,false,fout,&dc)))
if (ed) {
cache(fin);
while (dc.nlines--)
do {
cachegeteof(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); });
if (c == SDELIM)
aputc(SDELIM,fout);
aputc(c,fout);
} while (c != '\n');
uncache(fin);
}
}
OK_EOF:
aprintf(fout, "%c\n", SDELIM);
return true;
}
void
initdiffcmd(dc)
register struct diffcmd *dc;
/* Initialize *dc suitably for getdiffcmd(). */
{
dc->adprev = 0;
dc->dafter = 0;
}
static exiting void
badDiffOutput(buf)
char const *buf;
{
faterror("bad diff output line: %s", buf);
}
static exiting void
diffLineNumberTooLarge(buf)
char const *buf;
{
faterror("diff line number too large: %s", buf);
}
int
getdiffcmd(finfile, delimiter, foutfile, dc)
RILE *finfile;
FILE *foutfile;
int delimiter;
struct diffcmd *dc;
/* Get a editing command output by 'diff -n' from fin.
* The input is delimited by SDELIM if delimiter is set, EOF otherwise.
* Copy a clean version of the command to fout (if nonnull).
* Yield 0 for 'd', 1 for 'a', and -1 for EOF.
* Store the command's line number and length into dc->line1 and dc->nlines.
* Keep dc->adprev and dc->dafter up to date.
*/
{
register int c;
declarecache;
register FILE *fout;
register char *p;
register RILE *fin;
unsigned long line1, nlines, t;
char buf[BUFSIZ];
fin = finfile;
fout = foutfile;
setupcache(fin); cache(fin);
cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } );
if (delimiter) {
if (c==SDELIM) {
cacheget(c);
if (c==SDELIM) {
buf[0] = c;
buf[1] = 0;
badDiffOutput(buf);
}
uncache(fin);
nextc = c;
if (fout)
aprintf(fout, "%c%c", SDELIM, c);
return -1;
}
}
p = buf;
do {
if (buf+BUFSIZ-2 <= p) {
faterror("diff output command line too long");
}
*p++ = c;
cachegeteof(c, unexpected_EOF();) ;
} while (c != '\n');
uncache(fin);
if (delimiter)
++rcsline;
*p = '\0';
for (p = buf+1; (c = *p++) == ' '; )
;
line1 = 0;
while (isdigit(c)) {
t = line1 * 10;
if (
ULONG_MAX/10 < line1 ||
(line1 = t + (c - '0')) < t
)
diffLineNumberTooLarge(buf);
c = *p++;
}
while (c == ' ')
c = *p++;
nlines = 0;
while (isdigit(c)) {
t = nlines * 10;
if (
ULONG_MAX/10 < nlines ||
(nlines = t + (c - '0')) < t
)
diffLineNumberTooLarge(buf);
c = *p++;
}
if (c || !nlines) {
badDiffOutput(buf);
}
if (line1+nlines < line1)
diffLineNumberTooLarge(buf);
switch (buf[0]) {
case 'a':
if (line1 < dc->adprev) {
faterror("backward insertion in diff output: %s", buf);
}
dc->adprev = line1 + 1;
break;
case 'd':
if (line1 < dc->adprev || line1 < dc->dafter) {
faterror("backward deletion in diff output: %s", buf);
}
dc->adprev = line1;
dc->dafter = line1 + nlines;
break;
default:
badDiffOutput(buf);
}
if (fout) {
aprintf(fout, "%s\n", buf);
}
dc->line1 = line1;
dc->nlines = nlines;
return buf[0] == 'a';
}
#ifdef SYNTEST
char const cmdid[] = "syntest";
int
main(argc,argv)
int argc; char * argv[];
{
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s", argv[1]);
}
Lexinit();
getadmin();
putadmin(stdout);
gettree();
puttree(Head,stdout);
getdesc(true);
nextlex();
if (!eoflex()) {
fatserror("expecting EOF");
}
exitmain(EXIT_SUCCESS);
}
exiting void exiterr() { _exit(EXIT_FAILURE); }
#endif

View file

@ -0,0 +1,994 @@
/*
* RCS utilities
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsutil.c,v $
* Revision 5.10 1991/10/07 17:32:46 eggert
* Support piece tables even if !has_mmap.
*
* Revision 5.9 1991/08/19 03:13:55 eggert
* Add spawn() support. Explicate assumptions about getting invoker's name.
* Standardize user-visible dates. Tune.
*
* Revision 5.8 1991/04/21 11:58:30 eggert
* Plug setuid security hole.
*
* Revision 5.6 1991/02/26 17:48:39 eggert
* Fix setuid bug. Use fread, fwrite more portably.
* Support waitpid. Don't assume -1 is acceptable to W* macros.
* strsave -> str_save (DG/UX name clash)
*
* Revision 5.5 1990/12/04 05:18:49 eggert
* Don't output a blank line after a signal diagnostic.
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/01 05:03:53 eggert
* Remove unneeded setid check. Add awrite(), fremember().
*
* Revision 5.3 1990/10/06 00:16:45 eggert
* Don't fread F if feof(F).
*
* Revision 5.2 1990/09/04 08:02:31 eggert
* Store fread()'s result in an fread_type object.
*
* Revision 5.1 1990/08/29 07:14:07 eggert
* Declare getpwuid() more carefully.
*
* Revision 5.0 1990/08/22 08:13:46 eggert
* Add setuid support. Permit multiple locks per user.
* Remove compile-time limits; use malloc instead.
* Switch to GMT. Permit dates past 1999/12/31.
* Add -V. Remove snooping. Ansify and Posixate.
* Tune. Some USG hosts define NSIG but not sys_siglist.
* Don't run /bin/sh if it's hopeless.
* Don't leave garbage behind if the output is an empty pipe.
* Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
*
* Revision 4.6 89/05/01 15:13:40 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/11/08 16:01:02 narten
* corrected use of varargs routines
*
* Revision 4.4 88/08/09 19:13:24 eggert
* Check for memory exhaustion.
* Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
* Use execv(), not system(); yield exit status like diff(1)'s.
*
* Revision 4.3 87/10/18 10:40:22 narten
* Updating version numbers. Changes relative to 1.1 actually
* relative to 4.1
*
* Revision 1.3 87/09/24 14:01:01 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:43 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 15:53:13 wft
* Added getcaller() and findlock().
* Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
* (needed for background jobs in older shells). Added restoreints().
* Removed printing of full RCS path from logcommand().
*
* Revision 3.8 83/02/15 15:41:49 wft
* Added routine fastcopy() to copy remainder of a file in blocks.
*
* Revision 3.7 82/12/24 15:25:19 wft
* added catchints(), ignoreints() for catching and ingnoring interrupts;
* fixed catchsig().
*
* Revision 3.6 82/12/08 21:52:05 wft
* Using DATEFORM to format dates.
*
* Revision 3.5 82/12/04 18:20:49 wft
* Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
* lockedby-field.
*
* Revision 3.4 82/12/03 17:17:43 wft
* Added check to addlock() ensuring only one lock per person.
* Addlock also returns a pointer to the lock created. Deleted fancydate().
*
* Revision 3.3 82/11/27 12:24:37 wft
* moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
* Introduced macro SNOOP so that snoop can be placed in directory other than
* TARGETDIR. Changed %02d to %.2d for compatibility reasons.
*
* Revision 3.2 82/10/18 21:15:11 wft
* added function getfullRCSname().
*
* Revision 3.1 82/10/13 16:17:37 wft
* Cleanup message is now suppressed in quiet mode.
*/
#include "rcsbase.h"
libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
#if !has_memcmp
int
memcmp(s1, s2, n)
void const *s1, *s2;
size_t n;
{
register unsigned char const
*p1 = (unsigned char const*)s1,
*p2 = (unsigned char const*)s2;
register size_t i = n;
register int r = 0;
while (i-- && !(r = (*p1++ - *p2++)))
;
return r;
}
#endif
#if !has_memcpy
void *
memcpy(s1, s2, n)
void *s1;
void const *s2;
size_t n;
{
register char *p1 = (char*)s1;
register char const *p2 = (char const*)s2;
while (n--)
*p1++ = *p2++;
return s1;
}
#endif
#if lint
malloc_type lintalloc;
#endif
/*
* list of blocks allocated with ftestalloc()
* These blocks can be freed by ffree when we're done with the current file.
* We could put the free block inside struct alloclist, rather than a pointer
* to the free block, but that would be less portable.
*/
struct alloclist {
malloc_type alloc;
struct alloclist *nextalloc;
};
static struct alloclist *alloced;
static malloc_type
okalloc(p)
malloc_type p;
{
if (!p)
faterror("out of memory");
return p;
}
malloc_type
testalloc(size)
size_t size;
/* Allocate a block, testing that the allocation succeeded. */
{
return okalloc(malloc(size));
}
malloc_type
testrealloc(ptr, size)
malloc_type ptr;
size_t size;
/* Reallocate a block, testing that the allocation succeeded. */
{
return okalloc(realloc(ptr, size));
}
malloc_type
fremember(ptr)
malloc_type ptr;
/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
{
register struct alloclist *q = talloc(struct alloclist);
q->nextalloc = alloced;
alloced = q;
return q->alloc = ptr;
}
malloc_type
ftestalloc(size)
size_t size;
/* Allocate a block, putting it in 'alloced' so it can be freed later. */
{
return fremember(testalloc(size));
}
void
ffree()
/* Free all blocks allocated with ftestalloc(). */
{
register struct alloclist *p, *q;
for (p = alloced; p; p = q) {
q = p->nextalloc;
tfree(p->alloc);
tfree(p);
}
alloced = nil;
}
void
ffree1(f)
register char const *f;
/* Free the block f, which was allocated by ftestalloc. */
{
register struct alloclist *p, **a = &alloced;
while ((p = *a)->alloc != f)
a = &p->nextalloc;
*a = p->nextalloc;
tfree(p->alloc);
tfree(p);
}
char *
str_save(s)
char const *s;
/* Save s in permanently allocated storage. */
{
return strcpy(tnalloc(char, strlen(s)+1), s);
}
char *
fstr_save(s)
char const *s;
/* Save s in storage that will be deallocated when we're done with this file. */
{
return strcpy(ftnalloc(char, strlen(s)+1), s);
}
char *
cgetenv(name)
char const *name;
/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
{
register char *p;
return (p=getenv(name)) ? str_save(p) : p;
}
char const *
getusername(suspicious)
int suspicious;
/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
{
static char *name;
if (!name) {
if (
/* Prefer getenv() unless suspicious; it's much faster. */
# if getlogin_is_secure
(suspicious
||
!(name = cgetenv("LOGNAME"))
&& !(name = cgetenv("USER")))
&& !(name = getlogin())
# else
suspicious
||
!(name = cgetenv("LOGNAME"))
&& !(name = cgetenv("USER"))
&& !(name = getlogin())
# endif
) {
#if has_getuid && has_getpwuid
struct passwd const *pw = getpwuid(ruid());
if (!pw)
faterror("no password entry for userid %lu",
(unsigned long)ruid()
);
name = pw->pw_name;
#else
#if has_setuid
faterror("setuid not supported");
#else
faterror("Who are you? Please set LOGNAME.");
#endif
#endif
}
checksid(name);
}
return name;
}
#if has_signal
/*
* Signal handling
*
* Standard C places too many restrictions on signal handlers.
* We obey as many of them as we can.
* Posix places fewer restrictions, and we are Posix-compatible here.
*/
static sig_atomic_t volatile heldsignal, holdlevel;
static signal_type
catchsig(s)
int s;
{
char const *sname;
char buf[BUFSIZ];
#if sig_zaps_handler
/* If a signal arrives before we reset the signal handler, we lose. */
VOID signal(s, SIG_IGN);
#endif
if (holdlevel) {
heldsignal = s;
return;
}
ignoreints();
setrid();
if (!quietflag) {
sname = nil;
#if has_sys_siglist && defined(NSIG)
if ((unsigned)s < NSIG) {
# ifndef sys_siglist
extern char const *sys_siglist[];
# endif
sname = sys_siglist[s];
}
#else
switch (s) {
#ifdef SIGHUP
case SIGHUP: sname = "Hangup"; break;
#endif
#ifdef SIGINT
case SIGINT: sname = "Interrupt"; break;
#endif
#ifdef SIGPIPE
case SIGPIPE: sname = "Broken pipe"; break;
#endif
#ifdef SIGQUIT
case SIGQUIT: sname = "Quit"; break;
#endif
#ifdef SIGTERM
case SIGTERM: sname = "Terminated"; break;
#endif
#ifdef SIGXCPU
case SIGXCPU: sname = "Cputime limit exceeded"; break;
#endif
#ifdef SIGXFSZ
case SIGXFSZ: sname = "Filesize limit exceeded"; break;
#endif
}
#endif
if (sname)
VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname);
else
VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s);
VOID write(STDERR_FILENO, buf, strlen(buf));
}
exiterr();
}
void
ignoreints()
{
++holdlevel;
}
void
restoreints()
{
if (!--holdlevel && heldsignal)
VOID catchsig(heldsignal);
}
static int const sig[] = {
#ifdef SIGHUP
SIGHUP,
#endif
#ifdef SIGINT
SIGINT,
#endif
#ifdef SIGPIPE
SIGPIPE,
#endif
#ifdef SIGQUIT
SIGQUIT,
#endif
#ifdef SIGTERM
SIGTERM,
#endif
#ifdef SIGXCPU
SIGXCPU,
#endif
#ifdef SIGXFSZ
SIGXFSZ,
#endif
};
#define SIGS (sizeof(sig)/sizeof(*sig))
#if has_sigaction
static void
check_sig(r)
int r;
{
if (r != 0)
efaterror("signal");
}
static void
setup_catchsig()
{
register int i;
sigset_t blocked;
struct sigaction act;
check_sig(sigemptyset(&blocked));
for (i=SIGS; 0<=--i; )
check_sig(sigaddset(&blocked, sig[i]));
for (i=SIGS; 0<=--i; ) {
check_sig(sigaction(sig[i], (struct sigaction*)nil, &act));
if (act.sa_handler != SIG_IGN) {
act.sa_handler = catchsig;
act.sa_mask = blocked;
check_sig(sigaction(sig[i], &act, (struct sigaction*)nil));
}
}
}
#else
#if has_sigblock
static void
setup_catchsig()
{
register int i;
int mask;
mask = 0;
for (i=SIGS; 0<=--i; )
mask |= sigmask(sig[i]);
mask = sigblock(mask);
for (i=SIGS; 0<=--i; )
if (
signal(sig[i], catchsig) == SIG_IGN &&
signal(sig[i], SIG_IGN) != catchsig
)
faterror("signal catcher failure");
VOID sigsetmask(mask);
}
#else
static void
setup_catchsig()
{
register i;
for (i=SIGS; 0<=--i; )
if (
signal(sig[i], SIG_IGN) != SIG_IGN &&
signal(sig[i], catchsig) != SIG_IGN
)
faterror("signal catcher failure");
}
#endif
#endif
void
catchints()
{
static int catching_ints;
if (!catching_ints) {
catching_ints = true;
setup_catchsig();
}
}
#endif /* has_signal */
void
fastcopy(inf,outf)
register RILE *inf;
FILE *outf;
/* Function: copies the remainder of file inf to outf.
*/
{
#if large_memory
# if has_mmap
awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
inf->ptr = inf->lim;
# else
for (;;) {
awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
inf->ptr = inf->readlim;
if (inf->ptr == inf->lim)
break;
VOID Igetmore(inf);
}
# endif
#else
char buf[BUFSIZ*8];
register fread_type rcount;
/*now read the rest of the file in blocks*/
while (!feof(inf)) {
if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
testIerror(inf);
return;
}
awrite(buf, (size_t)rcount, outf);
}
#endif
}
#ifndef SSIZE_MAX
/* This does not work in #ifs, but it's good enough for us. */
/* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
# define SSIZE_MAX ((unsigned)-1 >> 1)
#endif
void
awrite(buf, chars, f)
char const *buf;
size_t chars;
FILE *f;
{
/* Posix 1003.1-1990 ssize_t hack */
while (SSIZE_MAX < chars) {
if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
Oerror();
buf += SSIZE_MAX;
chars -= SSIZE_MAX;
}
if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
Oerror();
}
static int
movefd(old, new)
int old, new;
{
if (old < 0 || old == new)
return old;
# ifdef F_DUPFD
new = fcntl(old, F_DUPFD, new);
# else
new = dup2(old, new);
# endif
return close(old)==0 ? new : -1;
}
static int
fdreopen(fd, file, flags)
int fd;
char const *file;
int flags;
{
int newfd;
VOID close(fd);
newfd =
#if !open_can_creat
flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
#endif
open(file, flags, S_IRUSR|S_IWUSR);
return movefd(newfd, fd);
}
#if !has_spawn
static void
tryopen(fd,file,flags)
int fd, flags;
char const *file;
{
if (file && fdreopen(fd,file,flags) != fd)
efaterror(file);
}
#else
static int
tryopen(fd,file,flags)
int fd, flags;
char const *file;
{
int newfd = -1;
if (file && ((newfd=dup(fd)) < 0 || fdreopen(fd,file,flags) != fd))
efaterror(file);
return newfd;
}
static void
redirect(old, new)
int old, new;
{
if (0 <= old && (close(new) != 0 || movefd(old,new) < 0))
efaterror("spawn I/O redirection");
}
#endif
#if !has_fork && !has_spawn
static void
bufargcat(b, c, s)
register struct buf *b;
int c;
register char const *s;
/* Append to B a copy of C, plus a quoted copy of S. */
{
register char *p;
register char const *t;
size_t bl, sl;
for (t=s, sl=0; *t; )
sl += 3*(*t++=='\'') + 1;
bl = strlen(b->string);
bufrealloc(b, bl + sl + 4);
p = b->string + bl;
*p++ = c;
*p++ = '\'';
while (*s) {
if (*s == '\'') {
*p++ = '\'';
*p++ = '\\';
*p++ = '\'';
}
*p++ = *s++;
}
*p++ = '\'';
*p = 0;
}
#endif
/*
* Run a command specified by the strings in 'inoutargs'.
* inoutargs[0], if nonnil, is the name of the input file.
* inoutargs[1], if nonnil, is the name of the output file.
* inoutargs[2..] form the command to be run.
*/
int
runv(inoutargs)
char const **inoutargs;
{
register char const **p;
int wstatus;
oflush();
eflush();
{
#if has_spawn
int in, out;
p = inoutargs;
in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
wstatus = spawn_RCS(0, *p, (char*const*)p);
if (wstatus == -1 && errno == ENOEXEC) {
*--p = RCS_SHELL;
wstatus = spawnv(0, *p, (char*const*)p);
}
redirect(in, STDIN_FILENO);
redirect(out, STDOUT_FILENO);
#else
#if has_fork
pid_t pid;
# if !has_waitpid
pid_t w;
# endif
if (!(pid = vfork())) {
p = inoutargs;
tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
VOID exec_RCS(*p, (char*const*)p);
if (errno == ENOEXEC) {
*--p = RCS_SHELL;
VOID execv(*p, (char*const*)p);
}
VOID write(STDERR_FILENO, *p, strlen(*p));
VOID write(STDERR_FILENO, ": not found\n", 12);
_exit(EXIT_TROUBLE);
}
if (pid < 0)
efaterror("fork");
# if has_waitpid
if (waitpid(pid, &wstatus, 0) < 0)
efaterror("waitpid");
# else
do {
if ((w = wait(&wstatus)) < 0)
efaterror("wait");
} while (w != pid);
# endif
#else
static struct buf b;
/* Use system(). On many hosts system() discards signals. Yuck! */
p = inoutargs+2;
bufscpy(&b, *p);
while (*++p)
bufargcat(&b, ' ', *p);
if (inoutargs[0])
bufargcat(&b, '<', inoutargs[0]);
if (inoutargs[1])
bufargcat(&b, '>', inoutargs[1]);
wstatus = system(b.string);
#endif
#endif
}
if (!WIFEXITED(wstatus))
faterror("%s failed", inoutargs[2]);
return WEXITSTATUS(wstatus);
}
#define CARGSMAX 20
/*
* Run a command.
* The first two arguments are the input and output files (if nonnil);
* the rest specify the command and its arguments.
*/
int
#if has_prototypes
run(char const *infile, char const *outfile, ...)
#else
/*VARARGS2*/
run(infile, outfile, va_alist)
char const *infile;
char const *outfile;
va_dcl
#endif
{
va_list ap;
char const *rgargs[CARGSMAX];
register i = 0;
rgargs[0] = infile;
rgargs[1] = outfile;
vararg_start(ap, outfile);
for (i = 2; (rgargs[i++] = va_arg(ap, char const*)); )
if (CARGSMAX <= i)
faterror("too many command arguments");
va_end(ap);
return runv(rgargs);
}
char const *
date2str(date, datebuf)
char const date[datesize];
char datebuf[datesize];
/*
* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
* Yield DATEBUF.
*/
{
register char const *p = date;
while (*p++ != '.')
;
VOID sprintf(datebuf,
"19%.*s/%.2s/%.2s %.2s:%.2s:%s" +
(date[2]=='.' && VERSION(5)<=RCSversion ? 0 : 2),
(int)(p-date-1), date,
p, p+3, p+6, p+9, p+12
);
return datebuf;
}
int RCSversion;
void
setRCSversion(str)
char const *str;
{
static int oldversion;
register char const *s = str + 2;
int v = VERSION_DEFAULT;
if (oldversion)
redefined('V');
oldversion = true;
if (*s) {
v = 0;
while (isdigit(*s))
v = 10*v + *s++ - '0';
if (*s)
faterror("%s isn't a number", str);
if (v < VERSION_min || VERSION_max < v)
faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max);
}
RCSversion = VERSION(v);
}
int
getRCSINIT(argc, argv, newargv)
int argc;
char **argv, ***newargv;
{
register char *p, *q, **pp;
unsigned n;
if (!(q = cgetenv("RCSINIT")))
*newargv = argv;
else {
n = argc + 2;
/*
* Count spaces in RCSINIT to allocate a new arg vector.
* This is an upper bound, but it's OK even if too large.
*/
for (p = q; ; ) {
switch (*p++) {
default:
continue;
case ' ':
case '\b': case '\f': case '\n':
case '\r': case '\t': case '\v':
n++;
continue;
case '\0':
break;
}
break;
}
*newargv = pp = tnalloc(char*, n);
*pp++ = *argv++; /* copy program name */
for (p = q; ; ) {
for (;;) {
switch (*q) {
case '\0':
goto copyrest;
case ' ':
case '\b': case '\f': case '\n':
case '\r': case '\t': case '\v':
q++;
continue;
}
break;
}
*pp++ = p;
++argc;
for (;;) {
switch ((*p++ = *q++)) {
case '\0':
goto copyrest;
case '\\':
if (!*q)
goto copyrest;
p[-1] = *q++;
continue;
default:
continue;
case ' ':
case '\b': case '\f': case '\n':
case '\r': case '\t': case '\v':
break;
}
break;
}
p[-1] = '\0';
}
copyrest:
while ((*pp++ = *argv++))
;
}
return argc;
}
#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
#if has_getuid
uid_t ruid() { cacheid(getuid()); }
#endif
#if has_setuid
uid_t euid() { cacheid(geteuid()); }
#endif
#if has_setuid
/*
* Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
* because it lets us switch back and forth between arbitrary users.
* If seteuid() doesn't work, we fall back on setuid(),
* which works if saved setuid is supported,
* unless the real or effective user is root.
* This area is such a mess that we always check switches at runtime.
*/
static void
set_uid_to(u)
uid_t u;
/* Become user u. */
{
static int looping;
if (euid() == ruid())
return;
#if (has_fork||has_spawn) && DIFF_ABSOLUTE
if (seteuid(u) != 0)
efaterror("setuid");
#endif
if (geteuid() != u) {
if (looping)
return;
looping = true;
faterror("root setuid not supported" + (u?5:0));
}
}
static int stick_with_euid;
void
/* Ignore all calls to seteid() and setrid(). */
nosetid()
{
stick_with_euid = true;
}
void
seteid()
/* Become effective user. */
{
if (!stick_with_euid)
set_uid_to(euid());
}
void
setrid()
/* Become real user. */
{
if (!stick_with_euid)
set_uid_to(ruid());
}
#endif

View file

@ -0,0 +1,7 @@
PROG= merge
SRCS= merge.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

View file

@ -0,0 +1,102 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: merge.1,v 5.3 1991/02/28 19:18:45 eggert Exp $
.TH MERGE 1 \*(Dt GNU
.SH NAME
merge \- three-way file merge
.SH SYNOPSIS
.B merge
[
.B \-L
.I label1
[
.B \-L
.I label3
] ] [
.B \-p
] [
.B \-q
]
.I "file1 file2 file3"
.SH DESCRIPTION
.B merge
incorporates all changes that lead from
.I file2
to
.I file3
into
.IR file1 .
The result goes to standard output if
.B \-p
is present, into
.I file1
otherwise.
.B merge
is useful for combining separate changes to an original. Suppose
.I file2
is the original, and both
.I file1
and
.I file3
are modifications of
.IR file2 .
Then
.B merge
combines both changes.
.PP
An overlap occurs if both
.I file1
and
.I file3
have changes in a common segment of lines.
On a few older hosts where
.B diff3
does not support the
.B \-E
option,
.B merge
does not detect overlaps, and merely supplies the changed lines from
.I file3.
On most hosts, if overlaps occur,
.B merge
outputs a message (unless the
.B \-q
option is given),
and includes both alternatives
in the result. The alternatives are delimited as follows:
.LP
.RS
.nf
.BI <<<<<<< " file1"
.I "lines in file1"
.B "======="
.I "lines in file3"
.BI >>>>>>> " file3"
.RE
.fi
.LP
If there are overlaps, the user should edit the result and delete one of the
alternatives.
If the
.BI \-L "\ label1"
and
.BI \-L "\ label3"
options are given, the labels are output in place of the names
.I file1
and
.I file3
in overlap reports.
.SH DIAGNOSTICS
Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH SEE ALSO
diff3(1), diff(1), rcsmerge(1), co(1).

View file

@ -0,0 +1,97 @@
/* merge - three-way file merge */
/* Copyright 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
static char const usage[] =
"\nmerge: usage: merge [-p] [-q] [-L label1 [-L label3]] file1 file2 file3\n";
static exiting void
badoption(a)
char const *a;
{
faterror("unknown option: %s%s", a-2, usage);
}
mainProg(mergeId, "merge", "$Id: merge.c,v 1.2 1991/08/19 03:13:55 eggert Exp $")
{
register char const *a;
char const *label[2], *arg[3];
int labels, tostdout;
labels = 0;
tostdout = false;
while ((a = *++argv) && *a++ == '-') {
switch (*a++) {
case 'p': tostdout = true; break;
case 'q': quietflag = true; break;
case 'L':
if (1<labels)
faterror("too many -L options");
if (!(label[labels++] = *++argv))
faterror("-L needs following argument");
--argc;
break;
default:
badoption(a);
}
if (*a)
badoption(a);
--argc;
}
if (argc != 4)
faterror("%s arguments%s",
argc<4 ? "not enough" : "too many", usage
);
/* This copy keeps us `const'-clean. */
arg[0] = argv[0];
arg[1] = argv[1];
arg[2] = argv[2];
switch (labels) {
case 0: label[0] = arg[0]; /* fall into */
case 1: label[1] = arg[2];
}
exitmain(merge(tostdout, label, arg));
}
#if lint
# define exiterr mergeExit
#endif
exiting void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}

View file

@ -0,0 +1,10 @@
PROG= rcs
SRCS= rcs.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
MAN1= rcs.0 rcsintro.0
MAN5= rcsfile.0
.include <bsd.prog.mk>

397
gnu/usr.bin/rcs/rcs/rcs.1 Normal file
View file

@ -0,0 +1,397 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcs.1,v 5.6 1991/09/26 23:16:17 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCS 1 \*(Dt GNU
.SH NAME
rcs \- change RCS file attributes
.SH SYNOPSIS
.B rcs
.RI [ " options " ] " file " .\|.\|.
.SH DESCRIPTION
.B rcs
creates new \*r files or changes attributes of existing ones.
An \*r file contains multiple revisions of text,
an access list, a change log,
descriptive text,
and some control attributes.
For
.B rcs
to work, the caller's login name must be on the access list,
except if the access list is empty, the caller is the owner of the file
or the superuser, or
the
.B \-i
option is present.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
Revision numbers use the syntax described in
.BR ci (1).
.SH OPTIONS
.TP
.B \-i
Create and initialize a new \*r file, but do not deposit any revision.
If the \*r file has no path prefix, try to place it
first into the subdirectory
.BR ./RCS ,
and then into the current directory.
If the \*r file
already exists, print an error message.
.TP
.BI \-a "logins"
Append the login names appearing in the comma-separated list
.I logins
to the access list of the \*r file.
.TP
.BI \-A "oldfile"
Append the access list of
.I oldfile
to the access list of the \*r file.
.TP
.BR \-e [\f2logins\fP]
Erase the login names appearing in the comma-separated list
.I logins
from the access list of the \*r file.
If
.I logins
is omitted, erase the entire access list.
.TP
.BR \-b [\f2rev\fP]
Set the default branch to
.IR rev .
If
.I rev
is omitted, the default
branch is reset to the (dynamically) highest branch on the trunk.
.TP
.BI \-c string
sets the comment leader to
.IR string .
The comment leader
is printed before every log message line generated by the keyword
.B $\&Log$
during checkout (see
.BR co (1)).
This is useful for programming
languages without multi-line comments.
An initial
.B ci ,
or an
.B "rcs\ \-i"
without
.BR \-c ,
guesses the comment leader from the suffix of the working file.
.TP
.BI \-k subst
Set the default keyword substitution to
.IR subst .
The effect of keyword substitution is described in
.BR co (1).
Giving an explicit
.B \-k
option to
.BR co ,
.BR rcsdiff ,
and
.B rcsmerge
overrides this default.
Beware
.BR "rcs\ \-kv",
because
.B \-kv
is incompatible with
.BR "co\ \-l".
Use
.B "rcs\ \-kkv"
to restore the normal default keyword substitution.
.TP
.BR \-l [\f2rev\fP]
Lock the revision with number
.IR rev .
If a branch is given, lock the latest revision on that branch.
If
.I rev
is omitted, lock the latest revision on the default branch.
Locking prevents overlapping changes.
A lock is removed with
.B ci
or
.B "rcs\ \-u"
(see below).
.TP
.BR \-u [\f2rev\fP]
Unlock the revision with number
.IR rev .
If a branch is given, unlock the latest revision on that branch.
If
.I rev
is omitted, remove the latest lock held by the caller.
Normally, only the locker of a revision may unlock it.
Somebody else unlocking a revision breaks the lock.
This causes a mail message to be sent to the original locker.
The message contains a commentary solicited from the breaker.
The commentary is terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
.TP
.B \-L
Set locking to
.IR strict .
Strict locking means that the owner
of an \*r file is not exempt from locking for checkin.
This option should be used for files that are shared.
.TP
.B \-U
Set locking to non-strict. Non-strict locking means that the owner of
a file need not lock a revision for checkin.
This option should
.I not
be used for files that are shared.
Whether default locking is strict is determined by your system administrator,
but it is normally strict.
.TP
\f3\-m\fP\f2rev\fP\f3:\fP\f2msg\fP
Replace revision
.IR rev 's
log message with
.IR msg .
.TP
\f3\-n\fP\f2name\fP[\f3:\fP[\f2rev\fP]]
Associate the symbolic name
.I name
with the branch or
revision
.IR rev .
Delete the symbolic name if both
.B :
and
.I rev
are omitted; otherwise, print an error message if
.I name
is already associated with
another number.
If
.I rev
is symbolic, it is expanded before association.
A
.I rev
consisting of a branch number followed by a
.B .\&
stands for the current latest revision in the branch.
A
.B :
with an empty
.I rev
stands for the current latest revision on the default branch,
normally the trunk.
For example,
.BI "rcs\ \-n" name ":\ RCS/*"
associates
.I name
with the current latest revision of all the named \*r files;
this contrasts with
.BI "rcs\ \-n" name ":$\ RCS/*"
which associates
.I name
with the revision numbers extracted from keyword strings
in the corresponding working files.
.TP
\f3\-N\fP\f2name\fP[\f3:\fP[\f2rev\fP]]
Act like
.BR \-n ,
except override any previous assignment of
.IR name .
.TP
.BI \-o range
deletes (\*(lqoutdates\*(rq) the revisions given by
.IR range .
A range consisting of a single revision number means that revision.
A range consisting of a branch number means the latest revision on that
branch.
A range of the form
.IB rev1 : rev2
means
revisions
.I rev1
to
.I rev2
on the same branch,
.BI : rev
means from the beginning of the branch containing
.I rev
up to and including
.IR rev ,
and
.IB rev :
means
from revision
.I rev
to the end of the branch containing
.IR rev .
None of the outdated revisions may have branches or locks.
.TP
.B \-q
Run quietly; do not print diagnostics.
.TP
.B \-I
Run interactively, even if the standard input is not a terminal.
.TP
.B \-s\f2state\fP\f1[\fP:\f2rev\fP\f1]\fP
Set the state attribute of the revision
.I rev
to
.I state .
If
.I rev
is a branch number, assume the latest revision on that branch.
If
.I rev
is omitted, assume the latest revision on the default branch.
Any identifier is acceptable for
.IR state .
A useful set of states
is
.B Exp
(for experimental),
.B Stab
(for stable), and
.B Rel
(for
released).
By default,
.BR ci (1)
sets the state of a revision to
.BR Exp .
.TP
.BR \-t [\f2file\fP]
Write descriptive text from the contents of the named
.I file
into the \*r file, deleting the existing text.
The
.IR file
pathname may not begin with
.BR \- .
If
.I file
is omitted, obtain the text from standard input,
terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
Prompt for the text if interaction is possible; see
.BR \-I .
With
.BR \-i ,
descriptive text is obtained
even if
.B \-t
is not given.
.TP
.BI \-t\- string
Write descriptive text from the
.I string
into the \*r file, deleting the existing text.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.SH COMPATIBILITY
The
.BI \-b rev
option generates an \*r file that cannot be parsed by \*r version 3 or earlier.
.PP
The
.BI \-k subst
options (except
.BR \-kkv )
generate an \*r file that cannot be parsed by \*r version 4 or earlier.
.PP
Use
.BI "rcs \-V" n
to make an \*r file acceptable to \*r version
.I n
by discarding information that would confuse version
.IR n .
.PP
\*r version 5.5 and earlier does not support the
.B \-x
option, and requires a
.B ,v
suffix on an \*r pathname.
.SH FILES
.B rcs
accesses files much as
.BR ci (1)
does,
except that it uses the effective user for all accesses,
it does not write the working file or its directory,
and it does not even read the working file unless a revision number of
.B $
is specified.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The \*r pathname and the revisions outdated are written to
the diagnostic output.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
co(1), ci(1), ident(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
The separator for revision ranges in the
.B \-o
option used to be
.B \-
instead of
.BR : ,
but this leads to confusion when symbolic names contain
.BR \- .
For backwards compatibility
.B "rcs \-o"
still supports the old
.B \-
separator, but it warns about this obsolete use.
.PP
Symbolic names need not refer to existing revisions or branches.
For example, the
.B \-o
option does not remove symbolic names for the outdated revisions; you must use
.B \-n
to remove the names.
.br

1554
gnu/usr.bin/rcs/rcs/rcs.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,224 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsfile.5,v 5.1 1991/08/19 03:13:55 eggert Exp $
.ds r \s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSFILE 5 \*(Dt GNU
.SH NAME
rcsfile \- format of RCS file
.SH DESCRIPTION
An \*r file's
contents are described by the grammar
below.
.PP
The text is free format: space, backspace, tab, newline, vertical
tab, form feed, and carriage return (collectively,
.IR "white space")
have no significance except in strings.
However, an \*r file must end in a newline character.
.PP
Strings are enclosed by
.BR @ .
If a string contains a
.BR @ ,
it must be doubled;
otherwise, strings may contain arbitrary binary data.
.PP
The meta syntax uses the following conventions: `|' (bar) separates
alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose
phrases that may be repeated zero or more times;
`{' and '}+' enclose phrases that must appear at least once and may be
repeated;
Terminal symbols are in
.BR boldface ;
nonterminal symbols are in
.IR italics .
.LP
.nr x \w'\f3branches\fP'
.nr y \w'{ \f3comment\fP'
.if \nx<\ny .nr x \ny
.nr y \w'\f3{ branch\fP'
.if \nx<\ny .nr x \ny
.ta \w'\f2deltatext\fP 'u +\w'::= 'u +\nxu+\w' 'u
.fc ~
.nf
\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}*
.LP
\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP
{ \f3branch\fP {\f2num\fP}\f3;\fP }
\f3access\fP {\f2id\fP}*\f3;\fP
\f3symbols\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP
\f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP}
{ \f3comment\fP {\f2string\fP}\f3;\fP }
{ \f3expand\fP {\f2string\fP}\f3;\fP }
{ \f2newphrase\fP }*
.LP
\f2delta\fP ::= \f2num\fP
\f3date\fP \f2num\fP\f3;\fP
\f3author\fP \f2id\fP\f3;\fP
\f3state\fP {\f2id\fP}\f3;\fP
\f3branches\fP {\f2num\fP}*\f3;\fP
\f3next\fP {\f2num\fP}\f3;\fP
{ \f2newphrase\fP }*
.LP
\f2desc\fP ::= \f3desc\fP \f2string\fP
.LP
\f2deltatext\fP ::= \f2num\fP
\f3log\fP \f2string\fP
{ \f2newphrase\fP }*
\f3text\fP \f2string\fP
.LP
\f2num\fP ::= {\f2digit\fP{\f3.\fP}}+
.LP
\f2digit\fP ::= \f30\fP | \f31\fP | .\|.\|. | \f39\fP
.LP
\f2id\fP ::= \f2letter\fP{\f2idchar\fP}*
.LP
\f2letter\fP ::= any letter
.LP
\f2idchar\fP ::= any visible graphic character except \f2special\fP
.LP
\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP
.LP
\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP
.LP
\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP
.LP
\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP
.fi
.PP
Identifiers are case sensitive. Keywords are in lower case only.
The sets of keywords and identifiers may overlap.
In most environments RCS uses the ISO 8859/1 encoding:
letters are octal codes 101\-132, 141\-172, 300\-326, 330\-366 and 370-377,
visible graphic characters are codes 041\-176 and 240\-377,
and white space characters are codes 010\-015 and 040.
.PP
The
.I newphrase
productions in the grammar are reserved for future extensions
to the format of \*r files.
No
.I newphrase
will begin with any keyword already in use.
.PP
The
.I delta
nodes form a tree. All nodes whose numbers
consist of a single pair
(e.g., 2.3, 2.1, 1.3, etc.)
are on the trunk, and are linked through the
.B next
field in order of decreasing numbers.
The
.B head
field in the
.I admin
node points to the head of that sequence (i.e., contains
the highest pair).
The
.B branch
node in the admin node indicates the default
branch (or revision) for most \*r operations.
If empty, the default
branch is the highest branch on the trunk.
.PP
All
.I delta
nodes whose numbers consist of
.RI 2 n
fields
.RI ( n >=2)
(e.g., 3.1.1.1, 2.1.2.2, etc.)
are linked as follows.
All nodes whose first
.RI 2 n \-1
number fields are identical are linked through the
.B next
field in order of increasing numbers.
For each such sequence,
the
.I delta
node whose number is identical to the first
.RI 2 n \-2
number fields of the deltas on that sequence is called the branchpoint.
The
.B branches
field of a node contains a list of the
numbers of the first nodes of all sequences for which it is a branchpoint.
This list is ordered in increasing numbers.
.LP
.nf
.vs 12
.ne 38
Example:
.if t .in +0.5i
.cs 1 20
.eo
Head
|
|
v / \
--------- / \
/ \ / \ | | / \ / \
/ \ / \ | 2.1 | / \ / \
/ \ / \ | | / \ / \
/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\
--------- --------- --------- --------- -------------
^ ^ | ^ ^
| | | | |
| | v | |
/ \ | --------- / \ |
/ \ | \ 1.3 / / \ |
/ \ ---------\ / / \-----------
/1.2.1.1\ \ / /1.2.2.1\
--------- \ / ---------
^ | ^
| | |
| v |
| --------- |
| \ 1.2 / |
----------------------\ /---------
\ /
\ /
|
|
v
---------
\ 1.1 /
\ /
\ /
\ /
.ec
.if t .in
.cs 1
.ce
Fig. 1: A revision tree
.vs
.fi
.PP
.SH IDENTIFICATION
.de VL
\\$2
..
Author: Walter F. Tichy,
Purdue University, West Lafayette, IN, 47907.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH SEE ALSO
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsmerge(1), rlog(1),
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.

View file

@ -0,0 +1,292 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsintro.1,v 5.1 1991/04/21 12:00:46 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.am SS
.LP
..
.TH RCSINTRO 1 \*(Dt GNU
.SH NAME
rcsintro \- introduction to RCS commands
.SH DESCRIPTION
The Revision Control System (\*r) manages multiple revisions of files.
\*r automates the storing, retrieval, logging, identification, and merging
of revisions. \*r is useful for text that is revised frequently, for example
programs, documentation, graphics, papers, and form letters.
.PP
The basic user interface is extremely simple. The novice only needs
to learn two commands:
.BR ci (1)
and
.BR co (1).
.BR ci ,
short for \*(lqcheck in\*(rq, deposits the contents of a
file into an archival file called an \*r file. An \*r file
contains all revisions of a particular file.
.BR co ,
short for \*(lqcheck out\*(rq, retrieves revisions from an \*r file.
.SS "Functions of \*r"
.IP \(bu
Store and retrieve multiple revisions of text. \*r saves all old
revisions in a space efficient way.
Changes no longer destroy the original, because the
previous revisions remain accessible. Revisions can be retrieved according to
ranges of revision numbers, symbolic names, dates, authors, and
states.
.IP \(bu
Maintain a complete history of changes.
\*r logs all changes automatically.
Besides the text of each revision, \*r stores the author, the date and time of
check-in, and a log message summarizing the change.
The logging makes it easy to find out
what happened to a module, without having to compare
source listings or having to track down colleagues.
.IP \(bu
Resolve access conflicts. When two or more programmers wish to
modify the same revision, \*r alerts the programmers and prevents one
modification from corrupting the other.
.IP \(bu
Maintain a tree of revisions. \*r can maintain separate lines of development
for each module. It stores a tree structure that represents the
ancestral relationships among revisions.
.IP \(bu
Merge revisions and resolve conflicts.
Two separate lines of development of a module can be coalesced by merging.
If the revisions to be merged affect the same sections of code, \*r alerts the
user about the overlapping changes.
.IP \(bu
Control releases and configurations.
Revisions can be assigned symbolic names
and marked as released, stable, experimental, etc.
With these facilities, configurations of modules can be
described simply and directly.
.IP \(bu
Automatically identify each revision with name, revision number,
creation time, author, etc.
The identification is like a stamp that can be embedded at an appropriate place
in the text of a revision.
The identification makes it simple to determine which
revisions of which modules make up a given configuration.
.IP \(bu
Minimize secondary storage. \*r needs little extra space for
the revisions (only the differences). If intermediate revisions are
deleted, the corresponding deltas are compressed accordingly.
.SS "Getting Started with \*r"
Suppose you have a file
.B f.c
that you wish to put under control of \*r.
If you have not already done so, make an \*r directory with the command
.IP
.B "mkdir RCS"
.LP
Then invoke the check-in command
.IP
.B "ci f.c"
.LP
This command creates an \*r file in the
.B RCS
directory,
stores
.B f.c
into it as revision 1.1, and
deletes
.BR f.c .
It also asks you for a description. The description
should be a synopsis of the contents of the file. All later check-in
commands will ask you for a log entry, which should summarize the
changes that you made.
.PP
Files in the \*r directory are called \*r files;
the others are called working files.
To get back the working file
.B f.c
in the previous example, use the check-out
command
.IP
.B "co f.c"
.LP
This command extracts the latest revision from the \*r file
and writes
it into
.BR f.c .
If you want to edit
.BR f.c ,
you must lock it as you check it out with the command
.IP
.B "co \-l f.c"
.LP
You can now edit
.BR f.c .
.PP
Suppose after some editing you want to know what changes that you have made.
The command
.IP
.B "rcsdiff f.c"
.LP
tells you the difference between the most recently checked-in version
and the working file.
You can check the file back in by invoking
.IP
.B "ci f.c"
.LP
This increments the revision number properly.
.PP
If
.B ci
complains with the message
.IP
.BI "ci error: no lock set by " "your name"
.LP
then you have tried to check in a file even though you did not
lock it when you checked it out.
Of course, it is too late now to do the check-out with locking, because
another check-out would
overwrite your modifications. Instead, invoke
.IP
.B "rcs \-l f.c"
.LP
This command will lock the latest revision for you, unless somebody
else got ahead of you already. In this case, you'll have to negotiate with
that person.
.PP
Locking assures that you, and only you, can check in the next update, and
avoids nasty problems if several people work on the same file.
Even if a revision is locked, it can still be checked out for
reading, compiling, etc. All that locking
prevents is a
.I "check-in"
by anybody but the locker.
.PP
If your \*r file is private, i.e., if you are the only person who is going
to deposit revisions into it, strict locking is not needed and you
can turn it off.
If strict locking is turned off,
the owner of the \*r file need not have a lock for check-in; all others
still do. Turning strict locking off and on is done with the commands
.IP
.BR "rcs \-U f.c" " and " "rcs \-L f.c"
.LP
If you don't want to clutter your working directory with \*r files, create
a subdirectory called
.B RCS
in your working directory, and move all your \*r
files there. \*r commands will look first into that directory to find
needed files. All the commands discussed above will still work, without any
modification.
(Actually, pairs of \*r and working files can be specified in three ways:
(a) both are given, (b) only the working file is given, (c) only the
\*r file is given. Both \*r and working files may have arbitrary path prefixes;
\*r commands pair them up intelligently.)
.PP
To avoid the deletion of the working file during check-in (in case you want to
continue editing or compiling), invoke
.IP
.BR "ci \-l f.c" " or " "ci \-u f.c"
.LP
These commands check in
.B f.c
as usual, but perform an implicit
check-out. The first form also locks the checked in revision, the second one
doesn't. Thus, these options save you one check-out operation.
The first form is useful if you want to continue editing,
the second one if you just want to read the file.
Both update the identification markers in your working file (see below).
.PP
You can give
.B ci
the number you want assigned to a checked in
revision. Assume all your revisions were numbered 1.1, 1.2, 1.3, etc.,
and you would like to start release 2.
The command
.IP
.BR "ci \-r2 f.c" " or " "ci \-r2.1 f.c"
.LP
assigns the number 2.1 to the new revision.
From then on,
.B ci
will number the subsequent revisions
with 2.2, 2.3, etc. The corresponding
.B co
commands
.IP
.BR "co \-r2 f.c" " and " "co \-r2.1 f.c"
.PP
retrieve the latest revision numbered
.RI 2. x
and the revision 2.1,
respectively.
.B co
without a revision number selects
the latest revision on the
.IR trunk ,
i.e. the highest
revision with a number consisting of two fields. Numbers with more than two
fields are needed for branches.
For example, to start a branch at revision 1.3, invoke
.IP
.B "ci \-r1.3.1 f.c"
.LP
This command starts a branch numbered 1 at revision 1.3, and assigns
the number 1.3.1.1 to the new revision. For more information about
branches, see
.BR rcsfile (5).
.SS "Automatic Identification"
\*r can put special strings for identification into your source and object
code. To obtain such identification, place the marker
.IP
.B "$\&Id$"
.LP
into your text, for instance inside a comment.
\*r will replace this marker with a string of the form
.IP
.BI $\&Id: " filename revision date time author state " $
.LP
With such a marker on the first page of each module, you can
always see with which revision you are working.
\*r keeps the markers up to date automatically.
To propagate the markers into your object code, simply put
them into literal character strings. In C, this is done as follows:
.IP
.ft 3
static char rcsid[] = \&"$\&Id$\&";
.ft
.LP
The command
.B ident
extracts such markers from any file, even object code
and dumps.
Thus,
.B ident
lets you find out
which revisions of which modules were used in a given program.
.PP
You may also find it useful to put the marker
.B $\&Log$
into your text, inside a comment. This marker accumulates
the log messages that are requested during check-in.
Thus, you can maintain the complete history of your file directly inside it.
There are several additional identification markers; see
.BR co (1)
for
details.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View file

@ -0,0 +1,7 @@
PROG= rcsclean
SRCS= rcsclean.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

View file

@ -0,0 +1,177 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsclean.1,v 1.8 1991/11/03 01:09:19 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSCLEAN 1 \*(Dt GNU
.SH NAME
rcsclean \- clean up working files
.SH SYNOPSIS
.B rcsclean
.RI [ options "] [ " file " .\|.\|. ]"
.SH DESCRIPTION
.B rcsclean
removes working files that were checked out and never modified.
For each
.I file
given,
.B rcsclean
compares the working file and a revision in the corresponding
\*r file. If it finds a difference, it does nothing.
Otherwise, it first unlocks the revision if the
.B \-u
option is given,
and then removes the working file
unless the working file is writable and the revision is locked.
It logs its actions by outputting the corresponding
.B "rcs \-u"
and
.B "rm \-f"
commands on the standard output.
.PP
If no
.I file
is given, all working files in the current directory are cleaned.
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
The number of the revision to which the working file is compared
may be attached to any of the options
.BR \-n ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
If no revision number is specified, then if the
.B \-u
option is given and the caller has one revision locked,
.B rcsclean
uses that revision; otherwise
.B rcsclean
uses the latest revision on the default branch, normally the root.
.PP
.B rcsclean
is useful for
.B clean
targets in Makefiles.
See also
.BR rcsdiff (1),
which prints out the differences,
and
.BR ci (1),
which
normally asks whether to check in a file
if it was not changed.
.SH OPTIONS
.TP
.BI \-k subst
Use
.I subst
style keyword substitution when retrieving the revision for comparison.
See
.BR co (1)
for details.
.TP
.BR \-n [\f2rev\fP]
Do not actually remove any files or unlock any revisions.
Using this option will tell you what
.B rcsclean
would do without actually doing it.
.TP
.BR \-q [\f2rev\fP]
Do not log the actions taken on standard output.
.TP
.BR \-r [\f2rev\fP]
This option has no effect other than specifying the revision for comparison.
.TP
.BR \-u [\f2rev\fP]
Unlock the revision if it is locked and no difference is found.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.SH EXAMPLES
.LP
.RS
.ft 3
rcsclean *.c *.h
.ft
.RE
.LP
removes all working files ending in
.B .c
or
.B .h
that were not changed
since their checkout.
.LP
.RS
.ft 3
rcsclean
.ft
.RE
.LP
removes all working files in the current directory
that were not changed since their checkout.
.SH FILES
.B rcsclean
accesses files much as
.BR ci (1)
does.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
A backslash escapes spaces within an option.
The
.B \s-1RCSINIT\s0
options are prepended to the argument lists of most \*r commands.
Useful
.B \s-1RCSINIT\s0
options include
.BR \-q ,
.BR \-V ,
and
.BR \-x .
.SH DIAGNOSTICS
The exit status is zero if and only if all operations were successful.
Missing working files and \*r files are silently ignored.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
At least one
.I file
must be given in older Unix versions that
do not provide the needed directory scanning operations.
.br

View file

@ -0,0 +1,297 @@
/* rcsclean - clean up working files */
/* Copyright 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
#if has_dirent
static int get_directory P((char const*,char***));
#endif
static int unlock P((struct hshentry *));
static void cleanup P((void));
static RILE *workptr;
static int exitstatus;
mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 eggert Exp $")
{
static char const usage[] =
"\nrcsclean: usage: rcsclean [-ksubst] [-{nqru}[rev]] [-Vn] [-xsuffixes] [file ...]";
static struct buf revision;
char *a, **newargv;
char const *rev, *p;
int changelock, expmode, perform, unlocked, unlockflag, waslocked;
struct hshentries *deltas;
struct hshentry *delta;
struct stat workstat;
setrid();
expmode = -1;
rev = nil;
suffixes = X_DEFAULT;
perform = true;
unlockflag = false;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
for (;;) {
if (--argc <= 0) {
# if has_dirent
argc = get_directory(".", &newargv);
argv = newargv;
break;
# else
faterror("no file names specified");
# endif
}
a = *++argv;
if (*a++ != '-')
break;
switch (*a++) {
case 'k':
if (0 <= expmode)
redefined('k');
if ((expmode = str2expmode(a)) < 0)
goto unknown;
break;
case 'n':
perform = false;
goto handle_revision;
case 'q':
quietflag = true;
/* fall into */
case 'r':
handle_revision:
if (*a) {
if (rev)
warn("redefinition of revision number");
rev = a;
}
break;
case 'u':
unlockflag = true;
goto handle_revision;
case 'V':
setRCSversion(*argv);
break;
case 'x':
suffixes = a;
break;
default:
unknown:
faterror("unknown option: %s%s", *argv, usage);
}
}
do {
ffree();
if (!(
0 < pairfilenames(
argc, argv,
unlockflag&perform ? rcswriteopen : rcsreadopen,
true, true
) &&
(workptr = Iopen(workfilename,FOPEN_R_WORK,&workstat))
))
continue;
gettree();
p = 0;
if (rev) {
if (!fexpandsym(rev, &revision, workptr))
continue;
p = revision.string;
} else if (Head)
switch (unlockflag ? findlock(false,&delta) : 0) {
default:
continue;
case 0:
p = Dbranch ? Dbranch : "";
break;
case 1:
p = delta->num;
break;
}
delta = 0;
deltas = 0; /* Keep lint happy. */
if (p && !(delta = genrevs(p,(char*)0,(char*)0,(char*)0,&deltas)))
continue;
waslocked = delta && delta->lockedby;
locker_expansion = unlock(delta);
unlocked = locker_expansion & unlockflag;
changelock = unlocked & perform;
if (unlocked<waslocked && workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH))
continue;
if (!dorewrite(unlockflag, changelock))
continue;
if (0 <= expmode)
Expand = expmode;
else if (
waslocked &&
Expand == KEYVAL_EXPAND &&
WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
)
Expand = KEYVALLOCK_EXPAND;
getdesc(false);
if (
!delta ? workstat.st_size!=0 :
0 < rcsfcmp(
workptr, &workstat,
buildrevision(deltas, delta, (FILE*)0, false),
delta
)
)
continue;
if (quietflag < unlocked)
aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSfilename);
if_advise_access(changelock && deltas->first != delta,
finptr, MADV_SEQUENTIAL
);
if (!donerewrite(changelock))
continue;
if (!quietflag)
aprintf(stdout, "rm -f %s\n", workfilename);
Izclose(&workptr);
if (perform && un_link(workfilename) != 0)
eerror(workfilename);
} while (cleanup(), ++argv, 0 < --argc);
tempunlink();
if (!quietflag)
Ofclose(stdout);
exitmain(exitstatus);
}
static void
cleanup()
{
if (nerror) exitstatus = EXIT_FAILURE;
Izclose(&finptr);
Izclose(&workptr);
Ozclose(&fcopy);
Ozclose(&frewrite);
dirtempunlink();
}
#if lint
# define exiterr rcscleanExit
#endif
exiting void
exiterr()
{
dirtempunlink();
tempunlink();
_exit(EXIT_FAILURE);
}
static int
unlock(delta)
struct hshentry *delta;
{
register struct lock **al, *l;
if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0)
for (al = &Locks; (l = *al); al = &l->nextlock)
if (l->delta == delta) {
*al = l->nextlock;
delta->lockedby = 0;
return true;
}
return false;
}
#if has_dirent
static int
get_directory(dirname, aargv)
char const *dirname;
char ***aargv;
/*
* Put a vector of all DIRNAME's directory entries names into *AARGV.
* Ignore names of RCS files.
* Yield the number of entries found. Terminate the vector with 0.
* Allocate the storage for the vector and entry names.
* Do not sort the names. Do not include '.' and '..'.
*/
{
int i, entries = 0, entries_max = 64;
size_t chars = 0, chars_max = 1024;
size_t *offset = tnalloc(size_t, entries_max);
char *a = tnalloc(char, chars_max), **p;
DIR *d;
struct dirent *e;
if (!(d = opendir(dirname)))
efaterror(dirname);
while ((errno = 0, e = readdir(d))) {
char const *en = e->d_name;
size_t s = strlen(en) + 1;
if (en[0]=='.' && (!en[1] || en[1]=='.' && !en[2]))
continue;
if (rcssuffix(en))
continue;
while (chars_max < s + chars)
a = trealloc(char, a, chars_max<<=1);
if (entries == entries_max)
offset = trealloc(size_t, offset, entries_max<<=1);
offset[entries++] = chars;
VOID strcpy(a+chars, en);
chars += s;
}
if (errno || closedir(d) != 0)
efaterror(dirname);
if (chars)
a = trealloc(char, a, chars);
else
tfree(a);
*aargv = p = tnalloc(char*, entries+1);
for (i=0; i<entries; i++)
*p++ = a + offset[i];
*p = 0;
tfree(offset);
return entries;
}
#endif

View file

@ -0,0 +1,7 @@
PROG= rcsdiff
SRCS= rcsdiff.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

View file

@ -0,0 +1,152 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsdiff.1,v 5.3 1991/04/21 12:00:46 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSDIFF 1 \*(Dt GNU
.SH NAME
rcsdiff \- compare RCS revisions
.SH SYNOPSIS
.B rcsdiff
[
.BI \-k subst
] [
.B \-q
] [
.BI \-r rev1
[
.BI \-r rev2
] ] [
.BI \-V n
] [
.BI \-x suffixes
] [
.I "diff options"
]
.I "file .\|.\|."
.SH DESCRIPTION
.B rcsdiff
runs
.BR diff (1)
to compare two revisions of each \*r file given.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
The option
.B \-q
suppresses diagnostic output.
Zero, one, or two revisions may be specified with
.BR \-r .
The option
.BI \-k subst
affects keyword substitution when extracting
revisions, as described in
.BR co (1);
for example,
.B "\-kk\ \-r1.1\ \-r1.2"
ignores differences in keyword values when comparing revisions
.B 1.1
and
.BR 1.2 .
To avoid excess output from locker name substitution,
.B \-kkvl
is assumed if (1) at most one revision option is given,
(2) no
.B \-k
option is given, (3)
.B \-kkv
is the default keyword substitution, and
(4) the working file's mode would be produced by
.BR "co\ \-l".
See
.BR co (1)
for details
about
.B \-V
and
.BR \-x .
Otherwise, all options of
.BR diff (1)
that apply to regular files are accepted, with the same meaning as for
.BR diff .
.PP
If both
.I rev1
and
.I rev2
are omitted,
.B rcsdiff
compares the latest revision on the
default branch (by default the trunk)
with the contents of the corresponding working file. This is useful
for determining what you changed since the last checkin.
.PP
If
.I rev1
is given, but
.I rev2
is omitted,
.B rcsdiff
compares revision
.I rev1
of the \*r file with
the contents of the corresponding working file.
.PP
If both
.I rev1
and
.I rev2
are given,
.B rcsdiff
compares revisions
.I rev1
and
.I rev2
of the \*r file.
.PP
Both
.I rev1
and
.I rev2
may be given numerically or symbolically.
.SH EXAMPLE
The command
.LP
.B " rcsdiff f.c"
.LP
compares the latest revision on the default branch of the \*r file
to the contents of the working file
.BR f.c .
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
Exit status is 0 for no differences during any comparison,
1 for some differences, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), rcsmerge(1), rlog(1)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View file

@ -0,0 +1,422 @@
/*
* RCS rcsdiff operation
*/
/*****************************************************************************
* generate difference between RCS revisions
*****************************************************************************
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsdiff.c,v $
* Revision 5.10 1991/10/07 17:32:46 eggert
* Remove lint.
*
* Revision 5.9 1991/08/19 03:13:55 eggert
* Add RCSINIT, -r$. Tune.
*
* Revision 5.8 1991/04/21 11:58:21 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.7 1990/12/13 06:54:07 eggert
* GNU diff 1.15 has -u.
*
* Revision 5.6 1990/11/01 05:03:39 eggert
* Remove unneeded setid check.
*
* Revision 5.5 1990/10/04 06:30:19 eggert
* Accumulate exit status across files.
*
* Revision 5.4 1990/09/27 01:31:43 eggert
* Yield 1, not EXIT_FAILURE, when diffs are found.
*
* Revision 5.3 1990/09/11 02:41:11 eggert
* Simplify -kkvl test.
*
* Revision 5.2 1990/09/04 17:07:19 eggert
* Diff's argv was too small by 1.
*
* Revision 5.1 1990/08/29 07:13:55 eggert
* Add -kkvl.
*
* Revision 5.0 1990/08/22 08:12:46 eggert
* Add -k, -V. Don't use access(). Add setuid support.
* Remove compile-time limits; use malloc instead.
* Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options.
* Add GNU diff's flags. Make lock and temp files faster and safer.
* Ansify and Posixate.
*
* Revision 4.6 89/05/01 15:12:27 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:12:41 eggert
* Use execv(), not system(); yield exit status like diff(1)s; allow cc -R.
*
* Revision 4.4 87/12/18 11:37:46 narten
* changes Jay Lepreau made in the 4.3 BSD version, to add support for
* "-i", "-w", and "-t" flags and to permit flags to be bundled together,
* merged in.
*
* Revision 4.3 87/10/18 10:31:42 narten
* Updating version numbers. Changes relative to 1.1 actually
* relative to 4.1
*
* Revision 1.3 87/09/24 13:59:21 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:15 jenkins
* Port to suns
*
* Revision 4.1 83/05/03 22:13:19 wft
* Added default branch, option -q, exit status like diff.
* Added fterror() to replace faterror().
*
* Revision 3.6 83/01/15 17:52:40 wft
* Expanded mainprogram to handle multiple RCS files.
*
* Revision 3.5 83/01/06 09:33:45 wft
* Fixed passing of -c (context) option to diff.
*
* Revision 3.4 82/12/24 15:28:38 wft
* Added call to catchsig().
*
* Revision 3.3 82/12/10 16:08:17 wft
* Corrected checking of return code from diff; improved error msgs.
*
* Revision 3.2 82/12/04 13:20:09 wft
* replaced getdelta() with gettree(). Changed diagnostics.
*
* Revision 3.1 82/11/28 19:25:04 wft
* Initial revision.
*
*/
#include "rcsbase.h"
#if DIFF_L
static char const *setup_label P((struct buf*,char const*,char const[datesize]));
#endif
static void cleanup P((void));
static int exitstatus;
static RILE *workptr;
static struct stat workstat;
mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
{
static char const cmdusage[] =
"\nrcsdiff usage: rcsdiff [-q] [-rrev1 [-rrev2]] [-Vn] [diff options] file ...";
int revnums; /* counter for revision numbers given */
char const *rev1, *rev2; /* revision numbers from command line */
char const *xrev1, *xrev2; /* expanded revision numbers */
char const *expandarg, *lexpandarg, *versionarg;
#if DIFF_L
static struct buf labelbuf[2];
int file_labels;
char const **diff_label1, **diff_label2;
char date2[datesize];
#endif
char const *cov[9];
char const **diffv, **diffp; /* argv for subsidiary diff */
char const **pp, *p, *diffvstr;
struct buf commarg;
struct buf numericrev; /* expanded revision number */
struct hshentries *gendeltas; /* deltas to be generated */
struct hshentry * target;
char *a, *dcp, **newargv;
register c;
exitstatus = DIFF_SUCCESS;
bufautobegin(&commarg);
bufautobegin(&numericrev);
revnums = 0;
rev1 = rev2 = xrev2 = nil;
#if DIFF_L
file_labels = 0;
#endif
expandarg = versionarg = 0;
suffixes = X_DEFAULT;
/* Room for args + 2 i/o [+ 2 labels] + 1 file + 1 trailing null. */
diffp = diffv = tnalloc(char const*, argc + 4 + 2*DIFF_L);
*diffp++ = nil;
*diffp++ = nil;
*diffp++ = DIFF;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
dcp = a;
while (c = *a++) switch (c) {
case 'r':
switch (++revnums) {
case 1: rev1=a; break;
case 2: rev2=a; break;
default: faterror("too many revision numbers");
}
goto option_handled;
#if DIFF_L
case 'L':
if (++file_labels == 2)
faterror("too many -L options");
/* fall into */
#endif
case 'C': case 'D': case 'F': case 'I':
*dcp++ = c;
if (*a)
do *dcp++ = *a++;
while (*a);
else {
if (!--argc)
faterror("-%c needs following argument%s",
c, cmdusage
);
*diffp++ = *argv++;
}
break;
case 'B': case 'H': case 'T':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'h': case 'i': case 'n': case 'p':
case 't': case 'u': case 'w':
*dcp++ = c;
break;
case 'q':
quietflag=true;
break;
case 'x':
suffixes = *argv + 2;
goto option_handled;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
goto option_handled;
case 'k':
expandarg = *argv;
if (0 <= str2expmode(expandarg+2))
goto option_handled;
/* fall into */
default:
faterror("unknown option: %s%s", *argv, cmdusage);
};
option_handled:
if (dcp != *argv+1) {
*dcp = 0;
*diffp++ = *argv;
}
} /* end of option processing */
if (argc<1) faterror("no input file%s", cmdusage);
for (pp = diffv+3, c = 0; pp<diffp; )
c += strlen(*pp++) + 1;
diffvstr = a = tnalloc(char, c + 1);
for (pp = diffv+3; pp<diffp; ) {
p = *pp++;
*a++ = ' ';
while ((*a = *p++))
a++;
}
*a = 0;
#if DIFF_L
diff_label1 = diff_label2 = nil;
if (file_labels < 2) {
if (!file_labels)
diff_label1 = diffp++;
diff_label2 = diffp++;
}
#endif
diffp[2] = nil;
cov[0] = 0;
cov[2] = CO;
cov[3] = "-q";
/* now handle all filenames */
do {
ffree();
if (pairfilenames(argc, argv, rcsreadopen, true, false) <= 0)
continue;
diagnose("===================================================================\nRCS file: %s\n",RCSfilename);
if (!rev2) {
/* Make sure work file is readable, and get its status. */
if (!(workptr = Iopen(workfilename,FOPEN_R_WORK,&workstat))) {
eerror(workfilename);
continue;
}
}
gettree(); /* reads in the delta tree */
if (Head==nil) {
error("no revisions present");
continue;
}
if (revnums==0 || !*rev1)
rev1 = Dbranch ? Dbranch : Head->num;
if (!fexpandsym(rev1, &numericrev, workptr)) continue;
if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue;
xrev1=target->num;
#if DIFF_L
if (diff_label1)
*diff_label1 = setup_label(&labelbuf[0], target->num, target->date);
#endif
lexpandarg = expandarg;
if (revnums==2) {
if (!fexpandsym(
*rev2 ? rev2 : Dbranch ? Dbranch : Head->num,
&numericrev,
workptr
))
continue;
if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue;
xrev2=target->num;
} else if (
target->lockedby
&& !lexpandarg
&& Expand == KEYVAL_EXPAND
&& WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
)
lexpandarg = "-kkvl";
Izclose(&workptr);
#if DIFF_L
if (diff_label2)
if (revnums == 2)
*diff_label2 = setup_label(&labelbuf[1], target->num, target->date);
else {
time2date(workstat.st_mtime, date2);
*diff_label2 = setup_label(&labelbuf[1], workfilename, date2);
}
#endif
diagnose("retrieving revision %s\n", xrev1);
bufscpy(&commarg, "-p");
bufscat(&commarg, xrev1);
cov[1] = diffp[0] = maketemp(0);
pp = &cov[4];
*pp++ = commarg.string;
if (lexpandarg)
*pp++ = lexpandarg;
if (versionarg)
*pp++ = versionarg;
*pp++ = RCSfilename;
*pp = 0;
if (runv(cov)) {
error("co failed");
continue;
}
if (!rev2) {
diffp[1] = workfilename;
if (workfilename[0] == '+') {
/* Some diffs have options with leading '+'. */
char *dp = ftnalloc(char, strlen(workfilename)+3);
diffp[1] = dp;
*dp++ = '.';
*dp++ = SLASH;
VOID strcpy(dp, workfilename);
}
} else {
diagnose("retrieving revision %s\n",xrev2);
bufscpy(&commarg, "-p");
bufscat(&commarg, xrev2);
cov[1] = diffp[1] = maketemp(1);
cov[4] = commarg.string;
if (runv(cov)) {
error("co failed");
continue;
}
}
if (!rev2)
diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workfilename);
else
diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2);
switch (runv(diffv)) {
case DIFF_SUCCESS:
break;
case DIFF_FAILURE:
if (exitstatus == DIFF_SUCCESS)
exitstatus = DIFF_FAILURE;
break;
default:
error("diff failed");
}
} while (cleanup(),
++argv, --argc >=1);
tempunlink();
exitmain(exitstatus);
}
static void
cleanup()
{
if (nerror) exitstatus = DIFF_TROUBLE;
Izclose(&finptr);
Izclose(&workptr);
}
#if lint
# define exiterr rdiffExit
#endif
exiting void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}
#if DIFF_L
static char const *
setup_label(b, name, date)
struct buf *b;
char const *name;
char const date[datesize];
{
char *p;
size_t l = strlen(name) + 3;
bufalloc(b, l+datesize);
p = b->string;
VOID sprintf(p, "-L%s\t", name);
VOID date2str(date, p+l);
return p;
}
#endif

View file

@ -0,0 +1,7 @@
# Do nothing for the following
obj clean cleandir depend rcsfreeze all:
@echo No need to make $@ for rcsfreeze\; ignored
install:
install -c -o bin -g bin -m 555 rcsfreeze.sh /usr/bin/rcsfreeze
install -c -o bin -g bin -m 444 rcsfreeze.1 /usr/share/man/man1

View file

@ -0,0 +1,68 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsfreeze.1,v 4.4 1990/11/13 15:43:42 hammer Exp $
.ds r \s-1RCS\s0
.TH RCSFREEZE 1 \*(Dt GNU
.SH NAME
rcsfreeze \- freeze a configuration of sources checked in under RCS
.SH SYNOPSIS
.B rcsfreeze
.RI [ "name" ]
.SH DESCRIPTION
.B rcsfreeze
assigns a symbolic revision
number to a set of \*r files that form a valid configuration.
.PP
The idea is to run
.B rcsfreeze
each time a new version is checked
in. A unique symbolic name (\c
.BI C_ number,
where
.I number
is increased each time
.B rcsfreeze
is run) is then assigned to the most
recent revision of each \*r file of the main trunk.
.PP
An optional
.I name
argument to
.B rcsfreeze
gives a symbolic name to the configuration.
The unique identifier is still generated
and is listed in the log file but it will not appear as
part of the symbolic revision name in the actual \*r files.
.PP
A log message is requested from the user for future reference.
.PP
The shell script works only on all \*r files at one time.
All changed files must be checked in already.
Run
.IR rcsclean (1)
first and see whether any sources remain in the current directory.
.SH FILES
.TP
.B RCS/.rcsfreeze.ver
version number
.TP
.B RCS/.rcsfreeze.log
log messages, most recent first
.SH AUTHOR
Stephan v. Bechtolsheim
.SH "SEE ALSO"
co(1), rcs(1), rcsclean(1), rlog(1)
.SH BUGS
.B rcsfreeze
does not check whether any sources are checked out and modified.
.PP
Although both source file names and RCS file names are accepted,
they are not paired as usual with RCS commands.
.PP
Error checking is rudimentary.
.PP
.B rcsfreeze
is just an optional example shell script, and should not be taken too seriously.
See \s-1CVS\s0 for a more complete solution.

View file

@ -0,0 +1,100 @@
#! /bin/sh
# rcsfreeze - assign a symbolic revision number to a configuration of RCS files
# $Id: rcsfreeze.sh,v 4.4 1991/04/21 11:58:24 eggert Exp $
# The idea is to run rcsfreeze each time a new version is checked
# in. A unique symbolic revision number (C_[number], where number
# is increased each time rcsfreeze is run) is then assigned to the most
# recent revision of each RCS file of the main trunk.
#
# If the command is invoked with an argument, then this
# argument is used as the symbolic name to freeze a configuration.
# The unique identifier is still generated
# and is listed in the log file but it will not appear as
# part of the symbolic revision name in the actual RCS file.
#
# A log message is requested from the user which is saved for future
# references.
#
# The shell script works only on all RCS files at one time.
# It is important that all changed files are checked in (there are
# no precautions against any error in this respect).
# file names:
# {RCS/}.rcsfreeze.ver version number
# {RCS/}.rscfreeze.log log messages, most recent first
PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:/usr/ucb:$PATH
export PATH
DATE=`date` || exit
# Check whether we have an RCS subdirectory, so we can have the right
# prefix for our paths.
if [ -d RCS ]
then RCSDIR=RCS/
else RCSDIR=
fi
# Version number stuff, log message file
VERSIONFILE=${RCSDIR}.rcsfreeze.ver
LOGFILE=${RCSDIR}.rcsfreeze.log
# Initialize, rcsfreeze never run before in the current directory
[ -r $VERSIONFILE ] || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit
# Get Version number, increase it, write back to file.
VERSIONNUMBER=`cat $VERSIONFILE` &&
VERSIONNUMBER=`expr $VERSIONNUMBER + 1` &&
echo $VERSIONNUMBER >$VERSIONFILE || exit
# Symbolic Revision Number
SYMREV=C_$VERSIONNUMBER
# Allow the user to give a meaningful symbolic name to the revision.
SYMREVNAME=${1-$SYMREV}
echo >&2 "rcsfreeze: symbolic revision number computed: \"${SYMREV}\"
rcsfreeze: symbolic revision number used: \"${SYMREVNAME}\"
rcsfreeze: the two differ only when rcsfreeze invoked with argument
rcsfreeze: give log message, summarizing changes (end with EOF or single '.')" \
|| exit
# Stamp the logfile. Because we order the logfile the most recent
# first we will have to save everything right now in a temporary file.
TMPLOG=/tmp/rcsfrz$$
trap 'rm -f $TMPLOG; exit 1' 1 2 13 15
# Now ask for a log message, continously add to the log file
(
echo "Version: $SYMREVNAME($SYMREV), Date: $DATE
-----------" || exit
while read MESS
do
case $MESS in
.) break
esac
echo " $MESS" || exit
done
echo "-----------
" &&
cat $LOGFILE
) >$TMPLOG &&
# combine old and new logfiles
cp $TMPLOG $LOGFILE &&
rm -f $TMPLOG || exit
trap 1 2 13 15
# Now the real work begins by assigning a symbolic revision number
# to each rcs file. Take the most recent version of the main trunk.
status=
for FILE in ${RCSDIR}*
do
# get the revision number of the most recent revision
HEAD=`rlog -h $FILE` &&
REV=`echo "$HEAD" | sed -n 's/^head:[ ]*//p'` &&
# assign symbolic name to it.
echo >&2 "rcsfreeze: $REV $FILE" &&
rcs -q -n$SYMREVNAME:$REV $FILE || status=$?
done
exit $status

View file

@ -0,0 +1,7 @@
PROG= rcsmerge
SRCS= rcsmerge.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

View file

@ -0,0 +1,140 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rcsmerge.1,v 5.3 1991/08/19 03:13:55 eggert Exp $
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSMERGE 1 \*(Dt GNU
.SH NAME
rcsmerge \- merge RCS revisions
.SH SYNOPSIS
.B rcsmerge
.RI [ options ] " file"
.SH DESCRIPTION
.B rcsmerge
incorporates the changes between two revisions
of an \*r file into the corresponding working file.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
At least one revision must be specified with one of the options
described below, usually
.BR \-r .
At most two revisions may be specified.
If only one revision is specified, the latest
revision on the default branch (normally the highest branch on the trunk)
is assumed for the second revision.
Revisions may be specified numerically or symbolically.
.PP
.B rcsmerge
prints a warning if there are overlaps, and delimits
the overlapping regions as explained in
.BR merge (1).
The command is useful for incorporating changes into a checked-out revision.
.SH OPTIONS
.TP
.BI \-k subst
Use
.I subst
style keyword substitution.
See
.BR co (1)
for details.
For example,
.B "\-kk\ \-r1.1\ \-r1.2"
ignores differences in keyword values when merging the changes from
.B 1.1
to
.BR 1.2 .
.TP
.BR \-p [\f2rev\fP]
Send the result to standard output instead of overwriting the working file.
.TP
.BR \-q [\f2rev\fP]
Run quietly; do not print diagnostics.
.TP
.BR \-r [\f2rev\fP]
Merge with respect to revision
.IR rev .
Here an empty
.I rev
stands for the latest revision on the default branch, normally the head.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.SH EXAMPLES
Suppose you have released revision 2.8 of
.BR f.c .
Assume
furthermore that after you complete an unreleased revision 3.4, you receive
updates to release 2.8 from someone else.
To combine the updates to 2.8 and your changes between 2.8 and 3.4,
put the updates to 2.8 into file f.c and execute
.LP
.B " rcsmerge \-p \-r2.8 \-r3.4 f.c >f.merged.c"
.PP
Then examine
.BR f.merged.c .
Alternatively, if you want to save the updates to 2.8 in the \*r file,
check them in as revision 2.8.1.1 and execute
.BR "co \-j":
.LP
.B " ci \-r2.8.1.1 f.c"
.br
.B " co \-r3.4 \-j2.8:2.8.1.1 f.c"
.PP
As another example, the following command undoes the changes
between revision 2.4 and 2.8 in your currently checked out revision
in
.BR f.c .
.LP
.B " rcsmerge \-r2.8 \-r2.4 f.c"
.PP
Note the order of the arguments, and that
.B f.c
will be
overwritten.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsintro(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View file

@ -0,0 +1,252 @@
/*
* rcsmerge operation
*/
/*****************************************************************************
* join 2 revisions with respect to a third
*****************************************************************************
*/
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/* $Log: rcsmerge.c,v $
* Revision 5.7 1991/11/20 17:58:09 eggert
* Don't Iopen(f, "r+"); it's not portable.
*
* Revision 5.6 1991/08/19 03:13:55 eggert
* Add -r$. Tune.
*
* Revision 5.5 1991/04/21 11:58:27 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.4 1991/02/25 07:12:43 eggert
* Merging a revision to itself is no longer an error.
*
* Revision 5.3 1990/11/01 05:03:50 eggert
* Remove unneeded setid check.
*
* Revision 5.2 1990/09/04 08:02:28 eggert
* Check for I/O error when reading working file.
*
* Revision 5.1 1990/08/29 07:14:04 eggert
* Add -q. Pass -L options to merge.
*
* Revision 5.0 1990/08/22 08:13:41 eggert
* Propagate merge's exit status.
* Remove compile-time limits; use malloc instead.
* Make lock and temp files faster and safer. Ansify and Posixate. Add -V.
* Don't use access(). Tune.
*
* Revision 4.5 89/05/01 15:13:16 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 88/08/09 19:13:13 eggert
* Beware merging into a readonly file.
* Beware merging a revision to itself (no change).
* Use execv(), not system(); yield exit status like diff(1)'s.
*
* Revision 4.3 87/10/18 10:38:02 narten
* Updating version numbers. Changes relative to version 1.1
* actually relative to 4.1
*
* Revision 1.3 87/09/24 14:00:31 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:36 jenkins
* Port to suns
*
* Revision 4.1 83/03/28 11:14:57 wft
* Added handling of default branch.
*
* Revision 3.3 82/12/24 15:29:00 wft
* Added call to catchsig().
*
* Revision 3.2 82/12/10 21:32:02 wft
* Replaced getdelta() with gettree(); improved error messages.
*
* Revision 3.1 82/11/28 19:27:44 wft
* Initial revision.
*
*/
#include "rcsbase.h"
static char const co[] = CO;
mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 eggert Exp $")
{
static char const cmdusage[] =
"\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] [-p] [-Vn] file";
static char const quietarg[] = "-q";
register int i;
char *a, **newargv;
char const *arg[3];
char const *rev[2]; /*revision numbers*/
char const *expandarg, *versionarg;
int tostdout;
int status;
RILE *workptr;
struct buf commarg;
struct buf numericrev; /* holds expanded revision number */
struct hshentries *gendeltas; /* deltas to be generated */
struct hshentry * target;
bufautobegin(&commarg);
bufautobegin(&numericrev);
rev[0] = rev[1] = nil;
status = 0; /* Keep lint happy. */
tostdout = false;
expandarg = versionarg = quietarg; /* i.e. a no-op */
suffixes = X_DEFAULT;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
switch (*a++) {
case 'p':
tostdout=true;
goto revno;
case 'q':
quietflag = true;
revno:
if (!*a)
break;
/* falls into -r */
case 'r':
if (!rev[0])
rev[0] = a;
else if (!rev[1])
rev[1] = a;
else
faterror("too many revision numbers");
break;
case 'x':
suffixes = a;
break;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
break;
case 'k':
expandarg = *argv;
if (0 <= str2expmode(expandarg+2))
break;
/* fall into */
default:
faterror("unknown option: %s%s", *argv, cmdusage);
};
} /* end of option processing */
if (argc<1) faterror("no input file%s", cmdusage);
if (!rev[0]) faterror("no base revision number given");
/* now handle all filenames */
if (0 < pairfilenames(argc, argv, rcsreadopen, true, false)) {
if (argc>2 || (argc==2&&argv[1]!=nil))
warn("too many arguments");
diagnose("RCS file: %s\n", RCSfilename);
if (!(workptr = Iopen(workfilename,
FOPEN_R_WORK,
(struct stat*)0
)))
efaterror(workfilename);
gettree(); /* reads in the delta tree */
if (Head==nil) faterror("no revisions present");
if (!*rev[0])
rev[0] = Dbranch ? Dbranch : Head->num;
if (!fexpandsym(rev[0], &numericrev, workptr))
goto end;
if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end;
rev[0] = target->num;
if (!rev[1] || !*rev[1])
rev[1] = Dbranch ? Dbranch : Head->num;
if (!fexpandsym(rev[1], &numericrev, workptr))
goto end;
if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end;
rev[1] = target->num;
if (strcmp(rev[0],rev[1]) == 0) {
if (tostdout) {
FILE *o;
# if text_equals_binary_stdio || text_work_stdio
o = stdout;
# else
if (!(o=fdopen(STDOUT_FILENO,FOPEN_W_WORK)))
efaterror("stdout");
# endif
fastcopy(workptr,o);
Ofclose(o);
}
goto end;
}
Izclose(&workptr);
for (i=0; i<2; i++) {
diagnose("retrieving revision %s\n", rev[i]);
bufscpy(&commarg, "-p");
bufscat(&commarg, rev[i]);
if (run(
(char*)0,
/* Do not collide with merger.c maketemp(). */
arg[i+1] = maketemp(i+3),
co, quietarg, commarg.string, expandarg,
versionarg, RCSfilename, (char*)0
))
faterror("co failed");
}
diagnose("Merging differences between %s and %s into %s%s\n",
rev[0], rev[1], workfilename,
tostdout?"; result to stdout":"");
arg[0] = rev[0] = workfilename;
status = merge(tostdout, rev, arg);
}
end:
Izclose(&workptr);
tempunlink();
exitmain(nerror ? DIFF_TROUBLE : status);
}
#if lint
# define exiterr rmergeExit
#endif
exiting void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}

397
gnu/usr.bin/rcs/rcstest Executable file
View file

@ -0,0 +1,397 @@
#!/bin/sh
# Test RCS's functions.
# The RCS commands are searched for in the PATH as usual;
# to test the working directory's commands, prepend . to your PATH.
# Test RCS by creating files RCS/a.* and RCS/a.c.
# If all goes well, output nothing, and remove the temporary files.
# Otherwise, send a message to standard output.
# Exit status is 0 if OK, 1 if an RCS bug is found, and 2 if scaffolding fails.
# With the -v option, output more debugging info.
# If diff outputs `No differences encountered' when comparing identical files,
# then rcstest may also output these noise lines; ignore them.
# The current directory and ./RCS must be readable, writable, and searchable.
# $Id: rcstest,v 5.8 1991/11/20 17:58:10 eggert Exp $
# Copyright 1990, 1991 by Paul Eggert
# Distributed under license by the Free Software Foundation, Inc.
#
# This file is part of RCS.
#
# RCS is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# RCS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with RCS; see the file COPYING. If not, write to
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Report problems and direct all questions to:
#
# rcs-bugs@cs.purdue.edu
RCSINIT=-x
export RCSINIT
SLASH=/
RCSfile=RCS${SLASH}a.c
RCS_alt=RCS${SLASH}a.d
lockfile=RCS${SLASH}a._
case $1 in
-v) q=; set -x;;
'') q=-q;;
*) echo >&2 "$0: usage: $0 [-v]"; exit 2
esac
test -d RCS || {
echo >&2 "$0: RCS: not a directory; please \`mkdir RCS' first."
exit 1
}
rm -f a.* $RCSfile $RCS_alt $lockfile &&
echo 1.1 >a.11 &&
echo 1.1.1.1 >a.3x1 &&
echo 1.2 >a.12 || { echo "#initialization failed"; exit 2; }
case `diff -c a.11 a.3x1` in
*'! 1.1.1.1')
diff='diff -c';;
*)
echo "#warning: diff -c does not work, so diagnostics may be cryptic"
diff=diff
esac
rcs -i -L -ta.11 $q a.c &&
<$RCSfile || {
echo "#rcs -i -L failed; perhaps RCS is not properly installed."
exit 1
}
rlog a.c >/dev/null || { echo "#rlog failed on empty RCS file"; exit 1; }
rm -f $RCSfile || exit 2
cp a.11 a.c &&
ci -ta.11 -mm $q a.c &&
<$RCSfile &&
rcs -L $q a.c || { echo "#ci+rcs -L failed"; exit 1; }
test ! -f a.c || { echo "#ci did not remove working file"; exit 1; }
for l in '' '-l'
do
co $l $q a.c &&
test -f a.c || { echo '#co' $l did not create working file; exit 1; }
$diff a.11 a.c || { echo '#ci' followed by co $l is not a no-op; exit 1; }
done
cp a.12 a.c &&
ci -mm $q a.c &&
co $q a.c &&
$diff a.12 a.c || { echo "#ci+co failed"; exit 1; }
co -r1.1 $q a.c &&
$diff a.11 a.c || { echo "#can't retrieve first revision"; exit 1; }
rm -f a.c &&
cp a.3x1 a.c &&
ci -r1.1.1 -mm $q a.c &&
co -r1.1.1.1 $q a.c &&
$diff a.3x1 a.c || { echo "#branches failed"; exit 1; }
co -l $q a.c &&
ci -f -mm $q a.c &&
co -r1.3 $q a.c &&
$diff a.12 a.c || { echo "#(co -l; ci -f) failed"; exit 1; }
co -l $q a.c &&
echo 1.4 >a.c &&
ci -l -mm $q a.c &&
echo error >a.c &&
ci -mm $q a.c || { echo "#ci -l failed"; exit 1; }
co -l $q a.c &&
echo 1.5 >a.c &&
ci -u -mm $q a.c &&
<a.c || { echo "#ci -u didn't create a working file"; exit 1; }
rm -f a.c &&
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#ci -u didn't unlock the file"; exit 1; }
rm -f a.c &&
rcs -l $q a.c &&
co -u $q a.c || { echo "#rcs -l + co -u failed"; exit 1; }
rm -f a.c &&
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#co -u didn't unlock the file"; exit 1; }
rm -f a.c &&
cp a.11 a.c &&
co -f $q a.c || { echo "#co -f failed"; exit 1; }
$diff a.11 a.c >/dev/null && { echo "#co -f had no effect"; exit 1; }
co -p1.1 $q a.c >a.t &&
$diff a.11 a.t || { echo "#co -p failed"; exit 1; }
for n in n N
do
rm -f a.c &&
co -l $q a.c &&
echo $n >a.$n &&
cp a.$n a.c &&
ci -${n}n -mm $q a.c &&
co -rn $q a.c &&
$diff a.$n a.c || { echo "#ci -$n failed"; exit 1; }
done
case $LOGNAME in
?*) me=$LOGNAME;;
*)
case $USER in
?*) me=$USER;;
*)
me=`who am i` || exit 2
me=`echo "$me" | sed -e 's/ .*//' -e 's/.*!//'`
case $me in
'') echo >&2 "$0: cannot deduce user name"; exit 2
esac
esac
esac
date=`date -u 2>/dev/null` ||
date=`TZ=GMT0 date 2>/dev/null` ||
date=`TZ= date` || exit 2
set $date
case $2 in
Jan) m=01;; Feb) m=02;; Mar) m=03;; Apr) m=04;; May) m=05;; Jun) m=06;;
Jul) m=07;; Aug) m=08;; Sep) m=09;; Oct) m=10;; Nov) m=11;; Dec) m=12;;
*) echo >&2 "$0: $2: unknown month name"; exit 2
esac
case $3 in
?) d=0$3;;
*) d=$3
esac
case $6 in
[0-9][0-9][0-9][0-9]*) D=$6/$m/$d;;
*)
case $5 in
[0-9][0-9][0-9][0-9]*) D=$5/$m/$d;;
*) echo >&2 "$0: bad date format: $date"; exit 2
esac
esac
T=$4
case $PWD in
'') PWD=`pwd`
esac &&
co -l $q a.c &&
sed 's/@/$/g' >a.kv <<EOF
@Author: w @
@Date: $D $T @
@Header: $PWD$SLASH$RCSfile 2.1 $D $T w s @
@Id: a.c 2.1 $D $T w s @
@Locker: @
@Log: a.c @
* Revision 2.1 $D $T w
* m
*
@RCSfile: a.c @
@Revision: 2.1 @
@Source: $PWD$SLASH$RCSfile @
@State: s @
EOF
test $? = 0 &&
sed 's/:.*\$/$/' a.kv >a.k &&
sed -e 's/w s [$]/w s '"$me"' $/' -e 's/[$]Locker: /&'"$me/" a.kv >a.kvl &&
sed -e '/^\$/!d' -e 's/\$$/: old $/' a.k >a.o &&
sed -e 's/\$[^ ]*: //' -e 's/ \$//' a.kv >a.v &&
cp a.o a.c &&
ci -d"$date" -ss -ww -u2.1 -mm $q a.c &&
$diff a.kv a.c || { echo "#keyword expansion failed"; exit 1; }
co -p -ko $q a.c >a.oo &&
$diff a.o a.oo || { echo "#co -p -ko failed"; exit 1; }
cp a.kv a.o || exit 2
rcs -o2.1 $q a.c &&
rcs -l $q a.c &&
ci -k -u $q a.c &&
$diff a.kv a.c || { echo "#ci -k failed"; exit 1; }
sed '/^[^$]/d' a.kv >a.i &&
ident a.c >a.i1 &&
sed -e 1d -e 's/^[ ]*//' a.i1 >a.i2 &&
$diff a.i a.i2 || { echo "#ident failed"; exit 1; }
rcs -i $q a.c 2>/dev/null && { echo "#rcs -i permitted existing file"; exit 1; }
co -l $q a.c &&
echo 2.2 >a.c &&
ci -mm $q a.c &&
echo 1.1.1.2 >a.c &&
rcs -l1.1.1 $q a.c &&
ci -r1.1.1.2 -mm $q a.c &&
rcs -b1.1.1 $q a.c &&
test " `co -p $q a.c`" = ' 1.1.1.2' || { echo "#rcs -b1.1.1 failed"; exit 1; }
rcs -b $q a.c &&
test " `co -p $q a.c`" = ' 2.2' || { echo "#rcs -b failed"; exit 1; }
echo 2.3 >a.c || exit 2
rcs -U $q a.c || { echo "#rcs -U failed"; exit 1; }
ci -mm $q a.c || { echo "#rcs -U didn't unset strict locking"; exit 1; }
rcs -L $q a.c || { echo "#rcs -L failed"; exit 1; }
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#ci retest failed"; exit 1; }
rm -f a.c &&
log0=`rlog -h a.c` &&
co -l $q a.c &&
ci -mm $q a.c &&
log1=`rlog -h a.c` &&
test " $log0" = " $log1" || { echo "#unchanged ci didn't revert"; exit 1; }
rm -f a.c &&
rcs -nN:1.1 $q a.c &&
co -rN $q a.c &&
$diff a.11 a.c || { echo "#rcs -n failed"; exit 1; }
rcs -NN:2.1 $q a.c &&
co -rN $q a.c &&
$diff a.kv a.c || { echo "#rcs -N failed"; exit 1; }
co -l $q a.c &&
rcs -c':::' $q a.c &&
echo '$''Log$' >a.c &&
ci -u -mm $q a.c &&
test " `sed '$!d' a.c`" = ' :::' || { echo "#rcs -c failed"; exit 1; }
rcs -o2.2: $q a.c &&
co $q a.c &&
$diff a.kv a.c || { echo "#rcs -o failed"; exit 1; }
rcsdiff -r1.1 -r2.1 $q a.c >a.0
case $? in
1) ;;
*) echo "#rcsdiff bad status"; exit 1
esac
diff a.11 a.kv >a.1
$diff a.0 a.1 || { echo "#rcsdiff failed"; exit 1; }
rcs -l2.1 $q a.c || { echo "#rcs -l2.1 failed"; exit 1; }
for i in k kv kvl o v
do
rm -f a.c &&
cp a.$i a.c &&
rcsdiff -k$i $q a.c || { echo "#rcsdiff -k$i failed"; exit 1; }
done
co -p1.1 -ko $q a.c >a.t &&
$diff a.11 a.t || { echo "#co -p1.1 -ko failed"; exit 1; }
rcs -u2.1 $q a.c || { echo "#rcs -u2.1 failed"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
cat >a.c <<'EOF'
2.2
a
b
c
d
EOF
test $? = 0 &&
ci -l -mm $q a.c &&
co -p2.2 $q a.c | sed -e s/2.2/2.3/ -e s/b/b1/ >a.c &&
ci -l -mm $q a.c &&
co -p2.2 $q a.c | sed -e s/2.2/new/ -e s/d/d1/ >a.c || exit 2
cat >a.0 <<'EOF'
2.3
a
b1
c
d1
EOF
cat >a.1 <<'EOF'
<<<<<<< a.c
new
=======
2.3
>>>>>>> 2.3
a
b1
c
d1
EOF
rcsmerge -r2.2 -r2.3 $q a.c
case $? in
0)
if $diff a.0 a.c >/dev/null
then echo "#warning: diff3 -E does not work, " \
"so merge and rcsmerge ignore overlaps and suppress overlap lines."
else
$diff a.1 a.c || { echo "#rcsmerge failed (status 0)"; exit 1; }
echo "#warning: The diff3 lib program exit status ignores overlaps," \
"so rcsmerge does not warn about overlap lines that it generates."
fi
;;
1)
$diff a.1 a.c || { echo "#rcsmerge failed (status 1)"; exit 1; }
;;
*)
echo "#rcsmerge bad status"; exit 1
esac
nl='
'
{
co -p $q a.c | tr "$nl" '\200' >a.24 &&
cp a.24 a.c &&
ciOut=`(ci -l -mm $q a.c 2>&1)` &&
case $ciOut in
?*) echo >&2 "$ciOut"
esac &&
co -p $q a.c | tr '\200' "$nl" >a.c &&
rcsdiff -r2.3 $q a.c >/dev/null &&
echo 2.5 >a.c &&
ci -l -mm $q a.c &&
cp a.24 a.c &&
rcsdiff -r2.4 $q a.c >/dev/null
} || echo "#warning: Traditional diff is used, so RCS is limited to text files."
rcs -u -o2.4: $q a.c || { echo "#rcs -u -o failed"; exit 1; }
rcs -i -Aa.c -t- $q a.d || { echo "#rcs -i -A failed"; exit 1; }
rlog -r2.1 a.c >a.t &&
grep '^checked in with -k' a.t >/dev/null &&
sed '/^checked in with -k/d' a.t >a.u &&
$diff - a.u <<EOF
RCS file: $RCSfile
Working file: a.c
head: 2.3
branch:
locks: strict
access list:
symbolic names:
N: 2.1
n: 1.8
comment leader: ":::"
keyword substitution: kv
total revisions: 13; selected revisions: 1
description:
1.1
----------------------------
revision 2.1
date: $D $T; author: w; state: s; lines: +13 -1
=============================================================================
EOF
test $? = 0 || { echo "#rlog failed"; exit 1; }
test ! -f $lockfile || { echo "#lock file not removed"; exit 1; }
exec rm -f a.* $RCSfile $RCS_alt

View file

@ -0,0 +1,7 @@
PROG= rlog
SRCS= rlog.c
LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
.include <bsd.prog.mk>

260
gnu/usr.bin/rcs/rlog/rlog.1 Normal file
View file

@ -0,0 +1,260 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $Id: rlog.1,v 5.3 1991/08/22 06:50:48 eggert Exp $
.ds g \&\s-1UTC\s0
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RLOG 1 \*(Dt GNU
.SH NAME
rlog \- print log messages and other information about RCS files
.SH SYNOPSIS
.B rlog
.RI [ " options " ] " file " .\|.\|.
.SH DESCRIPTION
.B rlog
prints information about \*r files.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
.B rlog
prints the following information for each
\*r file: \*r pathname, working pathname, head (i.e., the number
of the latest revision on the trunk), default branch, access list, locks,
symbolic names, suffix, total number of revisions,
number of revisions selected for printing, and
descriptive text. This is followed by entries for the selected revisions in
reverse chronological order for each branch. For each revision,
.B rlog
prints revision number, author, date/time, state, number of
lines added/deleted (with respect to the previous revision),
locker of the revision (if any), and log message.
All times are displayed in Coordinated Universal Time (\*g).
Without options,
.B rlog
prints complete information.
The options below restrict this output.
.nr n \w'\f3\-V\fP\f2n\fP '+1n-1/1n
.TP \nn
.B \-L
Ignore \*r files that have no locks set.
This is convenient in combination with
.BR \-h ,
.BR \-l ,
and
.BR \-R .
.TP
.B \-R
Print only the name of the \*r file.
This is convenient for translating a
working pathname into an \*r pathname.
.TP
.B \-h
Print only the \*r pathname, working pathname, head,
default branch, access list, locks,
symbolic names, and suffix.
.TP
.B \-t
Print the same as
.BR \-h ,
plus the descriptive text.
.TP
.B \-b
Print information about the revisions on the default branch, normally
the highest branch on the trunk.
.TP
.BI \-d "dates"
Print information about revisions with a checkin date/time in the ranges given by
the semicolon-separated list of
.IR dates .
A range of the form
.IB d1 < d2
or
.IB d2 > d1
selects the revisions that were deposited between
.I d1
and
.I d2
inclusive.
A range of the form
.BI < d
or
.IB d >
selects
all revisions dated
.I d
or earlier.
A range of the form
.IB d <
or
.BI > d
selects
all revisions dated
.I d
or later.
A range of the form
.I d
selects the single, latest revision dated
.I d
or earlier.
The date/time strings
.IR d ,
.IR d1 ,
and
.I d2
are in the free format explained in
.BR co (1).
Quoting is normally necessary, especially for
.B <
and
.BR > .
Note that the separator is
a semicolon.
.TP
.BR \-l [\f2lockers\fP]
Print information about locked revisions only.
In addition, if the comma-separated list
.I lockers
of login names is given,
ignore all locks other than those held by the
.IR lockers .
For example,
.B "rlog\ \-L\ \-R\ \-lwft\ RCS/*"
prints the name of \*r files locked by the user
.BR wft .
.TP
.BR \-r [\f2revisions\fP]
prints information about revisions given in the comma-separated list
.I revisions
of revisions and ranges.
A range
.IB rev1 : rev2
means revisions
.I rev1
to
.I rev2
on the same branch,
.BI : rev
means revisions from the beginning of the branch up to and including
.IR rev ,
and
.IB rev :
means revisions starting with
.I rev
to the end of the branch containing
.IR rev .
An argument that is a branch means all
revisions on that branch.
A range of branches means all revisions
on the branches in that range.
A branch followed by a
.B .\&
means the latest revision in that branch.
A bare
.B \-r
with no
.I revisions
means the latest revision on the default branch, normally the trunk.
.TP
.BI \-s states
prints information about revisions whose state attributes match one of the
states given in the comma-separated list
.IR states .
.TP
.BR \-w [\f2logins\fP]
prints information about revisions checked in by users with
login names appearing in the comma-separated list
.IR logins .
If
.I logins
is omitted, the user's login is assumed.
.TP
.BI \-V n
Emulate \*r version
.I n
when generating logs.
See
.BR co (1)
for more.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.PP
.B rlog
prints the intersection of the revisions selected with
the options
.BR \-d ,
.BR \-l ,
.BR \-s ,
and
.BR \-w ,
intersected
with the union of the revisions selected by
.B \-b
and
.BR \-r .
.SH EXAMPLES
.LP
.nf
.B " rlog \-L \-R RCS/*"
.B " rlog \-L \-h RCS/*"
.B " rlog \-L \-l RCS/*"
.B " rlog RCS/*"
.fi
.LP
The first command prints the names of all \*r files in the subdirectory
.B RCS
that have locks. The second command prints the headers of those files,
and the third prints the headers plus the log messages of the locked revisions.
The last command prints complete information.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Revision Number: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 by Walter F. Tichy.
.br
Copyright \(co 1990, 1991 by Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
The separator for revision ranges in the
.B \-r
option used to be
.B \-
instead of
.BR : ,
but this leads to confusion when symbolic names contain
.BR \- .
For backwards compatibility
.B "rlog \-r"
still supports the old
.B \-
separator, but it warns about this obsolete use.
.br

1204
gnu/usr.bin/rcs/rlog/rlog.c Normal file

File diff suppressed because it is too large Load diff

339
gnu/usr.bin/tar/COPYING Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1732
gnu/usr.bin/tar/ChangeLog Normal file

File diff suppressed because it is too large Load diff

14
gnu/usr.bin/tar/Makefile Normal file
View file

@ -0,0 +1,14 @@
PROG= tar
SRCS= buffer.c create.c diffarch.c extract.c fnmatch.c getdate.y \
getoldopt.c getopt.c getopt1.c gnu.c list.c mangle.c names.c port.c \
regex.c rtapelib.c tar.c update.c version.c
CFLAGS+= -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1
CFLAGS+= -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1
CFLAGS+= -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1
CFLAGS+= -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1
CFLAGS+= -DHAVE_VPRINTF=1 -DNEEDPAD -I${.CURDIR}
CFLAGS+= -DDEF_AR_FILE=\"/dev/rst0\" -DDEFBLOCKING=20
NOMAN=noman
.include <bsd.prog.mk>
.include "../../usr.bin/Makefile.inc"

View file

@ -0,0 +1,185 @@
# Generated automatically from Makefile.in by configure.
# Un*x Makefile for GNU tar program.
# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#### Start of system configuration section. ####
srcdir = .
VPATH = .
# If you use gcc, you should either run the fixincludes script that
# comes with it or else use gcc with the -traditional option. Otherwise
# ioctl calls will be compiled incorrectly on some systems.
CC = gcc
YACC = bison -y
INSTALL = /usr/local/bin/install -c
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
# Things you might add to DEFS:
# -DSTDC_HEADERS If you have ANSI C headers and libraries.
# -DHAVE_UNISTD_H If you have unistd.h.
# -DHAVE_STRING_H If you don't have ANSI C headers but have string.h.
# -DHAVE_LIMITS_H If you have limits.h.
# -DBSD42 If you have sys/dir.h (unless you use -DPOSIX),
# sys/file.h, and st_blocks in `struct stat'.
# -DDIRENT If you have dirent.h.
# -DSYSNDIR Old Xenix systems (sys/ndir.h).
# -DSYSDIR Old BSD systems (sys/dir.h).
# -DNDIR Old System V systems (ndir.h).
# -DMAJOR_IN_MKDEV If major, minor, makedev defined in sys/mkdev.h.
# -DMAJOR_IN_SYSMACROS If major, minor, makedev defined in sys/sysmacros.h.
# -DRETSIGTYPE=int If your signal handlers return int, not void.
# -DHAVE_SYS_MTIO_H If you have sys/mtio.h (magtape ioctls).
# -DHAVE_SYS_GENTAPE_H If you have sys/gentape.h (ISC magtape ioctls).
# -DHAVE_NETDB_H To use rexec for remote tape operations
# instead of forking rsh or remsh.
# -DNO_REMOTE If you have neither a remote shell nor rexec.
# -DHAVE_VPRINTF If you have vprintf function.
# -DHAVE_DOPRNT If you have _doprnt function (but lack vprintf).
# -DHAVE_FTIME If you have ftime system call.
# -DHAVE_STRSTR If you have strstr function.
# -DHAVE_VALLOC If you have valloc function.
# -DHAVE_MKDIR If you have mkdir and rmdir system calls.
# -DHAVE_MKNOD If you have mknod system call.
# -DHAVE_RENAME If you have rename system call.
# -DHAVE_GETCWD If not POSIX.1 but have getcwd function.
# -DHAVE_FTRUNCATE If you have ftruncate system call.
# -DV7 On Version 7 Unix (not tested in a long time).
# -DEMUL_OPEN3 If you lack a 3-argument version of open, and want
# to emulate it with system calls you do have.
# -DNO_OPEN3 If you lack the 3-argument open and want to
# disable the tar -k option instead of emulating open.
# -DXENIX If you have sys/inode.h and need it to be included.
DEF_AR_FILE = /dev/rst0
DEFBLOCKING = 20
DEFS = -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1 -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1 -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1 -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1 -DHAVE_VPRINTF=1 -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" -DDEFBLOCKING=$(DEFBLOCKING)
# Set this to rtapelib.o unless you defined NO_REMOTE, in which case
# make it empty.
RTAPELIB = rtapelib.o
LIBS =
CFLAGS = -g
LDFLAGS = -g
prefix = /usr/bin
exec_prefix = $(prefix)
# Prefix for each installed program, normally empty or `g'.
binprefix =
# The directory to install tar in.
bindir = $(exec_prefix)/bin
# Where to put the rmt executable.
libdir = /sbin
# The directory to install the info files in.
infodir = $(prefix)/info
#### End of system configuration section. ####
SHELL = /bin/sh
SRC1 = tar.c create.c extract.c buffer.c getoldopt.c update.c gnu.c mangle.c
SRC2 = version.c list.c names.c diffarch.c port.c fnmatch.c getopt.c malloc.c
SRC3 = getopt1.c regex.c getdate.y getdate.c alloca.c
SRCS = $(SRC1) $(SRC2) $(SRC3)
OBJ1 = tar.o create.o extract.o buffer.o getoldopt.o update.o gnu.o mangle.o
OBJ2 = version.o list.o names.o diffarch.o port.o fnmatch.o getopt.o
OBJ3 = getopt1.o regex.o getdate.o $(RTAPELIB)
OBJS = $(OBJ1) $(OBJ2) $(OBJ3)
AUX = README INSTALL NEWS COPYING ChangeLog Makefile.in makefile.pc \
configure configure.in \
tar.h fnmatch.h pathmax.h port.h open3.h getopt.h regex.h \
rmt.h rmt.c rtapelib.c \
msd_dir.h msd_dir.c tcexparg.c \
level-0 level-1 backup-specs dump-remind getpagesize.h
# tar.texinfo tar.info* texinfo.tex \
all: tar rmt
# tar.info
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I. $<
tar: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
rmt: rmt.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(srcdir)/rmt.c $(LIBS)
tar.info: tar.texinfo
makeinfo $(srcdir)/tar.texinfo
install: all
$(INSTALL_PROGRAM) tar $(bindir)/$(binprefix)tar
-test ! -f rmt || $(INSTALL_PROGRAM) rmt $(libdir)/rmt
# for file in $(srcdir)/tar.info*; \
# do $(INSTALL_DATA) $$file $(infodir)/$$file; \
# done
uninstall:
rm -f $(bindir)/$(binprefix)tar $(infodir)/tar.info*
-rm -f $(libdir)/rmt
$(OBJS): tar.h pathmax.h port.h
regex.o buffer.o tar.o: regex.h
tar.o fnmatch.o: fnmatch.h
getdate.c: getdate.y
$(YACC) $(srcdir)/getdate.y
mv y.tab.c getdate.c
# getdate.y has 8 shift/reduce conflicts.
TAGS: $(SRCS)
etags $(SRCS)
clean:
rm -f *.o tar rmt core
mostlyclean: clean
distclean: clean
rm -f Makefile config.status
realclean: distclean
rm -f TAGS *.info* getdate.c y.tab.c
shar: $(SRCS) $(AUX)
shar $(SRCS) $(AUX) | gzip > tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c`.shar.z
dist: $(SRCS) $(AUX)
echo tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c` > .fname
-rm -rf `cat .fname`
mkdir `cat .fname`
for file in $(SRCS) $(AUX); do \
ln $$file `cat .fname` || cp $$file `cat .fname`; done
tar chzf `cat .fname`.tar.z `cat .fname`
-rm -rf `cat .fname` .fname
tar.zoo: $(SRCS) $(AUX)
-rm -rf tmp.dir
-mkdir tmp.dir
-rm tar.zoo
for X in $(SRCS) $(AUX) ; do echo $$X ; sed 's/$$/ /' $$X > tmp.dir/$$X ; done
cd tmp.dir ; zoo aM ../tar.zoo *
-rm -rf tmp.dir
# Prevent GNU make v3 from overflowing arg limit on SysV.
.NOEXPORT:

40
gnu/usr.bin/tar/README Normal file
View file

@ -0,0 +1,40 @@
Hey! Emacs! Yo! This is -*- Text -*- !!!
This GNU tar 1.11.2. Please send bug reports, etc., to
bug-gnu-utils@prep.ai.mit.edu. This is a beta-test release. Please
try it out. There is no manual; the release of version 1.12 will
contain a manual.
GNU tar is based heavily on John Gilmore's public domain tar, but with
added features. The manual is currently being written.
This distribution also includes rmt, the remote tape server (which
normally must reside in /etc). The mt tape drive control program is
in the GNU cpio distribution.
See the file INSTALL for compilation and installation instructions for Unix.
See the file NEWS for information on all that is new in this version
of tar.
makefile.pc is a makefile for Turbo C 2.0 on MS-DOS.
Various people have been having problems using floppies on a NeXT. In
order to have them work right, you need to kill the automounting
program which tries to monut floppies as soon as they are added.
If you want to do incremental dumps, use the distributed backup
scripts. They are what we use at the FSF to do all our backups. Most
importantly, do not use --incremental (-G) or --after-date (-N) or
--newer-mtime to do incremental dumps. The only option that works
correctly for this purpose is --listed-incremental. (When extracting
incremental dumps, use --incremental (-G).)
If your system needs to link with -lPW to get alloca, but has
rename in the C library (so HAVE_RENAME is defined), -lPW might
give you an incorrect version of rename. On HP-UX this manifests
itself as an undefined data symbol called "Error" when linking cp, ln,
and mv. If this happens, use `ar x' to extract alloca.o from libPW.a
and `ar rc' to put it in a library liballoca.a, and put that in LIBS
instead of -lPW. This problem does not occur when using gcc, which
has alloca built in.

1584
gnu/usr.bin/tar/buffer.c Normal file

File diff suppressed because it is too large Load diff

1454
gnu/usr.bin/tar/create.c Normal file

File diff suppressed because it is too large Load diff

759
gnu/usr.bin/tar/diffarch.c Normal file
View file

@ -0,0 +1,759 @@
/* Diff files from a tar archive.
Copyright (C) 1988, 1992, 1993 Free Software Foundation
This file is part of GNU Tar.
GNU Tar is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Tar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Diff files from a tar archive.
*
* Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
*/
#include <stdio.h>
#include <errno.h>
#ifndef STDC_HEADERS
extern int errno;
#endif
#include <sys/types.h>
#ifdef BSD42
#include <sys/file.h>
#else
#ifndef V7
#include <fcntl.h>
#endif
#endif
#ifdef HAVE_SYS_MTIO_H
#include <sys/ioctl.h>
#include <sys/mtio.h>
#endif
#include "tar.h"
#include "port.h"
#include "rmt.h"
#ifndef S_ISLNK
#define lstat stat
#endif
extern void *valloc ();
extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */
void decode_header ();
void diff_sparse_files ();
void fill_in_sparse_array ();
void fl_read ();
long from_oct ();
int do_stat ();
extern void print_header ();
int read_header ();
void saverec ();
void sigh ();
extern void skip_file ();
extern void skip_extended_headers ();
int wantbytes ();
extern FILE *msg_file;
int now_verifying = 0; /* Are we verifying at the moment? */
int diff_fd; /* Descriptor of file we're diffing */
char *diff_buf = 0; /* Pointer to area for reading
file contents into */
char *diff_dir; /* Directory contents for LF_DUMPDIR */
int different = 0;
/*struct sp_array *sparsearray;
int sp_ar_size = 10;*/
/*
* Initialize for a diff operation
*/
void
diff_init ()
{
/*NOSTRICT*/
diff_buf = (char *) valloc ((unsigned) blocksize);
if (!diff_buf)
{
msg ("could not allocate memory for diff buffer of %d bytes",
blocksize);
exit (EX_ARGSBAD);
}
}
/*
* Diff a file against the archive.
*/
void
diff_archive ()
{
register char *data;
int check, namelen;
int err;
long offset;
struct stat filestat;
int compare_chunk ();
int compare_dir ();
int no_op ();
#ifndef __MSDOS__
dev_t dev;
ino_t ino;
#endif
char *get_dir_contents ();
long from_oct ();
errno = EPIPE; /* FIXME, remove perrors */
saverec (&head); /* Make sure it sticks around */
userec (head); /* And go past it in the archive */
decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
/* Print the record from 'head' and 'hstat' */
if (f_verbose)
{
if (now_verifying)
fprintf (msg_file, "Verify ");
print_header ();
}
switch (head->header.linkflag)
{
default:
msg ("Unknown file type '%c' for %s, diffed as normal file",
head->header.linkflag, current_file_name);
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_SPARSE:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen (current_file_name) - 1;
if (current_file_name[namelen] == '/')
goto really_dir;
if (do_stat (&filestat))
{
if (head->header.isextended)
skip_extended_headers ();
skip_file ((long) hstat.st_size);
different++;
goto quit;
}
if (!S_ISREG (filestat.st_mode))
{
fprintf (msg_file, "%s: not a regular file\n",
current_file_name);
skip_file ((long) hstat.st_size);
different++;
goto quit;
}
filestat.st_mode &= 07777;
if (filestat.st_mode != hstat.st_mode)
sigh ("mode");
if (filestat.st_uid != hstat.st_uid)
sigh ("uid");
if (filestat.st_gid != hstat.st_gid)
sigh ("gid");
if (filestat.st_mtime != hstat.st_mtime)
sigh ("mod time");
if (head->header.linkflag != LF_SPARSE &&
filestat.st_size != hstat.st_size)
{
sigh ("size");
skip_file ((long) hstat.st_size);
goto quit;
}
diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
if (diff_fd < 0 && !f_absolute_paths)
{
char tmpbuf[NAMSIZ + 2];
tmpbuf[0] = '/';
strcpy (&tmpbuf[1], current_file_name);
diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
}
if (diff_fd < 0)
{
msg_perror ("cannot open %s", current_file_name);
if (head->header.isextended)
skip_extended_headers ();
skip_file ((long) hstat.st_size);
different++;
goto quit;
}
/*
* Need to treat sparse files completely differently here.
*/
if (head->header.linkflag == LF_SPARSE)
diff_sparse_files (hstat.st_size);
else
wantbytes ((long) (hstat.st_size), compare_chunk);
check = close (diff_fd);
if (check < 0)
msg_perror ("Error while closing %s", current_file_name);
quit:
break;
#ifndef __MSDOS__
case LF_LINK:
if (do_stat (&filestat))
break;
dev = filestat.st_dev;
ino = filestat.st_ino;
err = stat (current_link_name, &filestat);
if (err < 0)
{
if (errno == ENOENT)
{
fprintf (msg_file, "%s: does not exist\n", current_file_name);
}
else
{
msg_perror ("cannot stat file %s", current_file_name);
}
different++;
break;
}
if (filestat.st_dev != dev || filestat.st_ino != ino)
{
fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
break;
}
break;
#endif
#ifdef S_ISLNK
case LF_SYMLINK:
{
char linkbuf[NAMSIZ + 3];
check = readlink (current_file_name, linkbuf,
(sizeof linkbuf) - 1);
if (check < 0)
{
if (errno == ENOENT)
{
fprintf (msg_file,
"%s: no such file or directory\n",
current_file_name);
}
else
{
msg_perror ("cannot read link %s", current_file_name);
}
different++;
break;
}
linkbuf[check] = '\0'; /* Null-terminate it */
if (strncmp (current_link_name, linkbuf, check) != 0)
{
fprintf (msg_file, "%s: symlink differs\n",
current_link_name);
different++;
}
}
break;
#endif
#ifdef S_IFCHR
case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto check_node;
#endif
#ifdef S_IFBLK
/* If local system doesn't support block devices, use default case */
case LF_BLK:
hstat.st_mode |= S_IFBLK;
goto check_node;
#endif
#ifdef S_ISFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
#ifdef S_IFIFO
hstat.st_mode |= S_IFIFO;
#endif
hstat.st_rdev = 0; /* FIXME, do we need this? */
goto check_node;
#endif
check_node:
/* FIXME, deal with umask */
if (do_stat (&filestat))
break;
if (hstat.st_rdev != filestat.st_rdev)
{
fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
different++;
break;
}
#ifdef S_IFMT
if (hstat.st_mode != filestat.st_mode)
#else /* POSIX lossage */
if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
#endif
{
fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
different++;
break;
}
break;
case LF_DUMPDIR:
data = diff_dir = get_dir_contents (current_file_name, 0);
if (data)
{
wantbytes ((long) (hstat.st_size), compare_dir);
free (data);
}
else
wantbytes ((long) (hstat.st_size), no_op);
/* FALL THROUGH */
case LF_DIR:
/* Check for trailing / */
namelen = strlen (current_file_name) - 1;
really_dir:
while (namelen && current_file_name[namelen] == '/')
current_file_name[namelen--] = '\0'; /* Zap / */
if (do_stat (&filestat))
break;
if (!S_ISDIR (filestat.st_mode))
{
fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
different++;
break;
}
if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
sigh ("mode");
break;
case LF_VOLHDR:
break;
case LF_MULTIVOL:
namelen = strlen (current_file_name) - 1;
if (current_file_name[namelen] == '/')
goto really_dir;
if (do_stat (&filestat))
break;
if (!S_ISREG (filestat.st_mode))
{
fprintf (msg_file, "%s: not a regular file\n",
current_file_name);
skip_file ((long) hstat.st_size);
different++;
break;
}
filestat.st_mode &= 07777;
offset = from_oct (1 + 12, head->header.offset);
if (filestat.st_size != hstat.st_size + offset)
{
sigh ("size");
skip_file ((long) hstat.st_size);
different++;
break;
}
diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
if (diff_fd < 0)
{
msg_perror ("cannot open file %s", current_file_name);
skip_file ((long) hstat.st_size);
different++;
break;
}
err = lseek (diff_fd, offset, 0);
if (err != offset)
{
msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
different++;
break;
}
wantbytes ((long) (hstat.st_size), compare_chunk);
check = close (diff_fd);
if (check < 0)
{
msg_perror ("Error while closing %s", current_file_name);
}
break;
}
/* We don't need to save it any longer. */
saverec ((union record **) 0);/* Unsave it */
}
int
compare_chunk (bytes, buffer)
long bytes;
char *buffer;
{
int err;
err = read (diff_fd, diff_buf, bytes);
if (err != bytes)
{
if (err < 0)
{
msg_perror ("can't read %s", current_file_name);
}
else
{
fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
}
different++;
return -1;
}
if (bcmp (buffer, diff_buf, bytes))
{
fprintf (msg_file, "%s: data differs\n", current_file_name);
different++;
return -1;
}
return 0;
}
int
compare_dir (bytes, buffer)
long bytes;
char *buffer;
{
if (bcmp (buffer, diff_dir, bytes))
{
fprintf (msg_file, "%s: data differs\n", current_file_name);
different++;
return -1;
}
diff_dir += bytes;
return 0;
}
/*
* Sigh about something that differs.
*/
void
sigh (what)
char *what;
{
fprintf (msg_file, "%s: %s differs\n",
current_file_name, what);
}
void
verify_volume ()
{
int status;
#ifdef MTIOCTOP
struct mtop t;
int er;
#endif
if (!diff_buf)
diff_init ();
#ifdef MTIOCTOP
t.mt_op = MTBSF;
t.mt_count = 1;
if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
{
if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
{
#endif
if (rmtlseek (archive, 0L, 0) != 0)
{
/* Lseek failed. Try a different method */
msg_perror ("Couldn't rewind archive file for verify");
return;
}
#ifdef MTIOCTOP
}
}
#endif
ar_reading = 1;
now_verifying = 1;
fl_read ();
for (;;)
{
status = read_header ();
if (status == 0)
{
unsigned n;
n = 0;
do
{
n++;
status = read_header ();
}
while (status == 0);
msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
}
if (status == 2 || status == EOF)
break;
diff_archive ();
}
ar_reading = 0;
now_verifying = 0;
}
int
do_stat (statp)
struct stat *statp;
{
int err;
err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
if (err < 0)
{
if (errno == ENOENT)
{
fprintf (msg_file, "%s: does not exist\n", current_file_name);
}
else
msg_perror ("can't stat file %s", current_file_name);
/* skip_file((long)hstat.st_size);
different++;*/
return 1;
}
else
return 0;
}
/*
* JK
* Diff'ing a sparse file with its counterpart on the tar file is a
* bit of a different story than a normal file. First, we must know
* what areas of the file to skip through, i.e., we need to contruct
* a sparsearray, which will hold all the information we need. We must
* compare small amounts of data at a time as we find it.
*/
void
diff_sparse_files (filesize)
int filesize;
{
int sparse_ind = 0;
char *buf;
int buf_size = RECORDSIZE;
union record *datarec;
int err;
long numbytes;
/* int amt_read = 0;*/
int size = filesize;
buf = (char *) ck_malloc (buf_size * sizeof (char));
fill_in_sparse_array ();
while (size > 0)
{
datarec = findrec ();
if (!sparsearray[sparse_ind].numbytes)
break;
/*
* 'numbytes' is nicer to write than
* 'sparsearray[sparse_ind].numbytes' all the time ...
*/
numbytes = sparsearray[sparse_ind].numbytes;
lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
/*
* take care to not run out of room in our buffer
*/
while (buf_size < numbytes)
{
buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
buf_size *= 2;
}
while (numbytes > RECORDSIZE)
{
if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
{
if (err < 0)
msg_perror ("can't read %s", current_file_name);
else
fprintf (msg_file, "%s: could only read %d of %d bytes\n",
current_file_name, err, numbytes);
break;
}
if (bcmp (buf, datarec->charptr, RECORDSIZE))
{
different++;
break;
}
numbytes -= err;
size -= err;
userec (datarec);
datarec = findrec ();
}
if ((err = read (diff_fd, buf, numbytes)) != numbytes)
{
if (err < 0)
msg_perror ("can't read %s", current_file_name);
else
fprintf (msg_file, "%s: could only read %d of %d bytes\n",
current_file_name, err, numbytes);
break;
}
if (bcmp (buf, datarec->charptr, numbytes))
{
different++;
break;
}
/* amt_read += numbytes;
if (amt_read >= RECORDSIZE) {
amt_read = 0;
userec(datarec);
datarec = findrec();
}*/
userec (datarec);
sparse_ind++;
size -= numbytes;
}
/*
* if the number of bytes read isn't the
* number of bytes supposedly in the file,
* they're different
*/
/* if (amt_read != filesize)
different++;*/
userec (datarec);
free (sparsearray);
if (different)
fprintf (msg_file, "%s: data differs\n", current_file_name);
}
/*
* JK
* This routine should be used more often than it is ... look into
* that. Anyhow, what it does is translate the sparse information
* on the header, and in any subsequent extended headers, into an
* array of structures with true numbers, as opposed to character
* strings. It simply makes our life much easier, doing so many
* comparisong and such.
*/
void
fill_in_sparse_array ()
{
int ind;
/*
* allocate space for our scratch space; it's initially
* 10 elements long, but can change in this routine if
* necessary
*/
sp_array_size = 10;
sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
/*
* there are at most five of these structures in the header
* itself; read these in first
*/
for (ind = 0; ind < SPARSE_IN_HDR; ind++)
{
if (!head->header.sp[ind].numbytes)
break;
sparsearray[ind].offset =
from_oct (1 + 12, head->header.sp[ind].offset);
sparsearray[ind].numbytes =
from_oct (1 + 12, head->header.sp[ind].numbytes);
}
/*
* if the header's extended, we gotta read in exhdr's till
* we're done
*/
if (head->header.isextended)
{
/* how far into the sparsearray we are 'so far' */
static int so_far_ind = SPARSE_IN_HDR;
union record *exhdr;
for (;;)
{
exhdr = findrec ();
for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
{
if (ind + so_far_ind > sp_array_size - 1)
{
/*
* we just ran out of room in our
* scratch area - realloc it
*/
sparsearray = (struct sp_array *)
ck_realloc (sparsearray,
sp_array_size * 2 * sizeof (struct sp_array));
sp_array_size *= 2;
}
/*
* convert the character strings into longs
*/
sparsearray[ind + so_far_ind].offset =
from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
sparsearray[ind + so_far_ind].numbytes =
from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
}
/*
* if this is the last extended header for this
* file, we can stop
*/
if (!exhdr->ext_hdr.isextended)
break;
else
{
so_far_ind += SPARSE_EXT_HDR;
userec (exhdr);
}
}
/* be sure to skip past the last one */
userec (exhdr);
}
}

907
gnu/usr.bin/tar/extract.c Normal file
View file

@ -0,0 +1,907 @@
/* Extract files from a tar archive.
Copyright (C) 1988, 1992, 1993 Free Software Foundation
This file is part of GNU Tar.
GNU Tar is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Tar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*/
#include <stdio.h>
#include <errno.h>
#ifndef STDC_HEADERS
extern int errno;
#endif
#include <sys/types.h>
#include <time.h>
time_t time ();
#ifdef BSD42
#include <sys/file.h>
#else
#ifndef V7
#include <fcntl.h>
#endif
#endif
#ifdef NO_OPEN3
/* We need the #define's even though we don't use them. */
#include "open3.h"
#endif
#ifdef EMUL_OPEN3
/* Simulated 3-argument open for systems that don't have it */
#include "open3.h"
#endif
#include "tar.h"
#include "port.h"
#if defined(_POSIX_VERSION)
#include <utime.h>
#else
struct utimbuf
{
long actime;
long modtime;
};
#endif
extern FILE *msg_file;
extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */
extern char *save_name;
extern long save_totsize;
extern long save_sizeleft;
int confirm ();
void decode_header ();
void extract_mangle ();
void extract_sparse_file ();
long from_oct ();
void gnu_restore ();
extern void print_header ();
extern void skip_file ();
extern void skip_extended_headers ();
extern void pr_mkdir ();
void saverec ();
int make_dirs (); /* Makes required directories */
static time_t now = 0; /* Current time */
static we_are_root = 0; /* True if our effective uid == 0 */
static int notumask = ~0; /* Masks out bits user doesn't want */
/*
* "Scratch" space to store the information about a sparse file before
* writing the info into the header or extended header
*/
/*struct sp_array *sparsearray;*/
/* number of elts storable in the sparsearray */
/*int sp_array_size = 10;*/
struct saved_dir_info
{
char *path;
int mode;
int atime;
int mtime;
struct saved_dir_info *next;
};
struct saved_dir_info *saved_dir_info_head;
/*
* Set up to extract files.
*/
void
extr_init ()
{
int ourmask;
now = time ((time_t *) 0);
if (geteuid () == 0)
we_are_root = 1;
/*
* We need to know our umask. But if f_use_protection is set,
* leave our kernel umask at 0, and our "notumask" at ~0.
*/
ourmask = umask (0); /* Read it */
if (!f_use_protection)
{
(void) umask (ourmask); /* Set it back how it was */
notumask = ~ourmask; /* Make umask override permissions */
}
}
/*
* Extract a file from the archive.
*/
void
extract_archive ()
{
register char *data;
int fd, check, namelen, written, openflag;
long size;
struct utimbuf acc_upd_times;
register int skipcrud;
register int i;
/* int sparse_ind = 0;*/
union record *exhdr;
struct saved_dir_info *tmp;
/* int end_nulls; */
saverec (&head); /* Make sure it sticks around */
userec (head); /* And go past it in the archive */
decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
if (f_confirm && !confirm ("extract", current_file_name))
{
if (head->header.isextended)
skip_extended_headers ();
skip_file ((long) hstat.st_size);
saverec ((union record **) 0);
return;
}
/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header ();
/*
* Check for fully specified pathnames and other atrocities.
*
* Note, we can't just make a pointer to the new file name,
* since saverec() might move the header and adjust "head".
* We have to start from "head" every time we want to touch
* the header record.
*/
skipcrud = 0;
while (!f_absolute_paths
&& '/' == current_file_name[skipcrud])
{
static int warned_once = 0;
skipcrud++; /* Force relative path */
if (!warned_once++)
{
msg ("Removing leading / from absolute path names in the archive.");
}
}
switch (head->header.linkflag)
{
default:
msg ("Unknown file type '%c' for %s, extracted as normal file",
head->header.linkflag, skipcrud + current_file_name);
/* FALL THRU */
/*
* JK - What we want to do if the file is sparse is loop through
* the array of sparse structures in the header and read in
* and translate the character strings representing 1) the offset
* at which to write and 2) how many bytes to write into numbers,
* which we store into the scratch array, "sparsearray". This
* array makes our life easier the same way it did in creating
* the tar file that had to deal with a sparse file.
*
* After we read in the first five (at most) sparse structures,
* we check to see if the file has an extended header, i.e.,
* if more sparse structures are needed to describe the contents
* of the new file. If so, we read in the extended headers
* and continue to store their contents into the sparsearray.
*/
case LF_SPARSE:
sp_array_size = 10;
sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
for (i = 0; i < SPARSE_IN_HDR; i++)
{
sparsearray[i].offset =
from_oct (1 + 12, head->header.sp[i].offset);
sparsearray[i].numbytes =
from_oct (1 + 12, head->header.sp[i].numbytes);
if (!sparsearray[i].numbytes)
break;
}
/* end_nulls = from_oct(1+12, head->header.ending_blanks);*/
if (head->header.isextended)
{
/* read in the list of extended headers
and translate them into the sparsearray
as before */
/* static */ int ind = SPARSE_IN_HDR;
for (;;)
{
exhdr = findrec ();
for (i = 0; i < SPARSE_EXT_HDR; i++)
{
if (i + ind > sp_array_size - 1)
{
/*
* realloc the scratch area
* since we've run out of room --
*/
sparsearray = (struct sp_array *)
ck_realloc (sparsearray,
2 * sp_array_size * (sizeof (struct sp_array)));
sp_array_size *= 2;
}
if (!exhdr->ext_hdr.sp[i].numbytes)
break;
sparsearray[i + ind].offset =
from_oct (1 + 12, exhdr->ext_hdr.sp[i].offset);
sparsearray[i + ind].numbytes =
from_oct (1 + 12, exhdr->ext_hdr.sp[i].numbytes);
}
if (!exhdr->ext_hdr.isextended)
break;
else
{
ind += SPARSE_EXT_HDR;
userec (exhdr);
}
}
userec (exhdr);
}
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen (skipcrud + current_file_name) - 1;
if (current_file_name[skipcrud + namelen] == '/')
goto really_dir;
/* FIXME, deal with protection issues */
again_file:
openflag = (f_keep ?
O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
| ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);
/*
* JK - The last | is a kludge to solve the problem
* the O_APPEND flag causes with files we are
* trying to make sparse: when a file is opened
* with O_APPEND, it writes to the last place
* that something was written, thereby ignoring
* any lseeks that we have done. We add this
* extra condition to make it able to lseek when
* a file is sparse, i.e., we don't open the new
* file with this flag. (Grump -- this bug caused
* me to waste a good deal of time, I might add)
*/
if (f_exstdout)
{
fd = 1;
goto extract_file;
}
#ifdef O_CTG
/*
* Contiguous files (on the Masscomp) have to specify
* the size in the open call that creates them.
*/
if (head->header.linkflag == LF_CONTIG)
fd = open ((longname ? longname : head->header.name)
+ skipcrud,
openflag | O_CTG,
hstat.st_mode, hstat.st_size);
else
#endif
{
#ifdef NO_OPEN3
/*
* On raw V7 we won't let them specify -k (f_keep), but
* we just bull ahead and create the files.
*/
fd = creat ((longname
? longname
: head->header.name) + skipcrud,
hstat.st_mode);
#else
/*
* With 3-arg open(), we can do this up right.
*/
fd = open (skipcrud + current_file_name,
openflag, hstat.st_mode);
#endif
}
if (fd < 0)
{
if (make_dirs (skipcrud + current_file_name))
goto again_file;
msg_perror ("Could not create file %s",
skipcrud + current_file_name);
if (head->header.isextended)
skip_extended_headers ();
skip_file ((long) hstat.st_size);
goto quit;
}
extract_file:
if (head->header.linkflag == LF_SPARSE)
{
char *name;
int namelen;
/*
* Kludge alert. NAME is assigned to header.name
* because during the extraction, the space that
* contains the header will get scribbled on, and
* the name will get munged, so any error messages
* that happen to contain the filename will look
* REAL interesting unless we do this.
*/
namelen = strlen (skipcrud + current_file_name) + 1;
name = (char *) ck_malloc ((sizeof (char)) * namelen);
bcopy (skipcrud + current_file_name, name, namelen);
size = hstat.st_size;
extract_sparse_file (fd, &size, hstat.st_size, name);
}
else
for (size = hstat.st_size;
size > 0;
size -= written)
{
/* long offset,
numbytes;*/
if (f_multivol)
{
save_name = current_file_name;
save_totsize = hstat.st_size;
save_sizeleft = size;
}
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
data = findrec ()->charptr;
if (data == NULL)
{ /* Check it... */
msg ("Unexpected EOF on archive file");
break;
}
/*
* JK - If the file is sparse, use the sparsearray
* that we created before to lseek into the new
* file the proper amount, and to see how many
* bytes we want to write at that position.
*/
/* if (head->header.linkflag == LF_SPARSE) {
off_t pos;
pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
printf("%d at %d\n", (int) pos, sparse_ind);
written = sparsearray[sparse_ind++].numbytes;
} else*/
written = endofrecs ()->charptr - data;
if (written > size)
written = size;
errno = 0;
check = write (fd, data, written);
/*
* The following is in violation of strict
* typing, since the arg to userec
* should be a struct rec *. FIXME.
*/
userec ((union record *) (data + written - 1));
if (check == written)
continue;
/*
* Error in writing to file.
* Print it, skip to next file in archive.
*/
if (check < 0)
msg_perror ("couldn't write to file %s",
skipcrud + current_file_name);
else
msg ("could only write %d of %d bytes to file %s",
check, written, skipcrud + current_file_name);
skip_file ((long) (size - written));
break; /* Still do the close, mod time, chmod, etc */
}
if (f_multivol)
save_name = 0;
/* If writing to stdout, don't try to do anything
to the filename; it doesn't exist, or we don't
want to touch it anyway */
if (f_exstdout)
break;
/* if (head->header.isextended) {
register union record *exhdr;
register int i;
for (i = 0; i < 21; i++) {
long offset;
if (!exhdr->ext_hdr.sp[i].numbytes)
break;
offset = from_oct(1+12,
exhdr->ext_hdr.sp[i].offset);
written = from_oct(1+12,
exhdr->ext_hdr.sp[i].numbytes);
lseek(fd, offset, 0);
check = write(fd, data, written);
if (check == written) continue;
}
}*/
check = close (fd);
if (check < 0)
{
msg_perror ("Error while closing %s",
skipcrud + current_file_name);
}
set_filestat:
/*
* If we are root, set the owner and group of the extracted
* file. This does what is wanted both on real Unix and on
* System V. If we are running as a user, we extract as that
* user; if running as root, we extract as the original owner.
*/
if (we_are_root || f_do_chown)
{
if (chown (skipcrud + current_file_name,
hstat.st_uid, hstat.st_gid) < 0)
{
msg_perror ("cannot chown file %s to uid %d gid %d",
skipcrud + current_file_name,
hstat.st_uid, hstat.st_gid);
}
}
/*
* Set the modified time of the file.
*
* Note that we set the accessed time to "now", which
* is really "the time we started extracting files".
* unless f_gnudump is used, in which case .st_atime is used
*/
if (!f_modified)
{
/* fixme if f_gnudump should set ctime too, but how? */
if (f_gnudump)
acc_upd_times.actime = hstat.st_atime;
else
acc_upd_times.actime = now; /* Accessed now */
acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */
if (utime (skipcrud + current_file_name,
&acc_upd_times) < 0)
{
msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name);
}
}
/* We do the utime before the chmod because some versions of
utime are broken and trash the modes of the file. Since
we then change the mode anyway, we don't care. . . */
/*
* If '-k' is not set, open() or creat() could have saved
* the permission bits from a previously created file,
* ignoring the ones we specified.
* Even if -k is set, if the file has abnormal
* mode bits, we must chmod since writing or chown() has
* probably reset them.
*
* If -k is set, we know *we* created this file, so the mode
* bits were set by our open(). If the file is "normal", we
* skip the chmod. This works because we did umask(0) if -p
* is set, so umask will have left the specified mode alone.
*/
if ((!f_keep)
|| (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
{
if (chmod (skipcrud + current_file_name,
notumask & (int) hstat.st_mode) < 0)
{
msg_perror ("cannot change mode of file %s to %ld",
skipcrud + current_file_name,
notumask & (int) hstat.st_mode);
}
}
quit:
break;
case LF_LINK:
again_link:
{
struct stat st1, st2;
check = link (current_link_name, skipcrud + current_file_name);
if (check == 0)
break;
if (make_dirs (skipcrud + current_file_name))
goto again_link;
if (f_gnudump && errno == EEXIST)
break;
if (stat (current_link_name, &st1) == 0
&& stat (current_file_name + skipcrud, &st2) == 0
&& st1.st_dev == st2.st_dev
&& st1.st_ino == st2.st_ino)
break;
msg_perror ("Could not link %s to %s",
skipcrud + current_file_name,
current_link_name);
}
break;
#ifdef S_ISLNK
case LF_SYMLINK:
again_symlink:
check = symlink (current_link_name,
skipcrud + current_file_name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs (current_file_name + skipcrud))
goto again_symlink;
msg_perror ("Could not create symlink to %s",
current_link_name);
break;
#endif
#ifdef S_IFCHR
case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto make_node;
#endif
#ifdef S_IFBLK
case LF_BLK:
hstat.st_mode |= S_IFBLK;
#endif
#if defined(S_IFCHR) || defined(S_IFBLK)
make_node:
check = mknod (current_file_name + skipcrud,
(int) hstat.st_mode, (int) hstat.st_rdev);
if (check != 0)
{
if (make_dirs (skipcrud + current_file_name))
goto make_node;
msg_perror ("Could not make %s",
current_file_name + skipcrud);
break;
};
goto set_filestat;
#endif
#ifdef S_ISFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
make_fifo:
check = mkfifo (current_file_name + skipcrud,
(int) hstat.st_mode);
if (check != 0)
{
if (make_dirs (current_file_name + skipcrud))
goto make_fifo;
msg_perror ("Could not make %s",
skipcrud + current_file_name);
break;
};
goto set_filestat;
#endif
case LF_DIR:
case LF_DUMPDIR:
namelen = strlen (current_file_name + skipcrud) - 1;
really_dir:
/* Check for trailing /, and zap as many as we find. */
while (namelen
&& current_file_name[skipcrud + namelen] == '/')
current_file_name[skipcrud + namelen--] = '\0';
if (f_gnudump)
{ /* Read the entry and delete files
that aren't listed in the archive */
gnu_restore (skipcrud);
}
else if (head->header.linkflag == LF_DUMPDIR)
skip_file ((long) (hstat.st_size));
again_dir:
check = mkdir (skipcrud + current_file_name,
(we_are_root ? 0 : 0300) | (int) hstat.st_mode);
if (check != 0)
{
struct stat st1;
if (make_dirs (skipcrud + current_file_name))
goto again_dir;
/* If we're trying to create '.', let it be. */
if (current_file_name[skipcrud + namelen] == '.' &&
(namelen == 0 ||
current_file_name[skipcrud + namelen - 1] == '/'))
goto check_perms;
if (errno == EEXIST
&& stat (skipcrud + current_file_name, &st1) == 0
&& (S_ISDIR (st1.st_mode)))
break;
msg_perror ("Could not create directory %s", skipcrud + current_file_name);
break;
}
check_perms:
if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode))
{
hstat.st_mode |= 0300;
msg ("Added write and execute permission to directory %s",
skipcrud + current_file_name);
}
/*
* If we are root, set the owner and group of the extracted
* file. This does what is wanted both on real Unix and on
* System V. If we are running as a user, we extract as that
* user; if running as root, we extract as the original owner.
*/
if (we_are_root || f_do_chown)
{
if (chown (skipcrud + current_file_name,
hstat.st_uid, hstat.st_gid) < 0)
{
msg_perror ("cannot chown file %s to uid %d gid %d",
skipcrud + current_file_name,
hstat.st_uid, hstat.st_gid);
}
}
if (!f_modified)
{
tmp = ((struct saved_dir_info *)
ck_malloc (sizeof (struct saved_dir_info)));
tmp->path = (char *) ck_malloc (strlen (skipcrud
+ current_file_name) + 1);
strcpy (tmp->path, skipcrud + current_file_name);
tmp->mode = hstat.st_mode;
tmp->atime = hstat.st_atime;
tmp->mtime = hstat.st_mtime;
tmp->next = saved_dir_info_head;
saved_dir_info_head = tmp;
}
else
{
/* This functions exactly as the code for set_filestat above. */
if ((!f_keep)
|| (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
{
if (chmod (skipcrud + current_file_name,
notumask & (int) hstat.st_mode) < 0)
{
msg_perror ("cannot change mode of file %s to %ld",
skipcrud + current_file_name,
notumask & (int) hstat.st_mode);
}
}
}
break;
case LF_VOLHDR:
if (f_verbose)
{
printf ("Reading %s\n", current_file_name);
}
break;
case LF_NAMES:
extract_mangle (head);
break;
case LF_MULTIVOL:
msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
skip_file ((long) hstat.st_size);
break;
case LF_LONGNAME:
case LF_LONGLINK:
msg ("Visible long name error\n");
skip_file ((long) hstat.st_size);
break;
}
/* We don't need to save it any longer. */
saverec ((union record **) 0);/* Unsave it */
}
/*
* After a file/link/symlink/dir creation has failed, see if
* it's because some required directory was not present, and if
* so, create all required dirs.
*/
int
make_dirs (pathname)
char *pathname;
{
char *p; /* Points into path */
int madeone = 0; /* Did we do anything yet? */
int save_errno = errno; /* Remember caller's errno */
int check;
if (errno != ENOENT)
return 0; /* Not our problem */
for (p = index (pathname, '/'); p != NULL; p = index (p + 1, '/'))
{
/* Avoid mkdir of empty string, if leading or double '/' */
if (p == pathname || p[-1] == '/')
continue;
/* Avoid mkdir where last part of path is '.' */
if (p[-1] == '.' && (p == pathname + 1 || p[-2] == '/'))
continue;
*p = 0; /* Truncate the path there */
check = mkdir (pathname, 0777); /* Try to create it as a dir */
if (check == 0)
{
/* Fix ownership */
if (we_are_root)
{
if (chown (pathname, hstat.st_uid,
hstat.st_gid) < 0)
{
msg_perror ("cannot change owner of %s to uid %d gid %d", pathname, hstat.st_uid, hstat.st_gid);
}
}
pr_mkdir (pathname, p - pathname, notumask & 0777);
madeone++; /* Remember if we made one */
*p = '/';
continue;
}
*p = '/';
if (errno == EEXIST) /* Directory already exists */
continue;
/*
* Some other error in the mkdir. We return to the caller.
*/
break;
}
errno = save_errno; /* Restore caller's errno */
return madeone; /* Tell them to retry if we made one */
}
void
extract_sparse_file (fd, sizeleft, totalsize, name)
int fd;
long *sizeleft, totalsize;
char *name;
{
/* register char *data;*/
union record *datarec;
int sparse_ind = 0;
int written, count;
/* assuming sizeleft is initially totalsize */
while (*sizeleft > 0)
{
datarec = findrec ();
if (datarec == NULL)
{
msg ("Unexpected EOF on archive file");
return;
}
lseek (fd, sparsearray[sparse_ind].offset, 0);
written = sparsearray[sparse_ind++].numbytes;
while (written > RECORDSIZE)
{
count = write (fd, datarec->charptr, RECORDSIZE);
if (count < 0)
msg_perror ("couldn't write to file %s", name);
written -= count;
*sizeleft -= count;
userec (datarec);
datarec = findrec ();
}
count = write (fd, datarec->charptr, written);
if (count < 0)
{
msg_perror ("couldn't write to file %s", name);
}
else if (count != written)
{
msg ("could only write %d of %d bytes to file %s", count,
totalsize, name);
skip_file ((long) (*sizeleft));
}
written -= count;
*sizeleft -= count;
userec (datarec);
}
free (sparsearray);
/* if (end_nulls) {
register int i;
printf("%d\n", (int) end_nulls);
for (i = 0; i < end_nulls; i++)
write(fd, "\000", 1);
}*/
userec (datarec);
}
/* Set back the utime and mode for all the extracted directories. */
void
restore_saved_dir_info ()
{
struct utimbuf acc_upd_times;
while (saved_dir_info_head != NULL)
{
/* fixme if f_gnudump should set ctime too, but how? */
if (f_gnudump)
acc_upd_times.actime = saved_dir_info_head->atime;
else
acc_upd_times.actime = now; /* Accessed now */
acc_upd_times.modtime = saved_dir_info_head->mtime; /* Mod'd */
if (utime (saved_dir_info_head->path, &acc_upd_times) < 0)
{
msg_perror ("couldn't change access and modification times of %s",
saved_dir_info_head->path);
}
if ((!f_keep) || (saved_dir_info_head->mode & (S_ISUID | S_ISGID | S_ISVTX)))
{
if (chmod (saved_dir_info_head->path,
notumask & saved_dir_info_head->mode) < 0)
{
msg_perror ("cannot change mode of file %s to %ld",
saved_dir_info_head->path,
notumask & saved_dir_info_head->mode);
}
}
saved_dir_info_head = saved_dir_info_head->next;
}
}

Some files were not shown because too many files have changed in this diff Show more