BSD 4_4_Lite2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Wed, 30 Dec 1992 10:02:38 +0000 (02:02 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Wed, 30 Dec 1992 10:02:38 +0000 (02:02 -0800)
Work on file usr/src/contrib/rcs-V5.6/src/ci.c
Work on file usr/src/contrib/rcs-V5.6/src/conf.sh
Work on file usr/src/contrib/rcs-V5.6/src/co.c
Work on file usr/src/contrib/rcs-V5.6/src/ident.c
Work on file usr/src/contrib/rcs-V5.6/src/maketime.c
Work on file usr/src/contrib/rcs-V5.6/src/merge.c
Work on file usr/src/contrib/rcs-V5.6/src/partime.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsbase.h
Work on file usr/src/contrib/rcs-V5.6/src/rcs.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsdiff.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsedit.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsfcmp.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsgen.c
Work on file usr/src/contrib/rcs-V5.6/src/rcskeep.c
Work on file usr/src/contrib/rcs-V5.6/src/rcskeys.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsmerge.c
Work on file usr/src/contrib/rcs-V5.6/src/rcslex.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsmap.c
Work on file usr/src/contrib/rcs-V5.6/src/rcssyn.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsrev.c
Work on file usr/src/contrib/rcs-V5.6/src/rcsutil.c
Work on file usr/src/contrib/rcs-V5.6/src/rcstest
Work on file usr/src/contrib/rcs-V5.6/src/rcsclean.c
Work on file usr/src/contrib/rcs-V5.6/src/rlog.c
Work on file usr/src/contrib/rcs-V5.6/src/conf.heg
Work on file usr/src/contrib/rcs-V5.6/src/rcsfreeze.sh
Work on file usr/src/contrib/rcs-V5.6/src/merger.c
Work on file usr/src/contrib/rcs-V5.6/src/README
Work on file usr/src/contrib/rcs-V5.6/src/COPYING

Synthesized-from: CSRG/cd3/4.4BSD-Lite2

29 files changed:
usr/src/contrib/rcs-V5.6/src/COPYING [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/README [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/ci.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/co.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/conf.heg [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/conf.sh [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/ident.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/maketime.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/merge.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/merger.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/partime.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcs.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsbase.h [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsclean.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsdiff.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsedit.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsfcmp.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsfreeze.sh [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsgen.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcskeep.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcskeys.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcslex.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsmap.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsmerge.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsrev.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcssyn.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcstest [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rcsutil.c [new file with mode: 0644]
usr/src/contrib/rcs-V5.6/src/rlog.c [new file with mode: 0644]

diff --git a/usr/src/contrib/rcs-V5.6/src/COPYING b/usr/src/contrib/rcs-V5.6/src/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       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.
diff --git a/usr/src/contrib/rcs-V5.6/src/README b/usr/src/contrib/rcs-V5.6/src/README
new file mode 100644 (file)
index 0000000..3294e01
--- /dev/null
@@ -0,0 +1,216 @@
+RCS configuration instructions
+
+       $Id: README,v 1.5 1991/10/07 17:32:46 eggert Exp $
+
+       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
+
+
+For Posix and near-Posix hosts, configure by editing Makefile,
+commenting out wrong lines and uncommenting right ones.
+See ``Makefile notes'' below for advice.
+
+After configuring, make conf.h; if this fails,
+look in src/conf.error to see what went wrong in the conf.sh shell file.
+Check the resulting conf.h for plausibility; see ``conf.h notes'' below.
+If conf.h is wrong, and the mistake isn't listed in ``conf.h notes'',
+there is a bug in conf.sh; please report it.
+You can patch conf.h if you're in a hurry, but it's better to fix it;
+look at a.h and conf.error for ideas.
+If all else fails, copy conf.heg to conf.h and edit it by hand.
+After configuring, make `all'.
+
+If all went well, make `install'.
+If installation succeeds, make `installtest';
+if this fails, make `installdebug' for detailed info.
+
+If you want to test RCS before installing it,
+you must set RCSPREFIX to be empty, make RCS from scratch,
+prefix the src directory to your PATH, and run `sh src/rcstest'.
+Be sure to restore RCSPREFIX and rebuild RCS before installing it.
+
+If you want to maintain RCS with itself,
+preserve the original revision numbers, dates, etc.
+by checking the files in with the -k option.
+
+
+__________
+
+Makefile notes
+
+CC is the name of your C compiler.
+
+CC_D is a list of extra options for the C preprocessor.
+It should include any extra -D and -I options needed on your system.
+
+CC_O is the C compiler options that affect just compilation and assembly;
+not preprocessing, linking or linting.
+If your compiler has the BSD -R option, -R can improve performance by
+making all initialized data read-only (not just string literals);
+this is not worth worrying about if your compiler also supports `const'.
+
+CC_W is the list of C compiler options to enable
+warnings about questionable constructs in the RCS source code.
+
+COMPAT2 is 1 if you still have version 2 RCS files around.
+(Version 2 became obsolete in 1982, so this isn't likely.)
+COMPAT2 assures that version 2 RCS files can still be read.
+When you have the new RCS installed, rename old version 2 RCS files as follows.
+Suppose the working file was `f.c';
+rename the RCS file `f.c.v' to `f.c,v', and the RCS file `f.v' to `f.c,v'.
+Thus suffixes are no longer dropped and RCS files end in `,v' rather than `.v'.
+After all version 2 RCS files have been updated with new versions of ci or rcs,
+you can remake RCS with COMPAT2 set to 0.
+
+DIFF is the name of your diff program.
+If you override DIFF, make sure you rebuild conf.h afterwards;
+otherwise you may introduce a security hole.
+On some versions of Unix, the standard diff does not support RCS
+and you must use something like /usr/lib/rdiff instead;
+perhaps you should try GNU diff instead.
+
+DIFFPREFIX act likes RCSPREFIX, except it applies to subsidiary diffs,
+not to subsidiary RCS commands.  It must be nonempty if setuid is to be used.
+
+DIFF_FLAGS are diff's options for RCS format output, probably -n.
+If available, also include the -a option for comparing arbitrary files.
+
+DIFF_L is 1 if diff understands the -L option for labeling context diff output.
+
+DIFF_SUCCESS, DIFF_FAILURE, DIFF_TROUBLE are integer constants
+representing diff's exit status when it finds
+no differences, some differences, or trouble respectively.
+The first two should be EXIT_SUCCESS and EXIT_FAILURE,
+but on some hosts they are broken.
+
+DIFF3 is the name of the diff3 program.
+With GNU diff, this is simply its user-visible diff3 program.
+With traditional diff3, this is not the user-visible diff3 program;
+instead, it this is the name of the undocumented diff3 auxiliary program,
+whose name is /usr/lib/diff3 or /usr/5lib/rdiff3prog or something similar.
+
+DIFF3_BIN is 1 if DIFF3 is the user-visible GNU diff3 program (see DIFF3).
+Before setting this to 1, make sure your diff3 understands -a, -L, and and -m;
+e.g. the command `echo x | diff3 -m /dev/null /dev/null -' should output `x'.
+
+ED is the name of the standard Unix line editor.
+It is needed only if DIFF3_BIN is 0.
+
+EXECUTABLE_PERMISSIONS are the options given to INSTALL for executable files
+on BSD Unix sites.  Watch out for `-g staff' if you don't have a staff group,
+or if your installation program doesn't have a -g option.
+
+INSTALL is the command that installs commands.
+
+LDFLAGS are the loader flags you need, e.g. -i, -n, -s, -x.
+
+LDLIBS are the loader libraries you need, e.g. -lbsd, -lBSD, -ljobs, -lPW, -lx.
+
+LINK is the command used to link together an executable.
+
+LINT is the name and usual arguments of your lint program.
+
+OTHER_OBJECT are any other object files you need to link.
+
+RCSCOMMANDS is the list of commands to be installed by default.
+It doesn't include rcsclean$x, which is still a little experimental.
+
+RCSDIR is where the RCS commands will be installed.
+
+RCSPREFIX is the prefix for subsidiary RCS commands like ci.
+If empty, RCS will search the PATH for these commands;
+this lets you move RCS commands after building them, and permits
+multiple instances of setuid RCS commands on the same host for different users.
+If nonempty, it should be a path followed by /;
+this makes RCS look in just one place, and makes execution faster.
+
+REMOVE is how to remove a file.
+
+SENDMAIL is a comma-separated list of strings that are a command to send mail.
+The name of the addressee will be appended as a separate argument,
+and the standard input will be the message
+(first line `Subject: xxxx', second line empty).
+If your host cannot send mail, leave SENDMAIL empty.
+
+TESTPREFIX is the prefix for test commands.
+
+o is the file name extension your host uses for object files.
+It includes the `.'.  It is typically `.o' on Unix hosts.
+
+x is the file name extension your host uses for executables.
+It includes the `.'.  It is empty on Unix hosts,
+which traditionally lack extensions on executables.
+
+
+__________
+
+conf.h notes
+
+See conf.sh for details about the definitions in conf.h.
+Comments below cover unusual situations that can require hand patches to conf.h.
+
+has_NFS - Set this if the target host might use NFS.
+NFS's stateless server model has well-known problems with
+the non-idempotent operations link(), rename(), and unlink().
+For example, unlink() can run twice on the file server,
+causing the client to think that the unlink failed with errno==ENOENT.
+has_NFS enables code that works around these problems.
+
+const - Some hosts support `const' but complain about it, perhaps because
+system headers are wrong.  `#define const /*empty*/' pacifies them.
+
+has_rename - This should be 0 in SCO Unix V.3.2.  Its NFS rename() is broken,
+but if you run conf.sh in a non-NFS filesystem, conf.sh thinks rename() works.
+
+has_seteuid - This should be 1 only if your seteuid lets you switch back and
+forth between any pair of users as specified in Posix 1003.1a Draft 5.
+One must be root to test this reliably, so conf.sh just guesses.
+
+large_memory - This should be 1 if main memory is large enough to hold entire
+copies of RCS files, perhaps because virtual memory is available.
+
+NAME_MAX - this should be #undef'ed in AT&T SVR4.0 V2.0, which defines NAME_MAX
+to be 14 even though longer names are permitted on some filesystems.
+If you run conf.sh in a short-name filesystem, conf.sh won't detect this bug.
+
+_POSIX_SOURCE must be #defined in a strict Standard C environment,
+because otherwise stdio.h cannot define useful names like fileno.
+Avoid defining _POSIX_SOURCE if possible,
+because it can disable useful non-Posix features in your host.
+Perhaps you can remove the need for _POSIX_SOURCE
+by supplying an option to your compiler to makes it less strict.
+
+text_work_stdio - This option makes sense only on non-Posix hosts that
+distinguish between text and binary I/O, e.g. DOS.  Set this option if
+you want to use text I/O for working files, but binary I/O for RCS
+files.  If set, RCS does not support non-text working files, but RCS
+files are portable to other hosts across binary network file systems
+like NFS.  If not set, RCS supports non-text working files, but RCS
+files are not portable to Posix hosts.
+
+TZ_must_be_set - set this to 1 on hosts where gmtime() yields bogus
+values unless the TZ environment variable is set.
+
+volatile - See `const'.  E.g. `volatile sig_atomic_t' is conforming, but some
+buggy hosts complain.  Also, Ultrix 4.0 Mips CC 2.0 has buggy volatile support.
diff --git a/usr/src/contrib/rcs-V5.6/src/ci.c b/usr/src/contrib/rcs-V5.6/src/ci.c
new file mode 100644 (file)
index 0000000..566747e
--- /dev/null
@@ -0,0 +1,1165 @@
+/* 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 checkin operation
+ */
+/*******************************************************************
+ *                       check revisions into RCS files
+ *******************************************************************
+ */
+
+
+
+/* $Log: ci.c,v $
+ * Revision 5.21  1991/11/20  17:58:07  eggert
+ * Don't read the delta tree from a nonexistent RCS file.
+ *
+ * Revision 5.20  1991/10/07  17:32:46  eggert
+ * Fix log bugs.  Remove lint.
+ *
+ * Revision 5.19  1991/09/26  23:10:30  eggert
+ * Plug file descriptor leak.
+ *
+ * Revision 5.18  1991/09/18  07:29:10  eggert
+ * Work around a common ftruncate() bug.
+ *
+ * Revision 5.17  1991/09/10  22:15:46  eggert
+ * Fix test for redirected stdin.
+ *
+ * Revision 5.16  1991/08/19  23:17:54  eggert
+ * When there are no changes, revert to previous revision instead of aborting.
+ * Add piece tables, -M, -r$.  Tune.
+ *
+ * Revision 5.15  1991/04/21  11:58:14  eggert
+ * Ensure that working file is newer than RCS file after ci -[lu].
+ * Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.14  1991/02/28  19:18:47  eggert
+ * Don't let a setuid ci create a new RCS file; rcs -i -a must be run first.
+ * Fix ci -ko -l mode bug.  Open work file at most once.
+ *
+ * Revision 5.13  1991/02/25  07:12:33  eggert
+ * getdate -> getcurdate (SVR4 name clash)
+ *
+ * Revision 5.12  1990/12/31  01:00:12  eggert
+ * Don't use uninitialized storage when handling -{N,n}.
+ *
+ * Revision 5.11  1990/12/04  05:18:36  eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.10  1990/11/05  20:30:10  eggert
+ * Don't remove working file when aborting due to no changes.
+ *
+ * Revision 5.9  1990/11/01  05:03:23  eggert
+ * Add -I and new -t behavior.  Permit arbitrary data in logs.
+ *
+ * Revision 5.8  1990/10/04  06:30:09  eggert
+ * Accumulate exit status across files.
+ *
+ * Revision 5.7  1990/09/25  20:11:46  hammer
+ * fixed another small typo
+ *
+ * Revision 5.6  1990/09/24  21:48:50  hammer
+ * added cleanups from Paul Eggert.
+ *
+ * Revision 5.5  1990/09/21  06:16:38  hammer
+ * made it handle multiple -{N,n}'s.  Also, made it treat re-directed stdin
+ * the same as the terminal
+ *
+ * Revision 5.4  1990/09/20  02:38:51  eggert
+ * ci -k now checks dates more thoroughly.
+ *
+ * Revision 5.3  1990/09/11  02:41:07  eggert
+ * Fix revision bug with `ci -k file1 file2'.
+ *
+ * Revision 5.2  1990/09/04  08:02:10  eggert
+ * Permit adjacent revisions with identical time stamps (possible on fast hosts).
+ * Improve incomplete line handling.  Standardize yes-or-no procedure.
+ *
+ * Revision 5.1  1990/08/29  07:13:44  eggert
+ * Expand locker value like co.  Clean old log messages too.
+ *
+ * Revision 5.0  1990/08/22  08:10:00  eggert
+ * Don't require a final newline.
+ * Make lock and temp files faster and safer.
+ * Remove compile-time limits; use malloc instead.
+ * Permit dates past 1999/12/31.  Switch to GMT.
+ * Add setuid support.  Don't pass +args to diff.  Check diff's output.
+ * Ansify and Posixate.  Add -k, -V.  Remove snooping.  Tune.
+ * Check diff's output.
+ *
+ * Revision 4.9  89/05/01  15:10:54  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.8  88/11/08  13:38:23  narten
+ * changes from root@seismo.CSS.GOV (Super User)
+ * -d with no arguments uses the mod time of the file it is checking in
+ * 
+ * Revision 4.7  88/08/09  19:12:07  eggert
+ * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
+ * Use execv(), not system(); allow cc -R; remove lint.
+ * isatty(fileno(stdin)) -> ttystdin()
+ * 
+ * Revision 4.6  87/12/18  11:34:41  narten
+ * lint cleanups (from Guy Harris)
+ * 
+ * Revision 4.5  87/10/18  10:18:48  narten
+ * Updating version numbers. Changes relative to revision 1.1 are actually
+ * relative to 4.3
+ * 
+ * Revision 1.3  87/09/24  13:57:19  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.2  87/03/27  14:21:33  jenkins
+ * Port to suns
+ * 
+ * Revision 4.3  83/12/15  12:28:54  wft
+ * ci -u and ci -l now set mode of working file properly.
+ * 
+ * Revision 4.2  83/12/05  13:40:54  wft
+ * Merged with 3.9.1.1: added calls to clearerr(stdin).
+ * made rewriteflag external.
+ * 
+ * Revision 4.1  83/05/10  17:03:06  wft
+ * Added option -d and -w, and updated assingment of date, etc. to new delta.
+ * Added handling of default branches.
+ * Option -k generates std. log message; fixed undef. pointer in reading of log.
+ * Replaced getlock() with findlock(), link--unlink with rename(),
+ * getpwuid() with getcaller().
+ * Moved all revision number generation to new routine addelta().
+ * Removed calls to stat(); now done by pairfilenames().
+ * Changed most calls to catchints() with restoreints().
+ * Directed all interactive messages to stderr.
+ * 
+ * Revision 3.9.1.1  83/10/19  04:21:03  lepreau
+ * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
+ * 
+ * Revision 3.9  83/02/15  15:25:44  wft
+ * 4.2 prerelease
+ * 
+ * Revision 3.9  83/02/15  15:25:44  wft
+ * Added call to fastcopy() to copy remainder of RCS file.
+ *
+ * Revision 3.8  83/01/14  15:34:05  wft
+ * Added ignoring of interrupts while new RCS file is renamed;
+ * Avoids deletion of RCS files by interrupts.
+ *
+ * Revision 3.7  82/12/10  16:09:20  wft
+ * Corrected checking of return code from diff.
+ *
+ * Revision 3.6  82/12/08  21:34:49  wft
+ * Using DATEFORM to prepare date of checked-in revision;
+ * Fixed return from addbranch().
+ *
+ * Revision 3.5  82/12/04  18:32:42  wft
+ * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
+ * field lockedby in removelock(), moved getlogmsg() before calling diff.
+ *
+ * Revision 3.4  82/12/02  13:27:13  wft
+ * added option -k.
+ *
+ * Revision 3.3  82/11/28  20:53:31  wft
+ * Added mustcheckin() to check for redundant checkins.
+ * Added xpandfile() to do keyword expansion for -u and -l;
+ * -m appends linefeed to log message if necessary.
+ * getlogmsg() suppresses prompt if stdin is not a terminal.
+ * Replaced keeplock with lockflag, fclose() with ffclose(),
+ * %02d with %.2d, getlogin() with getpwuid().
+ *
+ * Revision 3.2  82/10/18  20:57:23  wft
+ * An RCS file inherits its mode during the first ci from the working file,
+ * otherwise it stays the same, except that write permission is removed.
+ * Fixed ci -l, added ci -u (both do an implicit co after the ci).
+ * Fixed call to getlogin(), added call to getfullRCSname(), added check
+ * for write error.
+ * Changed conflicting identifiers.
+ *
+ * Revision 3.1  82/10/13  16:04:59  wft
+ * fixed type of variables receiving from getc() (char -> int).
+ * added include file dbm.h for getting BYTESIZ. This is used
+ * to check the return code from diff portably.
+ */
+
+#include "rcsbase.h"
+
+struct Symrev {
+       char const *ssymbol;
+       int override;
+       struct Symrev * nextsym;
+};
+
+static char const *getcurdate P((void));
+static int addbranch P((struct hshentry*,struct buf*));
+static int addelta P((void));
+static int addsyms P((char const*));
+static int fixwork P((mode_t,char const*));
+static int removelock P((struct hshentry*));
+static int xpandfile P((RILE*,char const*,struct hshentry const*,char const**));
+static struct cbuf getlogmsg P((void));
+static void cleanup P((void));
+static void incnum P((char const*,struct buf*));
+static void addassoclst P((int, char *));
+
+static FILE *exfile;
+static RILE *workptr;                  /* working file pointer         */
+static struct buf newdelnum;           /* new revision number          */
+static struct cbuf msg;
+static int exitstatus;
+static int forceciflag;                        /* forces check in              */
+static int keepflag, keepworkingfile, rcsinitflag;
+static struct hshentries *gendeltas;   /* deltas to be generated       */
+static struct hshentry *targetdelta;   /* old delta to be generated    */
+static struct hshentry newdelta;       /* new delta to be inserted     */
+static struct stat workstat;
+static struct Symrev *assoclst, *lastassoc;
+
+mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $")
+{
+       static char const cmdusage[] =
+               "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ...";
+       static char const default_state[] = DEFAULTSTATE;
+
+       char altdate[datesize];
+       char olddate[datesize];
+       char newdatebuf[datesize], targetdatebuf[datesize];
+       char *a, **newargv, *textfile;
+       char const *author, *krev, *rev, *state;
+       char const *diffilename, *expfilename;
+       char const *workdiffname, *newworkfilename;
+       char const *mtime;
+       int lockflag, lockthis, mtimeflag, removedlock;
+       int r;
+       int changedRCS, changework, newhead;
+       int usestatdate; /* Use mod time of file for -d.  */
+       mode_t newworkmode; /* mode for working file */
+       struct hshentry *workdelta;
+       
+       setrid();
+
+       author = rev = state = textfile = nil;
+       lockflag = false;
+       mtimeflag = false;
+       altdate[0]= '\0'; /* empty alternate date for -d */
+       usestatdate=false;
+       suffixes = X_DEFAULT;
+
+       argc = getRCSINIT(argc, argv, &newargv);
+       argv = newargv;
+       while (a = *++argv,  0<--argc && *a++=='-') {
+               switch (*a++) {
+
+                case 'r':
+                       keepworkingfile = lockflag = false;
+               revno:
+                       if (*a) {
+                               if (rev) warn("redefinition of revision number");
+                               rev = a;
+                        }
+                        break;
+
+                case 'l':
+                        keepworkingfile=lockflag=true;
+                        goto revno;
+
+                case 'u':
+                        keepworkingfile=true; lockflag=false;
+                        goto revno;
+
+               case 'I':
+                       interactiveflag = true;
+                       goto revno;
+
+                case 'q':
+                        quietflag=true;
+                        goto revno;
+
+                case 'f':
+                        forceciflag=true;
+                        goto revno;
+
+                case 'k':
+                        keepflag=true;
+                        goto revno;
+
+                case 'm':
+                       if (msg.size) redefined('m');
+                       msg = cleanlogmsg(a, strlen(a));
+                       if (!msg.size)
+                               warn("missing message for -m option");
+                        break;
+
+                case 'n':
+                       if (!*a) {
+                                error("missing symbolic name after -n");
+                               break;
+                       }
+                       checksid(a);
+                       addassoclst(false, a);
+                       break;
+               
+               case 'N':
+                       if (!*a) {
+                                error("missing symbolic name after -N");
+                               break;
+                       }
+                       checksid(a);
+                       addassoclst(true, a);
+                       break;
+
+                case 's':
+                       if (*a) {
+                               if (state) redefined('s');
+                               checksid(a);
+                               state = a;
+                       } else
+                               warn("missing state for -s option");
+                        break;
+
+                case 't':
+                       if (*a) {
+                               if (textfile) redefined('t');
+                               textfile = a;
+                        }
+                        break;
+
+               case 'd':
+                       if (altdate[0] || usestatdate)
+                               redefined('d');
+                       altdate[0] = 0;
+                       if (!(usestatdate = !*a))
+                               str2date(a, altdate);
+                        break;
+
+               case 'M':
+                       mtimeflag = true;
+                       goto revno;
+
+               case 'w':
+                       if (*a) {
+                               if (author) redefined('w');
+                               checksid(a);
+                               author = a;
+                       } else
+                               warn("missing author for -w option");
+                        break;
+
+               case 'x':
+                       suffixes = a;
+                       break;
+
+               case 'V':
+                       setRCSversion(*argv);
+                       break;
+
+
+
+                default:
+                       faterror("unknown option: %s%s", *argv, cmdusage);
+                };
+        }  /* end processing of options */
+
+       if (argc<1) faterror("no input file%s", cmdusage);
+
+        /* now handle all filenames */
+        do {
+        targetdelta=nil;
+       ffree();
+
+       switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
+
+        case -1:                /* New RCS file */
+#              if has_setuid && has_getuid
+                   if (euid() != ruid()) {
+                       error("setuid initial checkin prohibited; use `rcs -i -a' first");
+                       continue;
+                   }
+#              endif
+               rcsinitflag = true;
+                break;
+
+        case 0:                 /* Error */
+                continue;
+
+        case 1:                 /* Normal checkin with prev . RCS file */
+               rcsinitflag = !Head;
+        }
+
+        /* now RCSfilename contains the name of the RCS file, and
+         * workfilename contains the name of the working file.
+        * If the RCS file exists, finptr contains the file descriptor for the
+         * RCS file. The admin node is initialized.
+        * RCSstat is set.
+         */
+
+       diagnose("%s  <--  %s\n", RCSfilename,workfilename);
+
+       if (!(workptr = Iopen(workfilename, FOPEN_R_WORK, &workstat))) {
+               eerror(workfilename);
+               continue;
+       }
+       if (finptr && !checkaccesslist()) continue; /* give up */
+
+       krev = rev;
+        if (keepflag) {
+                /* get keyword values from working file */
+               if (!getoldkeys(workptr)) continue;
+               if (!rev  &&  !*(krev = prevrev.string)) {
+                       error("can't find a revision number in %s",workfilename);
+                        continue;
+                }
+               if (!*prevdate.string && *altdate=='\0' && usestatdate==false)
+                       warn("can't find a date in %s", workfilename);
+               if (!*prevauthor.string && !author)
+                       warn("can't find an author in %s", workfilename);
+               if (!*prevstate.string && !state)
+                       warn("can't find a state in %s", workfilename);
+        } /* end processing keepflag */
+
+       /* Read the delta tree.  */
+       if (finptr)
+           gettree();
+
+        /* expand symbolic revision number */
+       if (!fexpandsym(krev, &newdelnum, workptr))
+           continue;
+
+        /* splice new delta into tree */
+       if ((removedlock = addelta()) < 0)
+           continue;
+
+       newdelta.num = newdelnum.string;
+        newdelta.branches=nil;
+        newdelta.lockedby=nil; /*might be changed by addlock() */
+       newdelta.selector = true;
+       /* set author */
+       if (author!=nil)
+               newdelta.author=author;     /* set author given by -w         */
+       else if (keepflag && *prevauthor.string)
+               newdelta.author=prevauthor.string; /* preserve old author if possible*/
+       else    newdelta.author=getcaller();/* otherwise use caller's id      */
+       newdelta.state = default_state;
+       if (state!=nil)
+               newdelta.state=state;       /* set state given by -s          */
+       else if (keepflag && *prevstate.string)
+               newdelta.state=prevstate.string;   /* preserve old state if possible */
+       if (usestatdate) {
+           time2date(workstat.st_mtime, altdate);
+       }
+       if (*altdate!='\0')
+               newdelta.date=altdate;      /* set date given by -d           */
+       else if (keepflag && *prevdate.string) {
+               /* Preserve old date if possible.  */
+               str2date(prevdate.string, olddate);
+               newdelta.date = olddate;
+       } else
+               newdelta.date = getcurdate();  /* use current date */
+       /* now check validity of date -- needed because of -d and -k          */
+       if (targetdelta!=nil &&
+           cmpnum(newdelta.date,targetdelta->date) < 0) {
+               error("Date %s precedes %s in existing revision %s.",
+                       date2str(newdelta.date, newdatebuf),
+                       date2str(targetdelta->date, targetdatebuf),
+                       targetdelta->num
+               );
+               continue;
+       }
+
+
+       if (lockflag  &&  addlock(&newdelta) < 0) continue;
+       if (!addsyms(newdelta.num))
+           continue;
+
+    
+        putadmin(frewrite);
+        puttree(Head,frewrite);
+       putdesc(false,textfile);
+
+       changework = Expand != OLD_EXPAND;
+       lockthis = lockflag;
+       workdelta = &newdelta;
+
+        /* build rest of file */
+       if (rcsinitflag) {
+               diagnose("initial revision: %s\n", newdelnum.string);
+                /* get logmessage */
+                newdelta.log=getlogmsg();
+               if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
+               RCSstat.st_mode = workstat.st_mode;
+               changedRCS = true;
+        } else {
+               diffilename = maketemp(0);
+               workdiffname = workfilename;
+               if (workdiffname[0] == '+') {
+                   /* Some diffs have options with leading '+'.  */
+                   char *dp = ftnalloc(char, strlen(workfilename)+3);
+                   workdiffname = dp;
+                   *dp++ = '.';
+                   *dp++ = SLASH;
+                   VOID strcpy(dp, workfilename);
+               }
+               newhead  =  Head == &newdelta;
+               if (!newhead)
+                       foutptr = frewrite;
+               expfilename = buildrevision(
+                       gendeltas, targetdelta, (FILE*)0, false
+               );
+               if (
+                   !forceciflag  &&
+                   (changework = rcsfcmp(
+                       workptr, &workstat, expfilename, targetdelta
+                   )) <= 0
+               ) {
+                   diagnose("file is unchanged; reverting to previous revision %s\n",
+                       targetdelta->num
+                   );
+                   if (removedlock < lockflag) {
+                       diagnose("previous revision was not locked; ignoring -l option\n");
+                       lockthis = 0;
+                   }
+                   if (!(changedRCS  =
+                           lockflag < removedlock
+                       ||  assoclst
+                       ||      newdelta.state != default_state
+                           &&  strcmp(newdelta.state, targetdelta->state) != 0
+                   ))
+                       workdelta = targetdelta;
+                   else {
+                       /*
+                        * We have started to build the wrong new RCS file.
+                        * Start over from the beginning.
+                        */
+                       long hwm = ftell(frewrite);
+                       int bad_truncate;
+                       if (fseek(frewrite, 0L, SEEK_SET) != 0)
+                           Oerror();
+#                      if !has_ftruncate
+                           bad_truncate = 1;
+#                      else
+                           /*
+                            * Work around a common ftruncate() bug.
+                            * We can't rely on has_truncate, because we might
+                            * be using a filesystem exported to us via NFS.
+                            */
+                           bad_truncate = ftruncate(fileno(frewrite),(off_t)0);
+                           if (bad_truncate  &&  errno != EACCES)
+                               Oerror();
+#                      endif
+                       Irewind(finptr);
+                       Lexinit();
+                       getadmin();
+                       gettree();
+                       if (!(workdelta = genrevs(
+                           targetdelta->num, (char*)0, (char*)0, (char*)0,
+                           &gendeltas
+                       )))
+                           continue;
+                       workdelta->log = targetdelta->log;
+                       if (newdelta.state != default_state)
+                           workdelta->state = newdelta.state;
+                       if (removedlock && removelock(workdelta)<0)
+                           continue;
+                       if (!addsyms(workdelta->num))
+                           continue;
+                       if (!dorewrite(true, true))
+                           continue;
+                       fastcopy(finptr, frewrite);
+                       if (bad_truncate)
+                           while (ftell(frewrite) < hwm)
+                               /* White out any earlier mistake with '\n's.  */
+                               /* This is unlikely.  */
+                               afputc('\n', frewrite);
+                   }
+               } else {
+                   diagnose("new revision: %s; previous revision: %s\n",
+                       newdelnum.string, targetdelta->num
+                   );
+                   newdelta.log = getlogmsg();
+                   switch (run((char*)0, diffilename,
+                       DIFF DIFF_FLAGS,
+                       newhead ? workdiffname : expfilename,
+                       newhead ? expfilename : workdiffname,
+                       (char*)0
+                   )) {
+                       case DIFF_FAILURE: case DIFF_SUCCESS: break;
+                       default: faterror("diff failed");
+                   }
+                   if (newhead) {
+                       Irewind(workptr);
+                       if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
+                       if (!putdtext(targetdelta->num,targetdelta->log,diffilename,frewrite,true)) continue;
+                   } else
+                       if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue;
+                   changedRCS = true;
+                }
+        }
+       if (!donerewrite(changedRCS))
+               continue;
+
+        if (!keepworkingfile) {
+               Izclose(&workptr);
+               r = un_link(workfilename); /* Get rid of old file */
+        } else {
+               newworkmode = WORKMODE(RCSstat.st_mode,
+                       !   (Expand==VAL_EXPAND  ||  lockthis < StrictLocks)
+               );
+               mtime = mtimeflag ? workdelta->date : (char const*)0;
+
+               /* Expand if it might change or if we can't fix mode, time.  */
+               if (changework  ||  (r=fixwork(newworkmode,mtime)) != 0) {
+                   Irewind(workptr);
+                   /* Expand keywords in file.  */
+                   locker_expansion = lockthis;
+                   switch (xpandfile(
+                       workptr, workfilename,
+                       workdelta, &newworkfilename
+                   )) {
+                       default:
+                           continue;
+
+                       case 0:
+                           /*
+                            * No expansion occurred; try to reuse working file
+                            * unless we already tried and failed.
+                            */
+                           if (changework)
+                               if ((r=fixwork(newworkmode,mtime)) == 0)
+                                   break;
+                           /* fall into */
+                       case 1:
+                           if (!(r = setfiledate(newworkfilename,mtime))) {
+                               Izclose(&workptr);
+                               ignoreints();
+                               r = chnamemod(&exfile, newworkfilename, workfilename, newworkmode);
+                               keepdirtemp(newworkfilename);
+                               restoreints();
+                           }
+                   }
+               }
+        }
+       if (r != 0) {
+           eerror(workfilename);
+           continue;
+       }
+       diagnose("done\n");
+
+        } while (cleanup(),
+                 ++argv, --argc >=1);
+
+       tempunlink();
+       exitmain(exitstatus);
+}       /* end of main (ci) */
+
+       static void
+cleanup()
+{
+       if (nerror) exitstatus = EXIT_FAILURE;
+       Izclose(&finptr);
+       Izclose(&workptr);
+       Ozclose(&exfile);
+       Ozclose(&fcopy);
+       Ozclose(&frewrite);
+       dirtempunlink();
+}
+
+#if lint
+#      define exiterr ciExit
+#endif
+       exiting void
+exiterr()
+{
+       dirtempunlink();
+       tempunlink();
+       _exit(EXIT_FAILURE);
+}
+
+/*****************************************************************/
+/* the rest are auxiliary routines                               */
+
+
+       static int
+addelta()
+/* Function: Appends a delta to the delta tree, whose number is
+ * given by newdelnum.  Updates Head, newdelnum, newdelnumlength,
+ * and the links in newdelta.
+ * Return -1 on error, 1 if a lock is removed, 0 otherwise.
+ */
+{
+       register char *tp;
+       register unsigned i;
+       int removedlock;
+       unsigned newdnumlength;  /* actual length of new rev. num. */
+
+       newdnumlength = countnumflds(newdelnum.string);
+
+       if (rcsinitflag) {
+                /* this covers non-existing RCS file and a file initialized with rcs -i */
+               if ((newdnumlength==0)&&(Dbranch!=nil)) {
+                       bufscpy(&newdelnum, Dbranch);
+                       newdnumlength = countnumflds(Dbranch);
+               }
+               if (newdnumlength==0) bufscpy(&newdelnum, "1.1");
+               else if (newdnumlength==1) bufscat(&newdelnum, ".1");
+               else if (newdnumlength>2) {
+                   error("Branch point doesn't exist for %s.",newdelnum.string);
+                   return -1;
+                } /* newdnumlength == 2 is OK;  */
+                Head = &newdelta;
+                newdelta.next=nil;
+               return 0;
+        }
+        if (newdnumlength==0) {
+                /* derive new revision number from locks */
+               switch (findlock(true, &targetdelta)) {
+
+                 default:
+                   /* found two or more old locks */
+                   return -1;
+
+                 case 1:
+                    /* found an old lock */
+                    /* check whether locked revision exists */
+                   if (!genrevs(targetdelta->num,(char*)0,(char*)0,(char*)0,&gendeltas))
+                       return -1;
+                    if (targetdelta==Head) {
+                        /* make new head */
+                        newdelta.next=Head;
+                        Head= &newdelta;
+                   } else if (!targetdelta->next && countnumflds(targetdelta->num)>2) {
+                        /* new tip revision on side branch */
+                        targetdelta->next= &newdelta;
+                        newdelta.next = nil;
+                    } else {
+                        /* middle revision; start a new branch */
+                       bufscpy(&newdelnum, "");
+                       return addbranch(targetdelta,&newdelnum);
+                    }
+                   incnum(targetdelta->num, &newdelnum);
+                   return 1; /* successful use of existing lock */
+
+                 case 0:
+                    /* no existing lock; try Dbranch */
+                    /* update newdelnum */
+                   if (StrictLocks || !myself(RCSstat.st_uid)) {
+                       error("no lock set by %s",getcaller());
+                       return -1;
+                    }
+                    if (Dbranch) {
+                       bufscpy(&newdelnum, Dbranch);
+                    } else {
+                       incnum(Head->num, &newdelnum);
+                    }
+                   newdnumlength = countnumflds(newdelnum.string);
+                    /* now fall into next statement */
+                }
+        }
+        if (newdnumlength<=2) {
+                /* add new head per given number */
+                if(newdnumlength==1) {
+                    /* make a two-field number out of it*/
+                   if (cmpnumfld(newdelnum.string,Head->num,1)==0)
+                       incnum(Head->num, &newdelnum);
+                   else
+                       bufscat(&newdelnum, ".1");
+                }
+               if (cmpnum(newdelnum.string,Head->num) <= 0) {
+                    error("deltanumber %s too low; must be higher than %s",
+                         newdelnum.string, Head->num);
+                   return -1;
+                }
+               targetdelta = Head;
+               if (0 <= (removedlock = removelock(Head))) {
+                   if (!genrevs(Head->num,(char*)0,(char*)0,(char*)0,&gendeltas))
+                       return -1;
+                   newdelta.next = Head;
+                   Head = &newdelta;
+               }
+               return removedlock;
+        } else {
+                /* put new revision on side branch */
+                /*first, get branch point */
+               tp = newdelnum.string;
+               for (i = newdnumlength - (newdnumlength&1 ^ 1);  (--i);  )
+                       while (*tp++ != '.')
+                               ;
+               *--tp = 0; /* Kill final dot to get old delta temporarily. */
+               if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas)))
+                   return -1;
+               if (cmpnum(targetdelta->num, newdelnum.string) != 0) {
+                   error("can't find branchpoint %s", newdelnum.string);
+                   return -1;
+                }
+               *tp = '.'; /* Restore final dot. */
+               return addbranch(targetdelta,&newdelnum);
+        }
+}
+
+
+
+       static int
+addbranch(branchpoint,num)
+       struct hshentry *branchpoint;
+       struct buf *num;
+/* adds a new branch and branch delta at branchpoint.
+ * If num is the null string, appends the new branch, incrementing
+ * the highest branch number (initially 1), and setting the level number to 1.
+ * the new delta and branchhead are in globals newdelta and newbranch, resp.
+ * the new number is placed into num.
+ * Return -1 on error, 1 if a lock is removed, 0 otherwise.
+ */
+{
+       struct branchhead *bhead, **btrail;
+       struct buf branchnum;
+       int removedlock, result;
+       unsigned field, numlength;
+       static struct branchhead newbranch;  /* new branch to be inserted */
+
+       numlength = countnumflds(num->string);
+
+        if (branchpoint->branches==nil) {
+                /* start first branch */
+                branchpoint->branches = &newbranch;
+                if (numlength==0) {
+                       bufscpy(num, branchpoint->num);
+                       bufscat(num, ".1.1");
+               } else if (numlength&1)
+                       bufscat(num, ".1");
+                newbranch.nextbranch=nil;
+
+       } else if (numlength==0) {
+                /* append new branch to the end */
+                bhead=branchpoint->branches;
+                while (bhead->nextbranch) bhead=bhead->nextbranch;
+                bhead->nextbranch = &newbranch;
+               bufautobegin(&branchnum);
+               getbranchno(bhead->hsh->num, &branchnum);
+               incnum(branchnum.string, num);
+               bufautoend(&branchnum);
+               bufscat(num, ".1");
+                newbranch.nextbranch=nil;
+        } else {
+                /* place the branch properly */
+               field = numlength - (numlength&1 ^ 1);
+                /* field of branch number */
+               btrail = &branchpoint->branches;
+               while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) {
+                       btrail = &(*btrail)->nextbranch;
+                       if (!*btrail) {
+                               result = -1;
+                               break;
+                       }
+                }
+               if (result < 0) {
+                        /* insert/append new branchhead */
+                       newbranch.nextbranch = *btrail;
+                       *btrail = &newbranch;
+                       if (numlength&1) bufscat(num, ".1");
+                } else {
+                        /* branch exists; append to end */
+                       bufautobegin(&branchnum);
+                       getbranchno(num->string, &branchnum);
+                       targetdelta=genrevs(branchnum.string,(char*)nil,
+                                           (char*)nil,(char*)nil,&gendeltas);
+                       bufautoend(&branchnum);
+                       if (!targetdelta)
+                           return -1;
+                       if (cmpnum(num->string,targetdelta->num) <= 0) {
+                                error("deltanumber %s too low; must be higher than %s",
+                                     num->string,targetdelta->num);
+                               return -1;
+                        }
+                       if (0 <= (removedlock = removelock(targetdelta))) {
+                           if (numlength&1)
+                               incnum(targetdelta->num,num);
+                           targetdelta->next = &newdelta;
+                           newdelta.next = 0;
+                       }
+                       return removedlock;
+                       /* Don't do anything to newbranch.  */
+                }
+        }
+        newbranch.hsh = &newdelta;
+        newdelta.next=nil;
+       return 0;
+}
+
+       static int
+addsyms(num)
+       char const *num;
+{
+       register struct Symrev *p;
+
+       for (p = assoclst;  p;  p = p->nextsym)
+               if (!addsymbol(num, p->ssymbol, p->override))
+                       return false;
+       return true;
+}
+
+
+       static void
+incnum(onum,nnum)
+       char const *onum;
+       struct buf *nnum;
+/* Increment the last field of revision number onum by one and
+ * place the result into nnum.
+ */
+{
+       register char *tp, *np;
+       register size_t l;
+
+       l = strlen(onum);
+       bufalloc(nnum, l+2);
+       np = tp = nnum->string;
+       VOID strcpy(np, onum);
+       for (tp = np + l;  np != tp;  )
+               if (isdigit(*--tp)) {
+                       if (*tp != '9') {
+                               ++*tp;
+                               return;
+                       }
+                       *tp = '0';
+               } else {
+                       tp++;
+                       break;
+               }
+       /* We changed 999 to 000; now change it to 1000.  */
+       *tp = '1';
+       tp = np + l;
+       *tp++ = '0';
+       *tp = 0;
+}
+
+
+
+       static int
+removelock(delta)
+struct hshentry * delta;
+/* function: Finds the lock held by caller on delta,
+ * removes it, and returns nonzero if successful.
+ * Print an error message and return -1 if there is no such lock.
+ * An exception is if !StrictLocks, and caller is the owner of
+ * the RCS file. If caller does not have a lock in this case,
+ * return 0; return 1 if a lock is actually removed.
+ */
+{
+       register struct lock *next, **trail;
+       char const *num;
+
+        num=delta->num;
+       for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
+           if (next->delta == delta)
+               if (strcmp(getcaller(), next->login) == 0) {
+                   /* We found a lock on delta by caller; delete it.  */
+                   *trail = next->nextlock;
+                   delta->lockedby = 0;
+                   return 1;
+               } else {
+                    error("revision %s locked by %s",num,next->login);
+                   return -1;
+                }
+       if (!StrictLocks && myself(RCSstat.st_uid))
+           return 0;
+       error("no lock set by %s for revision %s", getcaller(), num);
+       return -1;
+}
+
+
+
+       static char const *
+getcurdate()
+/* Return a pointer to the current date.  */
+{
+       static char buffer[datesize]; /* date buffer */
+       time_t t;
+
+       if (!buffer[0]) {
+               t = time((time_t *)0);
+               if (t == -1)
+                       faterror("time not available");
+               time2date(t, buffer);
+       }
+        return buffer;
+}
+
+       static int
+#if has_prototypes
+fixwork(mode_t newworkmode, char const *mtime)
+  /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
+#else
+  fixwork(newworkmode, mtime)
+       mode_t newworkmode;
+       char const *mtime;
+#endif
+{
+       int r;
+       return
+                       1 < workstat.st_nlink
+                   ||  newworkmode&S_IWUSR && !myself(workstat.st_uid)
+               ?   -1
+           :
+                       workstat.st_mode != newworkmode
+                   &&
+                       (r =
+#                          if has_fchmod
+                               fchmod(Ifileno(workptr), newworkmode)
+#                          else
+                               chmod(workfilename, newworkmode)
+#                          endif
+                       ) != 0
+               ?   r
+           :
+               setfiledate(workfilename, mtime);
+}
+
+       static int
+xpandfile(unexfile, dir, delta, exfilename)
+       RILE *unexfile;
+       char const *dir;
+       struct hshentry const *delta;
+       char const **exfilename;
+/*
+ * Read unexfile and copy it to a
+ * file in dir, performing keyword substitution with data from delta.
+ * Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise.
+ * If successful, stores the stream descriptor into *EXFILEP
+ * and its name into *EXFILENAME.
+ */
+{
+       char const *targetfname;
+       int e, r;
+
+       targetfname = makedirtemp(dir, 1);
+       if (!(exfile = fopen(targetfname, FOPEN_W_WORK))) {
+               eerror(targetfname);
+               error("can't expand working file");
+               return -1;
+        }
+       r = 0;
+       if (Expand == OLD_EXPAND)
+               fastcopy(unexfile,exfile);
+       else {
+               for (;;) {
+                       e = expandline(unexfile,exfile,delta,false,(FILE*)nil);
+                       if (e < 0)
+                               break;
+                       r |= e;
+                       if (e <= 1)
+                               break;
+               }
+       }
+       *exfilename = targetfname;
+       aflush(exfile);
+       return r & 1;
+}
+
+
+
+
+/* --------------------- G E T L O G M S G --------------------------------*/
+
+
+       static struct cbuf
+getlogmsg()
+/* Obtain and yield a log message.
+ * If a log message is given with -m, yield that message.
+ * If this is the initial revision, yield a standard log message.
+ * Otherwise, reads a character string from the terminal.
+ * Stops after reading EOF or a single '.' on a
+ * line. getlogmsg prompts the first time it is called for the
+ * log message; during all later calls it asks whether the previous
+ * log message can be reused.
+ */
+{
+       static char const
+               emptych[] = EMPTYLOG,
+               initialch[] = "Initial revision";
+       static struct cbuf const
+               emptylog = { emptych, sizeof(emptych)-sizeof(char) },
+               initiallog = { initialch, sizeof(initialch)-sizeof(char) };
+       static struct buf logbuf;
+       static struct cbuf logmsg;
+
+       register char *tp;
+       register size_t i;
+       char const *caller;
+
+       if (msg.size) return msg;
+
+       if (keepflag) {
+               /* generate std. log message */
+               caller = getcaller();
+               i = sizeof(ciklog)+strlen(caller)+3;
+               bufalloc(&logbuf, i+datesize);
+               tp = logbuf.string;
+               VOID sprintf(tp, "%s%s at ", ciklog, caller);
+               VOID date2str(getcurdate(), tp+i);
+               logmsg.string = tp;
+               logmsg.size = strlen(tp);
+               return logmsg;
+       }
+
+       if (!targetdelta && (
+               cmpnum(newdelnum.string,"1.1")==0 ||
+               cmpnum(newdelnum.string,"1.0")==0
+       ))
+               return initiallog;
+
+       if (logmsg.size) {
+                /*previous log available*/
+           if (yesorno(true, "reuse log message of previous file? [yn](y): "))
+               return logmsg;
+        }
+
+        /* now read string from stdin */
+       logmsg = getsstdin("m", "log message", "", &logbuf);
+
+        /* now check whether the log message is not empty */
+       if (logmsg.size)
+               return logmsg;
+       return emptylog;
+}
+
+/*  Make a linked list of Symbolic names  */
+
+        static void
+addassoclst(flag, sp)
+int  flag;
+char * sp;
+{
+        struct Symrev *pt;
+       
+       pt = talloc(struct Symrev);
+       pt->ssymbol = sp;
+       pt->override = flag;
+       pt->nextsym = nil;
+       if (lastassoc)
+               lastassoc->nextsym = pt;
+       else
+               assoclst = pt;
+       lastassoc = pt;
+       return;
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/co.c b/usr/src/contrib/rcs-V5.6/src/co.c
new file mode 100644 (file)
index 0000000..9435574
--- /dev/null
@@ -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;
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/conf.heg b/usr/src/contrib/rcs-V5.6/src/conf.heg
new file mode 100644 (file)
index 0000000..c03fea8
--- /dev/null
@@ -0,0 +1,495 @@
+/* example RCS compile-time configuration */
+
+       /* $Id: conf.heg,v 1.8 1991/11/20 18:21:09 eggert Exp $ */
+
+/*
+ * This example RCS compile-time configuration describes a host that conforms
+ * to Standard C (1990) and Posix 1003.1-1990 and has GNU diff.
+ * If you can't get conf.sh to work as described in the Makefile,
+ * copy this file to conf.h and edit conf.h by hand; see README.
+ */
+
+#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 0 /* Does #include <sys/dir.h> work?  */
+#define has_sys_param_h 0 /* Does #include <sys/param.h> work?  */
+#define has_readlink 0 /* 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 1 /* Does fputs() work?  */
+#define has_ftruncate 0 /* 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 0 /* 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 0 /* Does getwd() work?  */
+#define has_mktemp 0 /* Does mktemp() work?  */
+#define has_NFS 0 /* 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 1 /* Does waitpid() work?  */
+#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */
+#define has_vfprintf 1 /* Does vfprintf() work?  */
+#define has__doprintf 0 /* Does _doprintf() work?  */
+#define has__doprnt 0 /* 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/local/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/local/bin/diff" /* name of 'diff' program */
+#define DIFF3 "/usr/local/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/local/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 "/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*));
diff --git a/usr/src/contrib/rcs-V5.6/src/conf.sh b/usr/src/contrib/rcs-V5.6/src/conf.sh
new file mode 100644 (file)
index 0000000..c046c50
--- /dev/null
@@ -0,0 +1,1671 @@
+#!/bin/sh
+# Output RCS compile-time configuration.
+Id='$Id: conf.sh,v 5.14 1991/11/20 18:21: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
+
+
+# Standard output should already be directed to "a.h";
+# later parts of this procedure need it.
+# Standard error can be ignored if a.h is OK,
+# and can be inspected for clues otherwise.
+
+# The Makefile overrides the following defaults.
+: ${CC=cc}
+: ${CFLAGS=-O}
+: ${COMPAT2=0}
+: ${DIFF3=${RCSPREFIX}diff3}
+: ${DIFF3_BIN=1}
+: ${DIFF=${RCSPREFIX}diff}
+: ${DIFF_FLAGS=-an}
+: ${DIFF_L=1}
+: ${DIFF_SUCCESS=0} ${DIFF_FAILURE=1} ${DIFF_TROUBLE=2}
+: ${ED=/bin/ed}
+: ${RCSPREFIX=/usr/local/bin/}
+: ${SENDMAIL='"/usr/lib/sendmail"'}
+# : ${LDFLAGS=} ${LDLIBS=} tickles old shell bug
+
+C="$CC $CFLAGS"
+CL="$CC $CFLAGS $LDFLAGS"
+L=$LDLIBS
+RM='rm -f a.out'
+
+cat <<EOF
+/* RCS compile-time configuration */
+
+       /* $Id */
+
+/*
+ * 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.
+ */
+
+EOF
+
+: exitmain
+cat >a.c <<EOF
+#include "a.h"
+int main(argc,argv) int argc; char **argv; { return argc-1; }
+EOF
+$RM && $CL a.c $L >&2 || exit
+e='exit(n), 3 /* lint fodder */'
+if ./a.out -
+then :
+elif ./a.out
+then e=n
+fi
+echo "#define exitmain(n) return $e /* how to exit from main() */"
+
+: _POSIX_SOURCE
+cat >a.c <<'EOF'
+#include "a.h"
+#include <stdio.h>
+int main() { exitmain(fileno(stdout) < 0); }
+EOF
+a='/* ' z='*/ '
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then :
+elif $RM || exit; ($CL -D_POSIX_SOURCE a.c $L && ./a.out) >&2
+then a= z=
+fi
+cat <<EOF
+$a#define _POSIX_SOURCE $z/* Define this if Posix + strict Standard C.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+EOF
+
+cat <<'EOF'
+
+/* Comment out #include lines below that do not work.  */
+EOF
+
+# Run `$CS a.c $LS' instead of `$CL a.c $L' for compile-time checking only.
+# This speeds up the configuration process.
+if $C -S a.c >&2
+then CS="$C -S" LS=    # Generate assembly language output.
+elif $C -c a.c >&2
+then CS="$C -c" LS=    # Generate object code.
+else CS=$CL LS=$L      # Generate an executable.
+fi
+
+# standard include files
+# sys/types.h and sys/stat.h must come first because others depend on them.
+has_signal=1
+for h in \
+       sys/types sys/stat \
+       dirent fcntl limits pwd signal stdlib string sys/mman sys/wait unistd utime vfork
+do
+       i="#include <$h.h>"
+       : $i
+       cat >a.c <<EOF
+#include "a.h"
+$i
+int main(){ exitmain(0); }
+EOF
+       $RM || exit
+       ($CL a.c $L && ./a.out) >&2 || {
+               case $h in
+               string)
+                       i='#include <strings.h>';;
+               *)
+                       i="/* $i */"
+               esac
+               case $h in
+               signal) has_signal=0
+               esac
+       }
+       echo "$i"
+done
+
+cat <<'EOF'
+
+/* Define the following symbols to be 1 or 0.  */
+EOF
+
+# has_sys_*_h
+for H in dir param
+do
+       : has_sys_${H}_h
+       cat >a.c <<EOF
+#include "a.h"
+#include <sys/$H.h>
+int main() { exitmain(0); }
+EOF
+       $RM || exit
+       if ($CL a.c $L && ./a.out) >&2
+       then h=1
+       else h=0
+       fi
+       echo "#define has_sys_${H}_h $h /* Does #include <sys/$H.h> work?  */"
+done
+
+
+# We must do NAME_MAX and has_readlink next, because they might generate
+# #include directives that affect later definitions.
+
+: NAME_MAX
+cat >a.c <<'EOF'
+#include "a.h"
+char b[NAME_MAX + 2];
+main()
+{
+#if !defined(NAME_MAX)
+       exitmain(1);
+#else
+       int i;
+       b[0] = 'a'; b[1] = '.';
+       for (i = 2;  i < NAME_MAX;  i++)
+               b[i] = 'a';
+       b[i] = 'b';
+       exitmain(creat(b, 0) < 0);
+#endif
+}
+EOF
+$RM a.*ab || exit
+if $CL a.c $L >&2 && ./a.out && test -f a.*ab
+then a= z=
+else a='/* ' z='*/ '
+fi
+rm -f a.*ab || exit
+
+: has_readlink
+cat >a.c <<'EOF'
+#include "a.h"
+char b[7];
+int main()
+{
+       exitmain(readlink("a.sym2",b,7) != 6  ||  strcmp(b, "a.sym1") != 0);
+}
+EOF
+$RM a.sym* || exit
+if (ln -s a.sym1 a.sym2 && $CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+cat <<EOF
+#define has_readlink $h /* Does readlink() work?  */
+
+$a#undef NAME_MAX $z/* 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
+EOF
+
+cat <<'EOF'
+
+/* Comment out the keyword definitions below if the keywords work.  */
+EOF
+
+: const, volatile
+for i in const volatile
+do
+       cat >a.c <<EOF
+#      include "a.h"
+       enum Boolean { false, true };
+       static enum Boolean $i zero;
+       static enum Boolean $i * $i azero = &zero;
+       static enum Boolean $i * $i * $i aazero = &azero;
+       int main() { exitmain(!!**aazero); }
+EOF
+       a= z=
+       if $CS a.c $LS >&2
+       then
+               cat >a.c <<EOF
+                       typedef unsigned char $i *Iptr_type;
+                       struct { Iptr_type lim; } s, *f = &s;
+                       int main() {
+                               Iptr_type lim;
+                               lim = f->lim;
+                               return !!lim;
+                       }
+EOF
+               if $CS a.c $LS >&2
+               then a='/* ' z=' */'
+               fi
+       fi
+       echo "$a#define $i$z"
+done
+
+# *_t
+cat <<'EOF'
+
+/* Comment out the typedefs below if the types are already declared.  */
+/* Fix any uncommented typedefs that are wrong.  */
+EOF
+cat >a.c <<'EOF'
+#include "a.h"
+t x;
+int main() { exitmain(0); }
+EOF
+for t in mode_t pid_t sig_atomic_t size_t ssize_t time_t uid_t
+do
+       : $t
+       case $t in
+       size_t) i=unsigned;;
+       time_t) i=long;;
+       *) i=int;;
+       esac
+       if $CS -Dt=$t a.c $LS >&2
+       then a='/* ' z=' */'
+       else a= z=
+       fi
+       echo "${a}typedef $i $t;$z"
+done
+
+: has_prototypes, has_stdarg, has_varargs, va_start_args
+cat >a.ha <<'EOF'
+#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
+EOF
+cat >a.c <<'EOF'
+#include "a.h"
+#include "a.ha"
+#if has_prototypes
+char *f(char **p, ...)
+#else
+char *f(p, va_alist) char **p; va_dcl
+#endif
+{
+       char *s;
+       va_list v;
+       vararg_start(v,p);
+       s = p[va_arg(v,int)];
+       va_end(v);
+       return s;
+}
+int main P((int, char**));
+int main(argc, argv) int argc; char **argv; {
+       exitmain(f(argv,0) != argv[0]  ||  f(argv,1) != argv[1]);
+}
+EOF
+for has_prototypes in 1 0
+do
+       for has_stdarg in 1 v 0
+       do
+               case $has_stdarg in
+               v) has_varargs=1 has_stdarg=0;;
+               *) has_varargs=0
+               esac
+               case $has_stdarg in
+               0) as='1 2';;
+               1) as='2 1'
+               esac
+               for va_start_args in $as
+               do
+                       $RM || exit
+                       $CL \
+                               -Dhas_prototypes=$has_prototypes \
+                               -Dhas_stdarg=$has_stdarg \
+                               -Dhas_varargs=$has_varargs \
+                               -Dva_start_args=$va_start_args \
+                               a.c $L >&2 && ./a.out && break
+               done && break
+       done && break
+done || {
+       echo >&2 "cannot deduce has_prototypes, has_stdarg, va_start_args"
+       exit 1
+}
+cat - a.ha <<EOF
+
+/* Define the following symbols to be 1 or 0.  */
+#define has_prototypes $has_prototypes /* Do function prototypes work?  */
+#define has_stdarg $has_stdarg /* Does <stdarg.h> work?  */
+#define has_varargs $has_varargs /* Does <varargs.h> work?  */
+#define va_start_args $va_start_args /* How many args does va_start() take?  */
+EOF
+
+: text_equals_binary_stdio, FOPEN_...
+cat >a.c <<'EOF'
+#include "a.h"
+       int
+copyto(filename, mode)
+       char const *filename, *mode;
+{
+       int c;
+       FILE *f, *g;
+       if (!(f = fopen("a.out", "rb")) || !(g = fopen(filename, mode)))
+               return 1;
+       while (c=getc(f), !feof(f))
+               if (ferror(f)  ||  putc(c,g)<0 && ferror(g))
+                       return 1;
+       return fclose(f)!=0 || fclose(g)!=0;
+}
+int main() { exitmain(copyto("a.d", "w+b") || copyto("a.e", "w+")); }
+EOF
+e=1
+$RM a.d a.e || exit
+$CL a.c $L >&2 && ./a.out && cmp a.out a.d && {
+       cmp a.out a.e || e=0
+}
+cat <<EOF
+
+#define text_equals_binary_stdio $e /* 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.  */
+EOF
+
+: bad_fopen_wplus
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(!fopen("a.d",FOPEN_WPLUS)); }
+EOF
+$RM || exit
+if echo nonempty >a.d && $CL a.c $L >&2 && ./a.out && test ! -s a.d
+then b=0
+else b=1
+fi
+echo "#define bad_fopen_wplus $b /* Does fopen(f,FOPEN_WPLUS) fail to truncate f?  */"
+
+: getlogin_is_secure
+echo "#define getlogin_is_secure 0 /* Is getlogin() secure?  Usually it's not.  */"
+
+: has_dirent
+cat >a.c <<'EOF'
+#include "a.h"
+int main() {
+       DIR *d = opendir(".");
+       struct dirent *e;
+       while ((e = readdir(d)))
+               if (strcmp(e->d_name, "a.c") == 0  &&  closedir(d) == 0)
+                       exitmain(0);
+       exitmain(1);
+}
+EOF
+$RM || exit
+if $CL a.c $L >&2 && ./a.out
+then h=1
+else h=0
+fi
+echo "#define has_dirent $h /* Do opendir(), readdir(), closedir() work?  */"
+
+: has_fchmod
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(fchmod(fileno(stdin),0) != 0); }
+EOF
+$RM || exit
+if $CL a.c $L >&2 && ./a.out <a.c && test ! -r a.c
+then h=1
+else h=0
+fi
+echo "#define has_fchmod $h /* Does fchmod() work?  */"
+rm -f a.c || exit
+
+: has_fputs
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(fputs("Hello\"\nworld", stdout) != 12); }
+EOF
+Hello='Hello"
+world'
+$RM || exit
+if $CL a.c $L >&2 && ./a.out >/dev/null && x=`./a.out` && test " $x" = " $Hello"
+then h=1
+else h=0
+fi
+echo "#define has_fputs $h /* Does fputs() work?  */"
+
+: has_ftruncate
+cat >a.c <<'EOF'
+#include "a.h"
+int main(argc, argv) int argc; char **argv; {
+       int f = creat(argv[1], 0);
+       if (f<0 || write(f,"abc",3)!=3 || ftruncate(f,(off_t)0)!=0 || close(f)!=0)
+                       exitmain(1);
+       exitmain(0);
+}
+EOF
+$RM || exit
+if $CL a.c $L >&2
+then
+       h=1
+       # Check out /tmp too; it's buggy on some hosts.
+       for d in . /tmp
+       do
+               if test -d $d
+               then
+                       f=$d/a.d
+                       rm -f $f || exit
+                       ./a.out $f && test ! -s $f && test -f $f  ||  h=0
+                       rm -f $f || exit
+               fi
+       done
+else h=0
+fi
+echo "#define has_ftruncate $h /* Does ftruncate() work?  */"
+
+: has_getuid
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getuid
+       uid_t getuid();
+#endif
+int main() { exitmain(getuid()!=getuid()); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then has_getuid=1
+else has_getuid=0
+fi
+echo "#define has_getuid $has_getuid /* Does getuid() work?  */"
+
+: has_getpwuid
+case $has_getuid in
+0)
+       a='/* ' z='*/ ' h=?;;
+*)
+       a= z=
+       cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(!getpwuid(0)); }
+EOF
+       $RM || exit
+       if ($CL a.c $L && ./a.out) >&2
+       then h=1
+       else h=0
+       fi
+esac
+echo "$a#define has_getpwuid $h $z/* Does getpwuid() work?  */"
+
+: has_link
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(link("a.c","a.d") != 0); }
+EOF
+$RM a.d || exit
+if ($CL a.c $L && ./a.out && cmp a.c a.d) >&2
+then h=1
+else h=0
+fi
+rm -f a.d || exit
+echo "#define has_link $h /* Does link() work?  */"
+
+: has_memcmp
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(memcmp("beautiful","beautiful",10) != 0); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_memcmp $h /* Does memcmp() work?  */"
+
+: has_memcpy
+cat >a.c <<'EOF'
+#include "a.h"
+char a[3];
+int main() {
+       memcpy(a,"xy",3);
+       exitmain(strcmp(a,"xy")!=0);
+}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_memcpy $h /* Does memcpy() work?  */"
+
+: has_memmove
+cat >a.c <<'EOF'
+#include "a.h"
+char a[4];
+int main() {
+       strcpy(a, "xy");
+       memmove(a+1, a, 3);
+       exitmain(strcmp(a,"xxy")!=0);
+}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_memmove $h /* Does memmove() work?  */"
+
+: has_mmap, has_madvise
+cat >a.c <<'EOF'
+#define CHAR1 '#' /* the first character in this file */
+#include "a.h"
+#ifndef mmap
+       caddr_t mmap();
+#endif
+caddr_t a;
+struct stat b;
+#ifndef MADVISE_OK
+#      define MADVISE_OK (madvise(a,b.st_size,MADV_SEQUENTIAL)==0 && madvise(a,b.st_size,MADV_NORMAL)==0)
+#endif
+int main()
+{
+       if (fstat(fileno(stdin), &b) != 0)
+               exitmain(1);
+       a = mmap(
+               (caddr_t)0, b.st_size, PROT_READ, MAP_SHARED,
+               fileno(stdin), (off_t)0
+       );
+       exitmain(
+               a == (caddr_t)-1  ||
+               !MADVISE_OK ||
+               *a != CHAR1  ||
+               munmap(a, b.st_size)  !=  0
+       );
+}
+EOF
+a=0 has_mmap=0
+$RM || exit
+if ($CL -DMADVISE_OK=1 a.c $L && ./a.out <a.c) >&2
+then
+       has_mmap=1
+       $RM || exit
+       ($CL a.c $L && ./a.out <a.c) >&2 && a=1
+fi
+echo "#define has_madvise $a /* Does madvise() work?  */"
+echo "#define has_mmap $has_mmap /* Does mmap() work on regular files?  */"
+
+: has_rename, bad_a_rename, bad_b_rename
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(rename("a.a","a.b") != 0); }
+EOF
+echo a >a.a && $RM a.b || exit
+if ($CL a.c $L && ./a.out && test -f a.b) >&2
+then
+       h=1
+       rm -f a.a a.b &&
+       echo a >a.a && chmod -w a.a || exit
+       if ./a.out && test ! -f a.a && test -f a.b
+       then a=0
+       else a=1
+       fi
+       rm -f a.a a.b &&
+       echo a >a.a && echo b >a.b && chmod -w a.b || exit
+       if ./a.out && test ! -f a.a && test -f a.b
+       then b=0
+       else b=1
+       fi
+       rm -f a.a a.b || exit
+else h=0 a=0 b=0
+fi
+echo "#define has_rename $h /* Does rename() work?  */"
+echo "#define bad_a_rename $a /* Does rename(A,B) fail if A is unwritable?  */"
+echo "#define bad_b_rename $b /* Does rename(A,B) fail if B is unwritable?  */"
+
+: void, VOID
+cat >a.c <<'EOF'
+#include "a.h"
+void f() {}
+int main() {f(); exitmain(0);}
+EOF
+if $CS a.c $LS >&2
+then
+       v='(void) '
+else
+       v=
+       echo 'typedef int void;'
+fi
+echo "#define VOID $v/* 'VOID e;' discards the value of an expression 'e'.  */"
+
+: has_seteuid
+case $has_getuid in
+0)
+       a='/* ' z='*/ ' has_seteuid=?;;
+*)
+       a= z=
+       cat >a.c <<'EOF'
+#include "a.h"
+#ifndef geteuid
+       uid_t geteuid();
+#endif
+int main() {
+/* Guess, don't test.  Ugh.  Testing would require running conf.sh setuid.  */
+/* seteuid() isn't standardized yet, so the guess below may well be wrong.  */
+#if !_POSIX_VERSION || _POSIX_VERSION<=199009L&&!defined(sgi)&&!defined(__sgi__)&&!defined(sun)&&!defined(__sun__)
+       exitmain(1);
+#else
+       exitmain(seteuid(geteuid()) != 0);
+#endif
+}
+EOF
+       $RM || exit
+       if ($CL a.c $L && ./a.out) >&2
+       then has_seteuid=1
+       else has_seteuid=0
+       fi
+esac
+echo "$a#define has_seteuid $has_seteuid $z/* Does seteuid() work?  See README.  */"
+
+: has_setuid
+h=$has_seteuid
+case $h in
+0)
+       cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getuid
+       uid_t getuid();
+#endif
+int main() { exitmain(setuid(getuid()) != 0); }
+EOF
+       $RM || exit
+       ($CL a.c $L && ./a.out) >&2 && h=1
+esac
+echo "$a#define has_setuid $h $z/* Does setuid() exist?  */"
+
+: has_signal, signal_args, signal_type, sig_zaps_handler
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getpid
+       pid_t getpid();
+#endif
+#if !defined(signal) && declare_signal
+       signal_type (*signal P((int,signal_type(*)signal_args)))signal_args;
+#endif
+signal_type nothing(i) int i; {}
+int main(argc, argv) int argc; char **argv;
+{
+       signal(SIGINT, nothing);
+       while (--argc)
+               kill(getpid(), SIGINT);
+       exitmain(0);
+}
+EOF
+for declare_signal in 1 0
+do
+       for signal_type in void int
+       do
+               for signal_args in 'P((int))' '()'
+               do
+                       $RM || exit
+                       ($CL \
+                               -Ddeclare_signal=$declare_signal \
+                               -Dsignal_args="$signal_args" \
+                               -Dsignal_type=$signal_type \
+                                       a.c $L && ./a.out 1) >&2 && break
+               done && break
+       done && break
+done || {
+       echo >&2 "cannot deduce signal_args, signal_type"
+       exit 1
+}
+if ./a.out 1 2 >&2
+then z=0
+else z=1
+fi
+cat <<EOF
+#define has_signal $has_signal /* Does signal() work?  */
+#define signal_args $signal_args /* arguments of signal handlers */
+#define signal_type $signal_type /* type returned by signal handlers */
+#define sig_zaps_handler $z /* Must a signal handler reinvoke signal()?  */
+EOF
+
+: has_sigaction
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getpid
+       pid_t getpid();
+#endif
+static sig_atomic_t volatile gotsig;
+static void getsig(i) int i; { gotsig = 1; }
+int main(argc, argv) int argc; char **argv;
+{
+       struct sigaction s;
+       sigset_t t;
+       if (sigemptyset(&t) != 0  ||  sigaddset(&t, SIGINT) != 0)
+               exitmain(1);
+       if (sigaction(SIGINT, (struct sigaction const*)0, &s) != 0)
+               exitmain(1);
+       s.sa_handler = getsig;
+       s.sa_mask = t;
+       if (sigaction(SIGINT, &s, (struct sigaction*)0) != 0)
+               exitmain(1);
+       kill(getpid(), SIGINT);
+       exitmain(gotsig != 1);
+}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then has_sigaction=1
+else has_sigaction=0
+fi
+echo "#define has_sigaction $has_sigaction /* Does struct sigaction work?  */"
+
+: has_sigblock, sigmask
+a='/* ' z='*/ '
+b='/* ' y='*/ '
+case $has_sigaction in
+1)
+       h=? n=?;;
+*)
+       a= z=
+       cat >a.c <<'EOF'
+#include "a.h"
+#include <signal.h>
+#if define_sigmask
+#      define sigmask(s) (1 << ((s)-1))
+#endif
+int main()
+{
+       sigblock(sigmask(SIGHUP));
+       exitmain(kill(getpid(), SIGHUP) != 0);
+}
+EOF
+       if $RM || exit; ($CL a.c $L && ./a.out) >&2
+       then h=1
+       elif $RM || exit; ($CL -Ddefine_sigmask=1 a.c $L && ./a.out) >&2
+       then h=1 b= y=
+       else h=0
+       fi
+esac
+echo "$a#define has_sigblock $h $z/* Does sigblock() work?  */"
+echo "$b#define sigmask(s) (1 << ((s)-1)) $y/* Yield mask for signal number.  */"
+
+: has_sys_siglist
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef sys_siglist
+       extern char const *sys_siglist[];
+#endif
+int main() { exitmain(!sys_siglist[1][0]); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_sys_siglist $h /* Does sys_siglist[] work?  */"
+
+: fread_type, Fread, Fwrite
+cat >a.c <<'EOF'
+#define CHAR1 '#' /* the first character in this file */
+#include "a.h"
+#if !defined(fread) && declare_fread
+       fread_type fread P((void*,freadarg_type,freadarg_type,FILE*));
+#endif
+int main()
+{
+       char b;
+       exitmain(!(
+               fread(&b, (freadarg_type)1, (freadarg_type)1, stdin) == 1  &&
+               b==CHAR1
+       ));
+}
+EOF
+for declare_fread in 1 0
+do
+       for fread_type in ssize_t size_t int unsigned
+       do
+               for freadarg_type in size_t ssize_t unsigned int
+               do
+                       $RM || exit
+                       (
+                               $CL \
+                                       -Ddeclare_fread=$declare_fread \
+                                       -Dfreadarg_type=$freadarg_type \
+                                       -Dfread_type=$fread_type \
+                                       a.c $L &&
+                               ./a.out <a.c
+                       ) >&2 && break
+               done && break
+       done && break
+done || {
+       echo >&2 "cannot deduce fread types"
+       exit 1
+}
+cat <<EOF
+typedef $fread_type fread_type; /* type returned by fread() and fwrite() */
+typedef $freadarg_type freadarg_type; /* type of their size arguments */
+EOF
+
+: malloc_type
+cat >a.c <<'EOF'
+#include "a.h"
+typedef void *malloc_type;
+#ifndef malloc
+       malloc_type malloc();
+#endif
+int main() { exitmain(!malloc(1)); }
+EOF
+if $CS a.c $LS >&2
+then t=void
+else t=char
+fi
+echo "typedef $t *malloc_type; /* type returned by malloc() */"
+
+: has_getcwd
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getcwd
+       char *getcwd();
+#endif
+char buf[10000];
+int main() { exitmain(!getcwd(buf,10000)); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then has_getcwd=1
+else has_getcwd=0
+fi
+echo "#define has_getcwd $has_getcwd /* Does getcwd() work?  */"
+
+: has_getwd
+case $has_getcwd in
+1)
+       a='/* ' z='*/ ' h=?;;
+*)
+       a= z=
+       cat >a.c <<'EOF'
+#include "a.h"
+#include <sys/param.h>
+#ifndef getwd
+       char *getwd();
+#endif
+char buf[MAXPATHLEN];
+int main() { exitmain(!getwd(buf)); }
+EOF
+       $RM || exit
+       if ($CL a.c $L && ./a.out) >&2
+       then h=1
+       else h=0
+       fi
+esac
+echo "$a#define has_getwd $h $z/* Does getwd() work?  */"
+
+: has_mktemp
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef mktemp
+       char *mktemp();
+#endif
+int main()
+{
+       char b[9];
+       strcpy(b, "a.XXXXXX");
+       exitmain(!mktemp(b));
+}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_mktemp $h /* Does mktemp() work?  */"
+
+: has_NFS
+echo "#define has_NFS 1 /* Might NFS be used?  */"
+
+: strchr
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef strchr
+       char *strchr();
+#endif
+int main() {exitmain(!strchr("abc", 'c'));}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then a='/* ' z='*/ '
+else a= z=
+fi
+echo "$a#define strchr index $z/* Use old-fashioned name for strchr()?  */"
+
+: strrchr
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef strrchr
+       char *strrchr();
+#endif
+int main() {exitmain(!strrchr("abc", 'c'));}
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then a='/* ' z='*/ '
+else a= z=
+fi
+echo "$a#define strrchr rindex $z/* Use old-fashioned name for strrchr()?  */"
+
+: bad_unlink
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(unlink("a.c") != 0); }
+EOF
+$RM && chmod -w a.c || exit
+if $CL a.c $L >&2 && ./a.out >/dev/null && test ! -f a.c
+then b=0
+else b=1
+fi
+rm -f a.c || exit
+echo "#define bad_unlink $b /* Does unlink() fail on unwritable files?  */"
+
+: has_vfork, has_fork, has_spawn, has_wait, has_waitpid, RCS_SHELL
+cat >a.c <<'EOF'
+#include "a.h"
+#ifndef getpid
+       pid_t getpid();
+#endif
+#if TRY_VFORK
+#      ifndef vfork
+               pid_t vfork();
+#      endif
+#else
+#      ifndef fork
+               pid_t fork();
+#      endif
+#      undef vfork
+#      define vfork fork
+#endif
+#if TRY_WAITPID
+#      ifndef waitpid
+               pid_t waitpid();
+#      endif
+#else
+#      ifndef wait
+               pid_t wait();
+#      endif
+#endif
+pid_t child;
+int status;
+struct stat st;
+int main()
+{
+       pid_t parent = getpid();
+       if (!(child = vfork())) {
+               /* Tickle vfork/compiler bug (e.g. sparc gcc -O (1.37.1).  */
+               pid_t i = getpid(), j = getpid();
+               if (i!=getpid() || j!=getpid())
+                       _exit(!i);
+               /* Tickle file descriptor bug (e.g. IRIX 3.3).  */
+               _exit(close(1) != 0);
+       } else {
+#              if TRY_WAITPID
+                       if (waitpid(child, &status, 0) != child)
+                               exitmain(1);
+#              else
+                       while (wait(&status) != child)
+                               ;
+#              endif
+               /* Test for presence of bugs.  */
+               exitmain(status  ||  parent != getpid()  ||  fstat(1,&st) != 0);
+       }
+}
+EOF
+$RM || exit
+if ($CL -DTRY_VFORK=1 a.c $L && ./a.out) >&2
+then has_vfork=1
+else has_vfork=0
+fi
+echo "#define has_vfork $has_vfork /* Does vfork() work?  */"
+h=$has_vfork
+case $h in
+0)
+       $RM || exit
+       ($CL a.c $L && ./a.out) >&2 && h=1
+esac
+echo "#define has_fork $h /* Does fork() work?  */"
+$RM || exit
+if ($CL -DTRY_VFORK=$has_vfork -DTRY_WAITPID=1 a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_spawn 0 /* Does spawn*() work?  */"
+echo "#define has_wait 1 /* Does wait() work?  */"
+echo "#define has_waitpid $h /* Does waitpid() work?  */"
+echo '#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */'
+
+: has_vfprintf
+cat >a.c <<'EOF'
+#include "a.h"
+#if has_prototypes
+int p(char const*format,...)
+#else
+/*VARARGS1*/ int p(format, va_alist) char *format; va_dcl
+#endif
+{
+       int r;
+       va_list args;
+       vararg_start(args, format);
+       r = vfprintf(stderr, format, args);
+       va_end(args);
+       return r;
+}
+int main() { exitmain(p("") != 0); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then h=1
+else h=0
+fi
+echo "#define has_vfprintf $h /* Does vfprintf() work?  */"
+
+: has__doprintf, has__doprnt
+case $h in
+1)
+       h=? a='/* ' z='*/ ';;
+*)
+       a= z=
+       cat >a.c <<'EOF'
+#include "a.h"
+#if has_prototypes
+int p(char const*format,...)
+#else
+/*VARARGS1*/ int p(format, va_alist) char *format; va_dcl
+#endif
+{
+       va_list args;
+       vararg_start(args, format);
+#      if TRY__DOPRINTF
+               _doprintf(stderr, format, args);
+#      else
+               _doprnt(format, args, stderr);
+#      endif
+       va_end(args);
+}
+int main() { p(""); exitmain(ferror(stderr) != 0); }
+EOF
+       $RM || exit
+       if ($CL -DTRY__DOPRINTF=1 a.c $L && ./a.out) >&2
+       then h=1
+       else h=0
+       fi
+esac
+echo "$a#define has__doprintf $h $z/* Does _doprintf() work?  */"
+case $h in
+0)
+       $RM || exit
+       if ($CL a.c $L && ./a.out) >&2
+       then h=1
+       else h=0
+       fi
+       a= z=;;
+*)
+       h=? a='/* ' z='*/ '
+esac
+echo "$a#define has__doprnt $h $z/* Does _doprnt() work?  */"
+
+: EXIT_FAILURE
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { exitmain(EXIT_FAILURE); }
+EOF
+$RM || exit
+if $CL a.c $L >&2 && ./a.out
+then a= z=
+else a='/* ' z='*/ '
+fi
+echo "$a#undef EXIT_FAILURE $z/* Uncomment this if EXIT_FAILURE is broken.  */"
+
+: large_memory
+echo "#define large_memory $has_mmap /* Can main memory hold entire RCS files?  */"
+
+: ULONG_MAX
+cat >a.c <<'EOF'
+#include "a.h"
+#ifdef ULONG_MAX
+       /*
+        * "#if ULONG_MAX/10 <= 0" does not always work,
+        * because some buggy implementations put casts in ULONG_MAX.
+        */
+       int main() { exitmain(ULONG_MAX/10 <= 0); }
+#else
+       int main() { exitmain(1); }
+#endif
+EOF
+$RM || exit
+if $CL a.c $L >&2 && ./a.out
+then a='/* ' z='*/ '
+else a= z=
+fi
+echo "$a#undef ULONG_MAX $z/* Uncomment this if ULONG_MAX is broken (e.g. < 0).  */"
+
+: struct utimbuf
+cat >a.c <<'EOF'
+#include "a.h"
+struct utimbuf s;
+int main() { s.actime = s.modtime = 1; exitmain(utime("a.c", &s) != 0); }
+EOF
+$RM || exit
+if ($CL a.c $L && ./a.out) >&2
+then a='/* ' z=' */'
+else a= z=
+fi
+echo "${a}struct utimbuf { time_t actime, modtime; };$z /* Uncomment this if needed.  */"
+
+: CO
+echo "#define CO \"${RCSPREFIX}co\" /* name of 'co' program */"
+
+: COMPAT2
+echo "#define COMPAT2 $COMPAT2 /* Are version 2 files supported?  */"
+
+: DATEFORM
+cat >a.c <<'EOF'
+#include "a.h"
+int main() { printf("%.2d", 1); exitmain(0); }
+EOF
+$RM && $CL a.c $L >&2 && r=`./a.out` || exit
+case $r in
+01)    f=%.2d;;
+*)     f=%02d
+esac
+echo "#define DATEFORM \"$f.$f.$f.$f.$f.${f}\" /* e.g. 01.01.01.01.01.01 */"
+
+: DIFF
+echo "#define DIFF \"${DIFF}\" /* name of 'diff' program */"
+
+: DIFF3
+echo "#define DIFF3 \"${DIFF3}\" /* name of 'diff3' program */"
+
+echo "#define DIFF3_BIN $DIFF3_BIN /* Is diff3 user-visible (not the /usr/lib auxiliary)?  */"
+
+: DIFF_FLAGS
+dfs=
+for df in $DIFF_FLAGS
+do dfs="$dfs, \"${df}\""
+done
+echo "#define DIFF_FLAGS $dfs /* Make diff output suitable for RCS.  */"
+
+: DIFF_L
+echo "#define DIFF_L $DIFF_L /* Does diff -L work? */"
+
+: DIFF_SUCCESS, DIFF_FAILURE, DIFF_TROUBLE
+cat <<EOF
+#define DIFF_SUCCESS $DIFF_SUCCESS /* DIFF status if no differences are found */
+#define DIFF_FAILURE $DIFF_FAILURE /* DIFF status if differences are found */
+#define DIFF_TROUBLE $DIFF_TROUBLE /* DIFF status if trouble */
+EOF
+
+: ED
+echo "#define ED \"${ED}\" /* name of 'ed' program (used only if !DIFF3_BIN) */"
+
+: MERGE
+echo "#define MERGE \"${RCSPREFIX}merge\" /* name of 'merge' program */"
+
+: '*SLASH*', ROOTPATH, TMPDIR, X_DEFAULT
+case ${PWD-`pwd`} in
+/*) # Posix
+       SLASH=/
+       qSLASH="'/'"
+       SLASHes=$qSLASH
+       isSLASH='#define isSLASH(c) ((c) == SLASH)'
+       ROOTPATH='isSLASH((p)[0])'
+       X_DEFAULT=",v$SLASH";;
+?:[/\\]*) # MS-DOS
+       SLASH='\'
+       qSLASH="'\\\\'"
+       SLASHes="$qSLASH: case '/': case ':'"
+       isSLASH='int isSLASH P((int));'
+       ROOTPATH='((p)[0] && (p)[1]==':' && isSLASH((p)[2]))'
+       X_DEFAULT="$SLASH,v";;
+*)
+       echo >&2 "cannot deduce SLASH"; exit 1
+esac
+cat <<EOF
+#define TMPDIR "${SLASH}tmp" /* default directory for temporary files */
+#define SLASH $qSLASH /* principal pathname separator */
+#define SLASHes $SLASHes /* \`case SLASHes:' labels all pathname separators */
+$isSLASH /* Is arg a pathname separator?  */
+#define ROOTPATH(p) $ROOTPATH /* Is p an absolute pathname?  */
+#define X_DEFAULT "$X_DEFAULT" /* default value for -x option */
+EOF
+
+: DIFF_ABSOLUTE
+case $DIFF in
+"$SLASH"*) a=1;;
+*) a=0
+esac
+echo "#define DIFF_ABSOLUTE $a /* Is ROOTPATH(DIFF) true?  */"
+
+: ALL_ABSOLUTE
+a=1
+for i in "$DIFF" "$DIFF3" "$ED" "$RCSPREFIX" "$SENDMAIL"/
+do
+       case $i in
+       "$SLASH"* | "\"$SLASH"*) ;;
+       *) a=0 break
+       esac
+done
+echo "#define ALL_ABSOLUTE $a /* Are all subprograms absolute pathnames?  */"
+
+: SENDMAIL
+case $SENDMAIL in
+'') a='/* ' z='*/ ';;
+*) a= z=
+esac
+echo "$a#define SENDMAIL $SENDMAIL $z/* how to send mail */"
+
+: TZ_must_be_set
+echo "#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work?  */"
+
+
+: standard function declarations
+
+cat <<'EOF'
+
+
+
+/* Adjust the following declarations as needed.  */
+
+
+#if __GNUC__ && !__STRICT_ANSI__
+#      define exiting volatile /* GCC extension: function cannot return */
+#else
+#      define exiting
+#endif
+EOF
+
+cat >a.ha <<EOF
+
+#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*));
+EOF
+
+cat >a.c <<'EOF'
+#include "a.h"
+#define a 0
+#define b 1
+#if h==a
+#      include "a.ha"
+#else
+#      include "a.hb"
+#endif
+int main() { exitmain(0); }
+EOF
+
+# Comment out lines in a.ha that the compiler rejects.
+# a.ha may not contain comments that cross line boundaries.
+# Leave the result in a.h$h.
+h=a l=1
+U=`wc -l <a.ha | sed 's| ||g'`
+commentOut='s|^[^#/][^/]*|/* & */|'
+
+until  test $U -lt $l  ||  $CS -Dh=$h a.c $LS >&2
+do
+       case $h in
+       a) i=b;;
+       *) i=a
+       esac
+
+       # The compiler rejects some line in l..U.
+       # Use binary search to set l to be the index of the first bad line in l..U.
+       u=$U
+       while test $l -lt $u
+       do
+               M=`expr '(' $l + $u ')' / 2`
+               M1=`expr $M + 1`
+               sed "$M1,\$$commentOut" a.h$h >a.h$i || exit
+               if $CS -Dh=$i a.c $LS >&2
+               then l=$M1
+               else u=$M
+               fi
+       done
+
+       # Comment out the bad line.
+       sed "$l$commentOut" a.h$h >a.h$i || exit
+
+       h=$i
+       l=`expr $l + 1`
+done
+
+cat a.h$h
diff --git a/usr/src/contrib/rcs-V5.6/src/ident.c b/usr/src/contrib/rcs-V5.6/src/ident.c
new file mode 100644 (file)
index 0000000..a2cc018
--- /dev/null
@@ -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;
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/maketime.c b/usr/src/contrib/rcs-V5.6/src/maketime.c
new file mode 100644 (file)
index 0000000..c95c9f0
--- /dev/null
@@ -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);
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/merge.c b/usr/src/contrib/rcs-V5.6/src/merge.c
new file mode 100644 (file)
index 0000000..4067c18
--- /dev/null
@@ -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);
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/merger.c b/usr/src/contrib/rcs-V5.6/src/merger.c
new file mode 100644 (file)
index 0000000..7162ffa
--- /dev/null
@@ -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;
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/partime.c b/usr/src/contrib/rcs-V5.6/src/partime.c
new file mode 100644 (file)
index 0000000..4751fc5
--- /dev/null
@@ -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;
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/rcs.c b/usr/src/contrib/rcs-V5.6/src/rcs.c
new file mode 100644 (file)
index 0000000..70e7ffc
--- /dev/null
@@ -0,0 +1,1554 @@
+/*
+ *                      RCS create/change 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: rcs.c,v $
+ * Revision 5.12  1991/11/20  17:58:08  eggert
+ * Don't read the delta tree from a nonexistent RCS file.
+ *
+ * Revision 5.11  1991/10/07  17:32:46  eggert
+ * Remove lint.
+ *
+ * Revision 5.10  1991/08/19  23:17:54  eggert
+ * Add -m, -r$, piece tables.  Revision separator is `:', not `-'.  Tune.
+ *
+ * Revision 5.9  1991/04/21  11:58:18  eggert
+ * Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.8  1991/02/25  07:12:38  eggert
+ * strsave -> str_save (DG/UX name clash)
+ * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability
+ *
+ * Revision 5.7  1990/12/18  17:19:21  eggert
+ * Fix bug with multiple -n and -N options.
+ *
+ * Revision 5.6  1990/12/04  05:18:40  eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.5  1990/11/11  00:06:35  eggert
+ * Fix `rcs -e' core dump.
+ *
+ * Revision 5.4  1990/11/01  05:03:33  eggert
+ * Add -I and new -t behavior.  Permit arbitrary data in logs.
+ *
+ * Revision 5.3  1990/10/04  06:30:16  eggert
+ * Accumulate exit status across files.
+ *
+ * Revision 5.2  1990/09/04  08:02:17  eggert
+ * Standardize yes-or-no procedure.
+ *
+ * Revision 5.1  1990/08/29  07:13:51  eggert
+ * Remove unused setuid support.  Clean old log messages too.
+ *
+ * Revision 5.0  1990/08/22  08:12:42  eggert
+ * Don't lose names when applying -a option to multiple files.
+ * Remove compile-time limits; use malloc instead.  Add setuid support.
+ * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
+ * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
+ * Yield proper exit status.  Check diff's output.
+ *
+ * Revision 4.11  89/05/01  15:12:06  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.10  88/11/08  16:01:54  narten
+ * didn't install previous patch correctly
+ * 
+ * Revision 4.9  88/11/08  13:56:01  narten
+ * removed include <sysexits.h> (not needed)
+ * minor fix for -A option
+ * 
+ * Revision 4.8  88/08/09  19:12:27  eggert
+ * Don't access freed storage.
+ * Use execv(), not system(); yield proper exit status; remove lint.
+ * 
+ * Revision 4.7  87/12/18  11:37:17  narten
+ * lint cleanups (Guy Harris)
+ * 
+ * Revision 4.6  87/10/18  10:28:48  narten
+ * Updating verison numbers. Changes relative to 1.1 are actually 
+ * relative to 4.3
+ * 
+ * Revision 1.4  87/09/24  13:58:52  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.3  87/03/27  14:21:55  jenkins
+ * Port to suns
+ * 
+ * Revision 1.2  85/12/17  13:59:09  albitz
+ * Changed setstate to rcs_setstate because of conflict with random.o.
+ * 
+ * Revision 4.3  83/12/15  12:27:33  wft
+ * rcs -u now breaks most recent lock if it can't find a lock by the caller.
+ * 
+ * Revision 4.2  83/12/05  10:18:20  wft
+ * Added conditional compilation for sending mail.
+ * Alternatives: V4_2BSD, V6, USG, and other.
+ * 
+ * Revision 4.1  83/05/10  16:43:02  wft
+ * Simplified breaklock(); added calls to findlock() and getcaller().
+ * Added option -b (default branch). Updated -s and -w for -b.
+ * Removed calls to stat(); now done by pairfilenames().
+ * Replaced most catchints() calls with restoreints().
+ * Removed check for exit status of delivermail().
+ * Directed all interactive output to stderr.
+ * 
+ * Revision 3.9.1.1  83/12/02  22:08:51  wft
+ * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
+ * 
+ * Revision 3.9  83/02/15  15:38:39  wft
+ * Added call to fastcopy() to copy remainder of RCS file.
+ *
+ * Revision 3.8  83/01/18  17:37:51  wft
+ * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
+ *
+ * Revision 3.7  83/01/15  18:04:25  wft
+ * Removed putree(); replaced with puttree() in rcssyn.c.
+ * Combined putdellog() and scanlogtext(); deleted putdellog().
+ * Cleaned up diagnostics and error messages. Fixed problem with
+ * mutilated files in case of deletions in 2 files in a single command.
+ * Changed marking of selector from 'D' to DELETE.
+ *
+ * Revision 3.6  83/01/14  15:37:31  wft
+ * Added ignoring of interrupts while new RCS file is renamed;
+ * Avoids deletion of RCS files by interrupts.
+ *
+ * Revision 3.5  82/12/10  21:11:39  wft
+ * Removed unused variables, fixed checking of return code from diff,
+ * introduced variant COMPAT2 for skipping Suffix on -A files.
+ *
+ * Revision 3.4  82/12/04  13:18:20  wft
+ * Replaced getdelta() with gettree(), changed breaklock to update
+ * field lockedby, added some diagnostics.
+ *
+ * Revision 3.3  82/12/03  17:08:04  wft
+ * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
+ * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
+ * fixed -u for missing revno. Disambiguated structure members.
+ *
+ * Revision 3.2  82/10/18  21:05:07  wft
+ * rcs -i now generates a file mode given by the umask minus write permission;
+ * otherwise, rcs keeps the mode, but removes write permission.
+ * I added a check for write error, fixed call to getlogin(), replaced
+ * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
+ * conflicting, long identifiers.
+ *
+ * Revision 3.1  82/10/13  16:11:07  wft
+ * fixed type of variables receiving from getc() (char -> int).
+ */
+
+
+#include "rcsbase.h"
+
+struct  Lockrev {
+       char const *revno;
+        struct  Lockrev   * nextrev;
+};
+
+struct  Symrev {
+       char const *revno;
+       char const *ssymbol;
+        int     override;
+        struct  Symrev  * nextsym;
+};
+
+struct Message {
+       char const *revno;
+       struct cbuf message;
+       struct Message *nextmessage;
+};
+
+struct  Status {
+       char const *revno;
+       char const *status;
+        struct  Status  * nextstatus;
+};
+
+enum changeaccess {append, erase};
+struct chaccess {
+       char const *login;
+       enum changeaccess command;
+       struct chaccess *nextchaccess;
+};
+
+struct delrevpair {
+       char const *strt;
+       char const *end;
+        int     code;
+};
+
+static int buildeltatext P((struct hshentries const*));
+static int removerevs P((void));
+static int sendmail P((char const*,char const*));
+static struct Lockrev *rmnewlocklst P((struct Lockrev const*));
+static void breaklock P((struct hshentry const*));
+static void buildtree P((void));
+static void cleanup P((void));
+static void doaccess P((void));
+static void doassoc P((void));
+static void dolocks P((void));
+static void domessages P((void));
+static void getaccessor P((char*,enum changeaccess));
+static void getassoclst P((int,char*));
+static void getchaccess P((char const*,enum changeaccess));
+static void getdelrev P((char*));
+static void getmessage P((char*));
+static void getstates P((char*));
+static void rcs_setstate P((char const*,char const*));
+static void scanlogtext P((struct hshentry*,int));
+static void setlock P((char const*));
+
+static struct buf numrev;
+static char const *headstate;
+static int chgheadstate, exitstatus, lockhead, unlockcaller;
+static struct Lockrev *newlocklst, *rmvlocklst;
+static struct Message *messagelst, *lastmessage;
+static struct Status *statelst, *laststate;
+static struct Symrev *assoclst, *lastassoc;
+static struct chaccess *chaccess, **nextchaccess;
+static struct delrevpair delrev;
+static struct hshentry *cuthead, *cuttail, *delstrt;
+static struct hshentries *gendeltas;
+
+mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $")
+{
+       static char const cmdusage[] =
+               "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iLU} -{nNs}name[:rev] -orange -t[file] -Vn file ...";
+
+       char *a, **newargv, *textfile;
+       char const *branchsym, *commsyml;
+       int branchflag, expmode, initflag;
+       int e, r, strictlock, strict_selected, textflag;
+       mode_t defaultRCSmode;  /* default mode for new RCS files */
+       mode_t RCSmode;
+       struct buf branchnum;
+       struct stat workstat;
+        struct  Lockrev *curlock,  * rmvlock, *lockpt;
+        struct  Status  * curstate;
+
+       nosetid();
+
+       nextchaccess = &chaccess;
+       branchsym = commsyml = textfile = nil;
+       branchflag = strictlock = false;
+       bufautobegin(&branchnum);
+       curlock = rmvlock = nil;
+       defaultRCSmode = 0;
+       expmode = -1;
+       suffixes = X_DEFAULT;
+        initflag= textflag = false;
+        strict_selected = 0;
+
+        /*  preprocessing command options    */
+       argc = getRCSINIT(argc, argv, &newargv);
+       argv = newargv;
+       while (a = *++argv,  0<--argc && *a++=='-') {
+               switch (*a++) {
+
+               case 'i':   /*  initial version  */
+                        initflag = true;
+                        break;
+
+                case 'b':  /* change default branch */
+                       if (branchflag) redefined('b');
+                        branchflag= true;
+                       branchsym = a;
+                        break;
+
+                case 'c':   /*  change comment symbol   */
+                       if (commsyml) redefined('c');
+                       commsyml = a;
+                        break;
+
+                case 'a':  /*  add new accessor   */
+                       getaccessor(*argv+1, append);
+                        break;
+
+                case 'A':  /*  append access list according to accessfile  */
+                       if (!*a) {
+                           error("missing file name after -A");
+                            break;
+                        }
+                       *argv = a;
+                       if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) {
+                           while (AccessList) {
+                               getchaccess(str_save(AccessList->login),append);
+                               AccessList = AccessList->nextaccess;
+                           }
+                           Izclose(&finptr);
+                        }
+                        break;
+
+                case 'e':    /*  remove accessors   */
+                       getaccessor(*argv+1, erase);
+                        break;
+
+                case 'l':    /*   lock a revision if it is unlocked   */
+                       if (!*a) {
+                           /* Lock head or default branch.  */
+                            lockhead = true;
+                            break;
+                        }
+                       lockpt = talloc(struct Lockrev);
+                       lockpt->revno = a;
+                        lockpt->nextrev = nil;
+                        if ( curlock )
+                            curlock->nextrev = lockpt;
+                        else
+                            newlocklst = lockpt;
+                        curlock = lockpt;
+                        break;
+
+                case 'u':   /*  release lock of a locked revision   */
+                       if (!*a) {
+                            unlockcaller=true;
+                            break;
+                        }
+                       lockpt = talloc(struct Lockrev);
+                       lockpt->revno = a;
+                        lockpt->nextrev = nil;
+                        if (rmvlock)
+                            rmvlock->nextrev = lockpt;
+                        else
+                            rmvlocklst = lockpt;
+                        rmvlock = lockpt;
+
+                        curlock = rmnewlocklst(lockpt);
+                        break;
+
+                case 'L':   /*  set strict locking */
+                        if (strict_selected++) {  /* Already selected L or U? */
+                          if (!strictlock)       /* Already selected -U? */
+                              warn("-L overrides -U.");
+                        }
+                        strictlock = true;
+                        break;
+
+                case 'U':   /*  release strict locking */
+                        if (strict_selected++) {  /* Already selected L or U? */
+                          if (strictlock)        /* Already selected -L? */
+                              warn("-L overrides -U.");
+                        }
+                       else
+                           strictlock = false;
+                        break;
+
+                case 'n':    /*  add new association: error, if name exists */
+                       if (!*a) {
+                           error("missing symbolic name after -n");
+                            break;
+                        }
+                        getassoclst(false, (*argv)+1);
+                        break;
+
+                case 'N':   /*  add or change association   */
+                       if (!*a) {
+                           error("missing symbolic name after -N");
+                            break;
+                        }
+                        getassoclst(true, (*argv)+1);
+                        break;
+
+               case 'm':   /*  change log message  */
+                       getmessage(a);
+                       break;
+
+               case 'o':   /*  delete revisions  */
+                       if (delrev.strt) redefined('o');
+                       if (!*a) {
+                           error("missing revision range after -o");
+                            break;
+                        }
+                        getdelrev( (*argv)+1 );
+                        break;
+
+                case 's':   /*  change state attribute of a revision  */
+                       if (!*a) {
+                           error("state missing after -s");
+                            break;
+                        }
+                        getstates( (*argv)+1);
+                        break;
+
+                case 't':   /*  change descriptive text   */
+                        textflag=true;
+                       if (*a) {
+                               if (textfile) redefined('t');
+                               textfile = a;
+                        }
+                        break;
+
+               case 'I':
+                       interactiveflag = true;
+                       break;
+
+                case 'q':
+                        quietflag = true;
+                        break;
+
+               case 'x':
+                       suffixes = a;
+                       break;
+
+               case 'V':
+                       setRCSversion(*argv);
+                       break;
+
+               case 'k':    /*  set keyword expand mode  */
+                       if (0 <= expmode) redefined('k');
+                       if (0 <= (expmode = str2expmode(a)))
+                           break;
+                       /* fall into */
+                default:
+                       faterror("unknown option: %s%s", *argv, cmdusage);
+                };
+        }  /* end processing of options */
+
+       if (argc<1) faterror("no input file%s", cmdusage);
+        if (nerror) {
+           diagnose("%s aborted\n",cmdid);
+           exitmain(EXIT_FAILURE);
+        }
+       if (initflag) {
+           defaultRCSmode = umask((mode_t)0);
+           VOID umask(defaultRCSmode);
+           defaultRCSmode = (S_IRUSR|S_IRGRP|S_IROTH) & ~defaultRCSmode;
+       }
+
+        /* now handle all filenames */
+        do {
+       ffree();
+
+        if ( initflag ) {
+           switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
+                case -1: break;        /*  not exist; ok */
+                case  0: continue;     /*  error         */
+                case  1: error("file %s exists already", RCSfilename);
+                         continue;
+            }
+       }
+        else  {
+           switch (pairfilenames(argc, argv, rcswriteopen, true, false)) {
+                case -1: continue;    /*  not exist      */
+                case  0: continue;    /*  errors         */
+                case  1: break;       /*  file exists; ok*/
+            }
+       }
+
+
+        /* now RCSfilename contains the name of the RCS file, and
+         * workfilename contains the name of the working file.
+         * if !initflag, finptr contains the file descriptor for the
+         * RCS file. The admin node is initialized.
+         */
+
+       diagnose("RCS file: %s\n", RCSfilename);
+
+       RCSmode = defaultRCSmode;
+       if (initflag) {
+               if (stat(workfilename, &workstat) == 0)
+                       RCSmode = workstat.st_mode;
+       } else {
+               if (!checkaccesslist()) continue;
+               gettree(); /* Read the delta tree.  */
+               RCSmode = RCSstat.st_mode;
+       }
+       RCSmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
+
+        /*  update admin. node    */
+        if (strict_selected) StrictLocks = strictlock;
+       if (commsyml) {
+               Comment.string = commsyml;
+               Comment.size = strlen(commsyml);
+       }
+       if (0 <= expmode) Expand = expmode;
+
+        /* update default branch */
+       if (branchflag && expandsym(branchsym, &branchnum)) {
+           if (countnumflds(branchnum.string)) {
+               Dbranch = branchnum.string;
+            } else
+                Dbranch = nil;
+        }
+
+       doaccess();     /* Update access list.  */
+
+       doassoc();      /* Update association list.  */
+
+       dolocks();      /* Update locks.  */
+
+       domessages();   /* Update log messages.  */
+
+        /*  update state attribution  */
+        if (chgheadstate) {
+            /* change state of default branch or head */
+            if (Dbranch==nil) {
+                if (Head==nil)
+                    warn("can't change states in an empty tree");
+                else Head->state = headstate;
+            } else {
+               rcs_setstate(Dbranch,headstate); /* Can't set directly */
+            }
+        }
+        curstate = statelst;
+        while( curstate ) {
+            rcs_setstate(curstate->revno,curstate->status);
+            curstate = curstate->nextstatus;
+        }
+
+        cuthead = cuttail = nil;
+       if (delrev.strt && removerevs()) {
+            /*  rebuild delta tree if some deltas are deleted   */
+            if ( cuttail )
+               VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
+                            (char *)nil, &gendeltas);
+            buildtree();
+        }
+
+       if (nerror)
+               continue;
+
+        putadmin(frewrite);
+        if ( Head )
+           puttree(Head, frewrite);
+       putdesc(textflag,textfile);
+
+        if ( Head) {
+           if (!delrev.strt && !messagelst) {
+               /* No revision was deleted and no message was changed.  */
+               fastcopy(finptr, frewrite);
+            } else {
+               if (!cuttail || buildeltatext(gendeltas)) {
+                   advise_access(finptr, MADV_SEQUENTIAL);
+                   scanlogtext((struct hshentry *)nil, false);
+                    /* copy rest of delta text nodes that are not deleted      */
+               }
+            }
+        }
+       Izclose(&finptr);
+        if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
+           /* update mode */
+           ignoreints();
+           r = chnamemod(&frewrite, newRCSfilename, RCSfilename, RCSmode);
+           e = errno;
+           keepdirtemp(newRCSfilename);
+           restoreints();
+           if (r != 0) {
+               enerror(e, RCSfilename);
+               error("saved in %s", newRCSfilename);
+               dirtempunlink();
+                break;
+            }
+           diagnose("done\n");
+        } else {
+           diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename);
+        }
+       } while (cleanup(),
+                 ++argv, --argc >=1);
+
+       tempunlink();
+       exitmain(exitstatus);
+}       /* end of main (rcs) */
+
+       static void
+cleanup()
+{
+       if (nerror) exitstatus = EXIT_FAILURE;
+       Izclose(&finptr);
+       Ozclose(&fcopy);
+       Ozclose(&frewrite);
+       dirtempunlink();
+}
+
+       exiting void
+exiterr()
+{
+       dirtempunlink();
+       tempunlink();
+       _exit(EXIT_FAILURE);
+}
+
+
+       static void
+getassoclst(flag, sp)
+int     flag;
+char    * sp;
+/*  Function:   associate a symbolic name to a revision or branch,      */
+/*              and store in assoclst                                   */
+
+{
+        struct   Symrev  * pt;
+       char const *temp;
+        int                c;
+
+        while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
+        temp = sp;
+       sp = checkid(sp, ':');  /*  check for invalid symbolic name  */
+       c = *sp;   *sp = '\0';
+        while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
+
+        if ( c != ':' && c != '\0') {
+           error("invalid string %s after option -n or -N",sp);
+            return;
+        }
+
+       pt = talloc(struct Symrev);
+        pt->ssymbol = temp;
+        pt->override = flag;
+       if (c == '\0')  /*  delete symbol  */
+            pt->revno = nil;
+        else {
+            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
+           pt->revno = sp;
+        }
+        pt->nextsym = nil;
+        if (lastassoc)
+            lastassoc->nextsym = pt;
+        else
+            assoclst = pt;
+        lastassoc = pt;
+        return;
+}
+
+
+       static void
+getchaccess(login, command)
+       char const *login;
+       enum changeaccess command;
+{
+       register struct chaccess *pt;
+
+       *nextchaccess = pt = talloc(struct chaccess);
+       pt->login = login;
+       pt->command = command;
+       pt->nextchaccess = nil;
+       nextchaccess = &pt->nextchaccess;
+}
+
+
+
+       static void
+getaccessor(opt, command)
+       char *opt;
+       enum changeaccess command;
+/*   Function:  get the accessor list of options -e and -a,     */
+/*             and store in chaccess                           */
+
+
+{
+        register c;
+       register char *sp;
+
+       sp = opt;
+        while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
+        if ( c == '\0') {
+           if (command == erase  &&  sp-opt == 1) {
+               getchaccess((char const*)nil, command);
+               return;
+           }
+           error("missing login name after option -a or -e");
+           return;
+        }
+
+        while( c != '\0') {
+               getchaccess(sp, command);
+               sp = checkid(sp,',');
+               c = *sp;   *sp = '\0';
+                while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
+        }
+}
+
+
+       static void
+getmessage(option)
+       char *option;
+{
+       struct Message *pt;
+       struct cbuf cb;
+       char *m;
+
+       if (!(m = strchr(option, ':'))) {
+               error("-m option lacks revision number");
+               return;
+       }
+       *m++ = 0;
+       cb = cleanlogmsg(m, strlen(m));
+       if (!cb.size) {
+               error("-m option lacks log message");
+               return;
+       }
+       pt = talloc(struct Message);
+       pt->revno = option;
+       pt->message = cb;
+       pt->nextmessage = 0;
+       if (lastmessage)
+               lastmessage->nextmessage = pt;
+       else
+               messagelst = pt;
+       lastmessage = pt;
+}
+
+
+       static void
+getstates(sp)
+char    *sp;
+/*   Function:  get one state attribute and the corresponding   */
+/*              revision and store in statelst                  */
+
+{
+       char const *temp;
+        struct  Status  *pt;
+        register        c;
+
+        while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
+        temp = sp;
+       sp = checkid(sp,':');  /* check for invalid state attribute */
+       c = *sp;   *sp = '\0';
+        while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
+
+        if ( c == '\0' ) {  /*  change state of def. branch or Head  */
+            chgheadstate = true;
+            headstate  = temp;
+            return;
+        }
+        else if ( c != ':' ) {
+           error("missing ':' after state in option -s");
+            return;
+        }
+
+        while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
+       pt = talloc(struct Status);
+        pt->status     = temp;
+        pt->revno      = sp;
+        pt->nextstatus = nil;
+        if (laststate)
+            laststate->nextstatus = pt;
+        else
+            statelst = pt;
+        laststate = pt;
+}
+
+
+
+       static void
+getdelrev(sp)
+char    *sp;
+/*   Function:  get revision range or branch to be deleted,     */
+/*              and place in delrev                             */
+{
+        int    c;
+        struct  delrevpair      *pt;
+       int separator;
+
+       pt = &delrev;
+        while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
+
+       /* Support old ambiguous '-' syntax; this will go away.  */
+       if (strchr(sp,':'))
+               separator = ':';
+       else {
+               if (strchr(sp,'-')  &&  VERSION(5) <= RCSversion)
+                   warn("`-' is obsolete in `-o%s'; use `:' instead", sp);
+               separator = '-';
+       }
+
+       if (c == separator) { /* -o:rev */
+            while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
+            pt->strt = sp;    pt->code = 1;
+            while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
+            *sp = '\0';
+           pt->end = nil;
+            return;
+        }
+        else {
+            pt->strt = sp;
+            while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
+                  && c != separator )  c = *++sp;
+            *sp = '\0';
+            while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
+            if ( c == '\0' )  {  /*   -o rev or branch   */
+                pt->end = nil;   pt->code = 0;
+                return;
+            }
+           if (c != separator) {
+               faterror("invalid range %s %s after -o", pt->strt, sp);
+            }
+            while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
+           if (!c) {  /* -orev: */
+                pt->end = nil;   pt->code = 2;
+                return;
+            }
+        }
+       /* -orev1:rev2 */
+       pt->end = sp;  pt->code = 3;
+        while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
+        *sp = '\0';
+}
+
+
+
+
+       static void
+scanlogtext(delta,edit)
+       struct hshentry *delta;
+       int edit;
+/* Function: Scans delta text nodes up to and including the one given
+ * by delta, or up to last one present, if delta==nil.
+ * For the one given by delta (if delta!=nil), the log message is saved into
+ * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied.
+ * Assumes the initial lexeme must be read in first.
+ * Does not advance nexttok after it is finished, except if delta==nil.
+ */
+{
+       struct hshentry const *nextdelta;
+       struct cbuf cb;
+
+       for (;;) {
+               foutptr = 0;
+               if (eoflex()) {
+                    if(delta)
+                       faterror("can't find delta for revision %s", delta->num);
+                   return; /* no more delta text nodes */
+                }
+               nextlex();
+               if (!(nextdelta=getnum()))
+                   faterror("delta number corrupted");
+               if (nextdelta->selector) {
+                       foutptr = frewrite;
+                       aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
+                }
+               getkeystring(Klog);
+               if (nextdelta == cuttail) {
+                       cb = savestring(&curlogbuf);
+                       if (!delta->log.string)
+                           delta->log = cleanlogmsg(curlogbuf.string, cb.size);
+               } else if (nextdelta->log.string && nextdelta->selector) {
+                       foutptr = 0;
+                       readstring();
+                       foutptr = frewrite;
+                       putstring(foutptr, false, nextdelta->log, true);
+                       afputc(nextc, foutptr);
+                } else {readstring();
+                }
+                nextlex();
+               while (nexttok==ID && strcmp(NextString,Ktext)!=0)
+                       ignorephrase();
+               getkeystring(Ktext);
+
+               if (delta==nextdelta)
+                       break;
+               readstring(); /* skip over it */
+
+       }
+       /* got the one we're looking for */
+       if (edit)
+               editstring((struct hshentry *)nil);
+       else
+               enterstring();
+}
+
+
+
+       static struct Lockrev *
+rmnewlocklst(which)
+       struct Lockrev const *which;
+/*   Function:  remove lock to revision which->revno from newlocklst   */
+
+{
+        struct  Lockrev   * pt, *pre;
+
+        while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
+           struct Lockrev *pn = newlocklst->nextrev;
+           tfree(newlocklst);
+           newlocklst = pn;
+        }
+
+        pt = pre = newlocklst;
+        while( pt ) {
+            if ( ! strcmp(pt->revno, which->revno) ) {
+                pre->nextrev = pt->nextrev;
+               tfree(pt);
+               pt = pre->nextrev;
+            }
+            else {
+                pre = pt;
+                pt = pt->nextrev;
+            }
+        }
+        return pre;
+}
+
+
+
+       static void
+doaccess()
+{
+       register struct chaccess *ch;
+       register struct access **p, *t;
+
+       for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
+               switch (ch->command) {
+               case erase:
+                       if (!ch->login)
+                           AccessList = nil;
+                       else
+                           for (p = &AccessList;  (t = *p);  )
+                               if (strcmp(ch->login, t->login) == 0)
+                                       *p = t->nextaccess;
+                               else
+                                       p = &t->nextaccess;
+                       break;
+               case append:
+                       for (p = &AccessList;  ;  p = &t->nextaccess)
+                               if (!(t = *p)) {
+                                       *p = t = ftalloc(struct access);
+                                       t->login = ch->login;
+                                       t->nextaccess = nil;
+                                       break;
+                               } else if (strcmp(ch->login, t->login) == 0)
+                                       break;
+                       break;
+               }
+       }
+}
+
+
+       static int
+sendmail(Delta, who)
+       char const *Delta, *who;
+/*   Function:  mail to who, informing him that his lock on delta was
+ *   broken by caller. Ask first whether to go ahead. Return false on
+ *   error or if user decides not to break the lock.
+ */
+{
+#ifdef SENDMAIL
+       char const *messagefile;
+       int old1, old2, c;
+        FILE    * mailmess;
+#endif
+
+
+       aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
+       if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
+               return false;
+
+        /* go ahead with breaking  */
+#ifdef SENDMAIL
+       messagefile = maketemp(0);
+       if (!(mailmess = fopen(messagefile, "w"))) {
+           efaterror(messagefile);
+        }
+
+       aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n",
+               basename(RCSfilename), Delta, getfullRCSname(), getcaller()
+       );
+       aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);
+
+        old1 = '\n';    old2 = ' ';
+        for (; ;) {
+           c = getcstdin();
+           if (feof(stdin)) {
+               aprintf(mailmess, "%c\n", old1);
+                break;
+            }
+            else if ( c == '\n' && old1 == '.' && old2 == '\n')
+                break;
+            else {
+               afputc(old1, mailmess);
+                old2 = old1;   old1 = c;
+               if (c=='\n') aputs(">> ", stderr);
+            }
+        }
+       Ozclose(&mailmess);
+
+       if (run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil))
+               warn("Mail may have failed."),
+#else
+               warn("Mail notification of broken locks is not available."),
+#endif
+               warn("Please tell `%s' why you broke the lock.", who);
+       return(true);
+}
+
+
+
+       static void
+breaklock(delta)
+       struct hshentry const *delta;
+/* function: Finds the lock held by caller on delta,
+ * and removes it.
+ * Sends mail if a lock different from the caller's is broken.
+ * Prints an error message if there is no such lock or error.
+ */
+{
+        register struct lock * next, * trail;
+       char const *num;
+        struct lock dummy;
+
+       num=delta->num;
+        dummy.nextlock=next=Locks;
+        trail = &dummy;
+        while (next!=nil) {
+               if (strcmp(num, next->delta->num) == 0) {
+                       if (
+                               strcmp(getcaller(),next->login) != 0
+                           &&  !sendmail(num, next->login)
+                       ) {
+                           error("%s still locked by %s", num, next->login);
+                           return;
+                       }
+                       break; /* exact match */
+                }
+                trail=next;
+                next=next->nextlock;
+        }
+        if (next!=nil) {
+                /*found one */
+               diagnose("%s unlocked\n",next->delta->num);
+                trail->nextlock=next->nextlock;
+                next->delta->lockedby=nil;
+                Locks=dummy.nextlock;
+        } else  {
+               error("no lock set on revision %s", num);
+        }
+}
+
+
+
+       static struct hshentry *
+searchcutpt(object, length, store)
+       char const *object;
+       unsigned length;
+       struct hshentries *store;
+/*   Function:  Search store and return entry with number being object. */
+/*              cuttail = nil, if the entry is Head; otherwise, cuttail */
+/*              is the entry point to the one with number being object  */
+
+{
+       cuthead = nil;
+       while (compartial(store->first->num, object, length)) {
+               cuthead = store->first;
+               store = store->rest;
+       }
+       return store->first;
+}
+
+
+
+       static int
+branchpoint(strt, tail)
+struct  hshentry        *strt,  *tail;
+/*   Function: check whether the deltas between strt and tail  */
+/*             are locked or branch point, return 1 if any is  */
+/*             locked or branch point; otherwise, return 0 and */
+/*             mark deleted                                    */
+
+{
+        struct  hshentry    *pt;
+       struct lock const *lockpt;
+        int     flag;
+
+
+        pt = strt;
+        flag = false;
+        while( pt != tail) {
+            if ( pt->branches ){ /*  a branch point  */
+                flag = true;
+               error("can't remove branch point %s", pt->num);
+            }
+           lockpt = Locks;
+           while(lockpt && lockpt->delta != pt)
+               lockpt = lockpt->nextlock;
+           if ( lockpt ) {
+               flag = true;
+               error("can't remove locked revision %s",pt->num);
+           }
+            pt = pt->next;
+        }
+
+        if ( ! flag ) {
+            pt = strt;
+            while( pt != tail ) {
+               pt->selector = false;
+               diagnose("deleting revision %s\n",pt->num);
+                pt = pt->next;
+            }
+        }
+        return flag;
+}
+
+
+
+       static int
+removerevs()
+/*   Function:  get the revision range to be removed, and place the     */
+/*              first revision removed in delstrt, the revision before  */
+/*              delstrt in cuthead( nil, if delstrt is head), and the   */
+/*              revision after the last removed revision in cuttail(nil */
+/*              if the last is a leaf                                   */
+
+{
+       struct  hshentry *target, *target2, *temp;
+       unsigned length;
+       int flag;
+
+        flag = false;
+       if (!expandsym(delrev.strt, &numrev)) return 0;
+       target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
+        if ( ! target ) return 0;
+       if (cmpnum(target->num, numrev.string)) flag = true;
+       length = countnumflds(numrev.string);
+
+       if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
+           if (length & 1)
+               temp=searchcutpt(target->num,length+1,gendeltas);
+           else if (flag) {
+               error("Revision %s doesn't exist.", numrev.string);
+               return 0;
+           }
+           else
+               temp = searchcutpt(numrev.string, length, gendeltas);
+           cuttail = target->next;
+            if ( branchpoint(temp, cuttail) ) {
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;     /* first revision to be removed   */
+            return 1;
+        }
+
+       if (length & 1) {   /*  invalid branch after -o   */
+           error("invalid branch range %s after -o", numrev.string);
+            return 0;
+        }
+
+       if (delrev.code == 1) {  /*  -o  -rev   */
+            if ( length > 2 ) {
+                temp = searchcutpt( target->num, length-1, gendeltas);
+                cuttail = target->next;
+            }
+            else {
+                temp = searchcutpt(target->num, length, gendeltas);
+                cuttail = target;
+                while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
+                    cuttail = cuttail->next;
+            }
+            if ( branchpoint(temp, cuttail) ){
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;
+            return 1;
+        }
+
+       if (delrev.code == 2) {   /*  -o  rev-   */
+            if ( length == 2 ) {
+                temp = searchcutpt(target->num, 1,gendeltas);
+                if ( flag)
+                    cuttail = target;
+                else
+                    cuttail = target->next;
+            }
+            else  {
+                if ( flag){
+                    cuthead = target;
+                    if ( !(temp = target->next) ) return 0;
+                }
+                else
+                    temp = searchcutpt(target->num, length, gendeltas);
+               getbranchno(temp->num, &numrev);  /* get branch number */
+               target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
+            }
+            if ( branchpoint( temp, cuttail ) ) {
+                cuttail = nil;
+                return 0;
+            }
+            delstrt = temp;
+            return 1;
+        }
+
+        /*   -o   rev1-rev2   */
+       if (!expandsym(delrev.end, &numrev)) return 0;
+       if (
+               length != countnumflds(numrev.string)
+           ||  length>2 && compartial(numrev.string, target->num, length-1)
+       ) {
+           error("invalid revision range %s-%s", target->num, numrev.string);
+            return 0;
+        }
+
+       target2 = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas);
+        if ( ! target2 ) return 0;
+
+        if ( length > 2) {  /* delete revisions on branches  */
+            if ( cmpnum(target->num, target2->num) > 0) {
+               if (cmpnum(target2->num, numrev.string))
+                    flag = true;
+                else
+                    flag = false;
+                temp = target;
+                target = target2;
+                target2 = temp;
+            }
+            if ( flag ) {
+                if ( ! cmpnum(target->num, target2->num) ) {
+                   error("Revisions %s-%s don't exist.", delrev.strt,delrev.end);
+                    return 0;
+                }
+                cuthead = target;
+                temp = target->next;
+            }
+            else
+                temp = searchcutpt(target->num, length, gendeltas);
+            cuttail = target2->next;
+        }
+        else { /*  delete revisions on trunk  */
+            if ( cmpnum( target->num, target2->num) < 0 ) {
+                temp = target;
+                target = target2;
+                target2 = temp;
+            }
+            else
+               if (cmpnum(target2->num, numrev.string))
+                    flag = true;
+                else
+                    flag = false;
+            if ( flag ) {
+                if ( ! cmpnum(target->num, target2->num) ) {
+                   error("Revisions %s-%s don't exist.", delrev.strt, delrev.end);
+                    return 0;
+                }
+                cuttail = target2;
+            }
+            else
+                cuttail = target2->next;
+            temp = searchcutpt(target->num, length, gendeltas);
+        }
+        if ( branchpoint(temp, cuttail) )  {
+            cuttail = nil;
+            return 0;
+        }
+        delstrt = temp;
+        return 1;
+}
+
+
+
+       static void
+doassoc()
+/*   Function: add or delete(if revno is nil) association      */
+/*             which is stored in assoclst                     */
+
+{
+       char const *p;
+       struct Symrev const *curassoc;
+       struct  assoc   * pre,  * pt;
+
+        /*  add new associations   */
+        curassoc = assoclst;
+        while( curassoc ) {
+            if ( curassoc->revno == nil ) {  /* delete symbol  */
+               pre = pt = Symbols;
+                while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
+                   pre = pt;
+                   pt = pt->nextassoc;
+               }
+               if ( pt )
+                   if ( pre == pt )
+                       Symbols = pt->nextassoc;
+                   else
+                       pre->nextassoc = pt->nextassoc;
+               else
+                   warn("can't delete nonexisting symbol %s",curassoc->ssymbol);
+           }
+           else {
+               if (curassoc->revno[0]) {
+                   p = 0;
+                   if (expandsym(curassoc->revno, &numrev))
+                       p = fstr_save(numrev.string);
+               } else if (!(p = tiprev()))
+                   error("no latest revision to associate with symbol %s",
+                           curassoc->ssymbol
+                   );
+               if (p)
+                   VOID addsymbol(p, curassoc->ssymbol, curassoc->override);
+           }
+            curassoc = curassoc->nextsym;
+        }
+
+}
+
+
+
+       static void
+dolocks()
+/* Function: remove lock for caller or first lock if unlockcaller is set;
+ *           remove locks which are stored in rmvlocklst,
+ *           add new locks which are stored in newlocklst,
+ *           add lock for Dbranch or Head if lockhead is set.
+ */
+{
+       struct Lockrev const *lockpt;
+       struct hshentry *target;
+
+       if (unlockcaller) { /*  find lock for caller  */
+            if ( Head ) {
+               if (Locks) {
+                   switch (findlock(true, &target)) {
+                     case 0:
+                       breaklock(Locks->delta); /* remove most recent lock */
+                       break;
+                     case 1:
+                       diagnose("%s unlocked\n",target->num);
+                       break;
+                   }
+               } else {
+                   warn("No locks are set.");
+               }
+            } else {
+               warn("can't unlock an empty tree");
+            }
+        }
+
+        /*  remove locks which are stored in rmvlocklst   */
+        lockpt = rmvlocklst;
+        while( lockpt ) {
+           if (expandsym(lockpt->revno, &numrev)) {
+               target = genrevs(numrev.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas);
+                if ( target )
+                  if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+                       error("can't unlock nonexisting revision %s",lockpt->revno);
+                   else
+                       breaklock(target);
+                        /* breaklock does its own diagnose */
+            }
+            lockpt = lockpt->nextrev;
+        }
+
+        /*  add new locks which stored in newlocklst  */
+        lockpt = newlocklst;
+        while( lockpt ) {
+           setlock(lockpt->revno);
+            lockpt = lockpt->nextrev;
+        }
+
+       if (lockhead) {  /*  lock default branch or head  */
+            if (Dbranch) {
+               setlock(Dbranch);
+           } else if (Head) {
+               if (0 <= addlock(Head))
+                   diagnose("%s locked\n",Head->num);
+            } else {
+               warn("can't lock an empty tree");
+            }
+        }
+
+}
+
+
+
+       static void
+setlock(rev)
+       char const *rev;
+/* Function: Given a revision or branch number, finds the corresponding
+ * delta and locks it for caller.
+ */
+{
+        struct  hshentry *target;
+
+       if (expandsym(rev, &numrev)) {
+           target = genrevs(numrev.string, (char*)nil, (char*)nil,
+                            (char*)nil, &gendeltas);
+            if ( target )
+              if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+                   error("can't lock nonexisting revision %s", numrev.string);
+               else
+                   if (0 <= addlock(target))
+                       diagnose("%s locked\n", target->num);
+        }
+}
+
+
+       static void
+domessages()
+{
+       struct hshentry *target;
+       struct Message *p;
+
+       for (p = messagelst;  p;  p = p->nextmessage)
+           if (
+               expandsym(p->revno, &numrev)  &&
+               (target = genrevs(
+                       numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas
+               ))
+           )
+               target->log = p->message;
+}
+
+
+       static void
+rcs_setstate(rev,status)
+       char const *rev, *status;
+/* Function: Given a revision or branch number, finds the corresponding delta
+ * and sets its state to status.
+ */
+{
+        struct  hshentry *target;
+
+       if (expandsym(rev, &numrev)) {
+           target = genrevs(numrev.string, (char*)nil, (char*)nil,
+                            (char*)nil, &gendeltas);
+            if ( target )
+              if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+                   error("can't set state of nonexisting revision %s to %s",
+                         numrev.string, status);
+               else
+                    target->state = status;
+        }
+}
+
+
+
+
+
+       static int
+buildeltatext(deltas)
+       struct hshentries const *deltas;
+/*   Function:  put the delta text on frewrite and make necessary   */
+/*              change to delta text                                */
+{
+       register FILE *fcut;    /* temporary file to rebuild delta tree */
+       char const *cutfilename, *diffilename;
+
+       cutfilename = nil;
+       cuttail->selector = false;
+       scanlogtext(deltas->first, false);
+        if ( cuthead )  {
+           cutfilename = maketemp(3);
+           if (!(fcut = fopen(cutfilename, FOPEN_W_WORK))) {
+               efaterror(cutfilename);
+            }
+
+           while (deltas->first != cuthead) {
+               deltas = deltas->rest;
+               scanlogtext(deltas->first, true);
+            }
+
+           snapshotedit(fcut);
+           Ofclose(fcut);
+        }
+
+       while (deltas->first != cuttail)
+           scanlogtext((deltas = deltas->rest)->first, true);
+       finishedit((struct hshentry *)nil, (FILE*)0, true);
+       Ozclose(&fcopy);
+
+        if ( cuthead ) {
+           diffilename = maketemp(0);
+           switch (run((char*)nil,diffilename,
+                       DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil
+           )) {
+               case DIFF_FAILURE: case DIFF_SUCCESS: break;
+               default: faterror ("diff failed");
+           }
+           return putdtext(cuttail->num,cuttail->log,diffilename,frewrite,true);
+       } else
+           return putdtext(cuttail->num,cuttail->log,resultfile,frewrite,false);
+}
+
+
+
+       static void
+buildtree()
+/*   Function:  actually removes revisions whose selector field  */
+/*             is false, and rebuilds the linkage of deltas.    */
+/*              asks for reconfirmation if deleting last revision*/
+{
+       struct  hshentry   * Delta;
+        struct  branchhead      *pt, *pre;
+
+        if ( cuthead )
+           if ( cuthead->next == delstrt )
+                cuthead->next = cuttail;
+           else {
+                pre = pt = cuthead->branches;
+                while( pt && pt->hsh != delstrt )  {
+                    pre = pt;
+                    pt = pt->nextbranch;
+                }
+                if ( cuttail )
+                    pt->hsh = cuttail;
+                else if ( pt == pre )
+                    cuthead->branches = pt->nextbranch;
+                else
+                    pre->nextbranch = pt->nextbranch;
+            }
+       else {
+            if ( cuttail == nil && !quietflag) {
+               if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
+                   error("No revision deleted");
+                   Delta = delstrt;
+                   while( Delta) {
+                       Delta->selector = true;
+                       Delta = Delta->next;
+                   }
+                   return;
+               }
+           }
+            Head = cuttail;
+       }
+        return;
+}
+
+#if lint
+/* This lets us lint everything all at once. */
+
+char const cmdid[] = "";
+
+#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();}
+
+       int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       go(ciId,        ciExit);
+       go(coId,        coExit);
+       go(identId,     identExit);
+       go(mergeId,     mergeExit);
+       go(rcsId,       exiterr);
+       go(rcscleanId,  rcscleanExit);
+       go(rcsdiffId,   rdiffExit);
+       go(rcsmergeId,  rmergeExit);
+       go(rlogId,      rlogExit);
+       return 0;
+}
+#endif
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsbase.h b/usr/src/contrib/rcs-V5.6/src/rcsbase.h
new file mode 100644 (file)
index 0000000..c46b397
--- /dev/null
@@ -0,0 +1,678 @@
+
+/*
+ *                     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*));
+/* jlf int pairfilenames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); */
+int pairfilenames ();
+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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsclean.c b/usr/src/contrib/rcs-V5.6/src/rcsclean.c
new file mode 100644 (file)
index 0000000..ba24ab7
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsdiff.c b/usr/src/contrib/rcs-V5.6/src/rcsdiff.c
new file mode 100644 (file)
index 0000000..7155c8d
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsedit.c b/usr/src/contrib/rcs-V5.6/src/rcsedit.c
new file mode 100644 (file)
index 0000000..fab4f62
--- /dev/null
@@ -0,0 +1,1656 @@
+/*
+ *                     RCS stream editor
+ */
+/**********************************************************************************
+ *                       edits the input file according to a
+ *                       script from stdin, generated by diff -n
+ *                       performs keyword expansion
+ **********************************************************************************
+ */
+
+/* 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: rcsedit.c,v $
+ * Revision 5.11  1991/11/03  01:11:44  eggert
+ * Move the warning about link breaking to where they're actually being broken.
+ *
+ * Revision 5.10  1991/10/07  17:32:46  eggert
+ * Support piece tables even if !has_mmap.  Fix rare NFS bugs.
+ *
+ * Revision 5.9  1991/09/17  19:07:40  eggert
+ * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
+ *
+ * Revision 5.8  1991/08/19  03:13:55  eggert
+ * Add piece tables, NFS bug workarounds.  Catch odd filenames.  Tune.
+ *
+ * Revision 5.7  1991/04/21  11:58:21  eggert
+ * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.6  1991/02/25  07:12:40  eggert
+ * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
+ *
+ * Revision 5.5  1990/12/30  05:07:35  eggert
+ * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
+ *
+ * Revision 5.4  1990/11/01  05:03:40  eggert
+ * Permit arbitrary data in comment leaders.
+ *
+ * Revision 5.3  1990/09/11  02:41:13  eggert
+ * Tune expandline().
+ *
+ * Revision 5.2  1990/09/04  08:02:21  eggert
+ * Count RCS lines better.  Improve incomplete line handling.
+ *
+ * Revision 5.1  1990/08/29  07:13:56  eggert
+ * Add -kkvl.
+ * Fix bug when getting revisions to files ending in incomplete lines.
+ * Fix bug in comment leader expansion.
+ *
+ * Revision 5.0  1990/08/22  08:12:47  eggert
+ * Don't require final newline.
+ * Don't append "checked in with -k by " to logs,
+ * so that checking in a program with -k doesn't change it.
+ * Don't generate trailing white space for empty comment leader.
+ * Remove compile-time limits; use malloc instead.  Add -k, -V.
+ * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
+ * Ansify and Posixate.  Check diff's output.
+ *
+ * Revision 4.8  89/05/01  15:12:35  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.7  88/11/08  13:54:14  narten
+ * misplaced semicolon caused infinite loop
+ * 
+ * Revision 4.6  88/08/09  19:12:45  eggert
+ * Shrink stdio code size; allow cc -R.
+ * 
+ * Revision 4.5  87/12/18  11:38:46  narten
+ * Changes from the 43. version. Don't know the significance of the
+ * first change involving "rewind". Also, additional "lint" cleanup.
+ * (Guy Harris)
+ * 
+ * Revision 4.4  87/10/18  10:32:21  narten
+ * Updating version numbers. Changes relative to version 1.1 actually
+ * relative to 4.1
+ * 
+ * Revision 1.4  87/09/24  13:59:29  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.3  87/09/15  16:39:39  shepler
+ * added an initializatin of the variables editline and linecorr
+ * this will be done each time a file is processed.
+ * (there was an obscure bug where if co was used to retrieve multiple files
+ *  it would dump)
+ * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
+ * 
+ * Revision 1.2  87/03/27  14:22:17  jenkins
+ * Port to suns
+ * 
+ * Revision 4.1  83/05/12  13:10:30  wft
+ * Added new markers Id and RCSfile; added locker to Header and Id.
+ * Overhauled expandline completely() (problem with $01234567890123456789@).
+ * Moved trymatch() and marker table to rcskeys.c.
+ * 
+ * Revision 3.7  83/05/12  13:04:39  wft
+ * Added retry to expandline to resume after failed match which ended in $.
+ * Fixed truncation problem for $19chars followed by@@.
+ * Log no longer expands full path of RCS file.
+ * 
+ * Revision 3.6  83/05/11  16:06:30  wft
+ * added retry to expandline to resume after failed match which ended in $.
+ * Fixed truncation problem for $19chars followed by@@.
+ * 
+ * Revision 3.5  82/12/04  13:20:56  wft
+ * Added expansion of keyword Locker.
+ *
+ * Revision 3.4  82/12/03  12:26:54  wft
+ * Added line number correction in case editing does not start at the
+ * beginning of the file.
+ * Changed keyword expansion to always print a space before closing KDELIM;
+ * Expansion for Header shortened.
+ *
+ * Revision 3.3  82/11/14  14:49:30  wft
+ * removed Suffix from keyword expansion. Replaced fclose with ffclose.
+ * keyreplace() gets log message from delta, not from curlogmsg.
+ * fixed expression overflow in while(c=putc(GETC....
+ * checked nil printing.
+ *
+ * Revision 3.2  82/10/18  21:13:39  wft
+ * I added checks for write errors during the co process, and renamed
+ * expandstring() to xpandstring().
+ *
+ * Revision 3.1  82/10/13  15:52:55  wft
+ * changed type of result of getc() from char to int.
+ * made keyword expansion loop in expandline() portable to machines
+ * without sign-extension.
+ */
+
+
+#include "rcsbase.h"
+
+libId(editId, "$Id: rcsedit.c,v 5.11 1991/11/03 01:11:44 eggert Exp $")
+
+static void keyreplace P((enum markers,struct hshentry const*,FILE*));
+
+
+FILE *fcopy;            /* result file descriptor                          */
+char const *resultfile;  /* result file name                               */
+int locker_expansion;   /* should the locker name be appended to Id val?   */
+#if !large_memory
+       static RILE *fedit; /* edit file descriptor */
+       static char const *editfile; /* edit pathname */
+#endif
+static unsigned long editline; /* edit line counter; #lines before cursor   */
+static long linecorr; /* #adds - #deletes in each edit run.                */
+               /*used to correct editline in case file is not rewound after */
+               /* applying one delta                                        */
+
+#define DIRTEMPNAMES 2
+enum maker {notmade, real, effective};
+struct buf dirtfname[DIRTEMPNAMES];            /* unlink these when done */
+static enum maker volatile dirtfmaker[DIRTEMPNAMES];   /* if these are set */
+
+
+#if has_NFS || bad_unlink
+       int
+un_link(s)
+       char const *s;
+/*
+ * Remove S, even if it is unwritable.
+ * Ignore unlink() ENOENT failures; NFS generates bogus ones.
+ */
+{
+#      if bad_unlink
+               int e;
+               if (unlink(s) == 0)
+                       return 0;
+               e = errno;
+#              if has_NFS
+                       if (e == ENOENT)
+                               return 0;
+#              endif
+               if (chmod(s, S_IWUSR) != 0) {
+                       errno = e;
+                       return -1;
+               }
+#      endif
+#      if has_NFS
+               return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
+#      else
+               return unlink(s);
+#      endif
+}
+#endif
+
+#if !has_rename
+#  if !has_NFS
+#      define do_link(s,t) link(s,t)
+#  else
+       static int
+do_link(s, t)
+       char const *s, *t;
+/* Link S to T, ignoring bogus EEXIST problems due to NFS failures.  */
+{
+       struct stat sb, tb;
+
+       if (link(s,t) == 0)
+               return 0;
+       if (errno != EEXIST)
+               return -1;
+       if (
+           stat(s, &sb) == 0  &&
+           stat(t, &tb) == 0  &&
+           sb.st_ino == tb.st_ino  &&
+           sb.st_dev == tb.st_dev
+       )
+               return 0;
+       errno = EEXIST;
+       return -1;
+}
+#  endif
+#endif
+
+
+       static exiting void
+editEndsPrematurely()
+{
+       fatserror("edit script ends prematurely");
+}
+
+       static exiting void
+editLineNumberOverflow()
+{
+       fatserror("edit script refers to line past end of file");
+}
+
+
+#if large_memory
+
+#if has_memmove
+#      define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
+#else
+       static void
+movelines(s1, s2, n)
+       register Iptr_type *s1;
+       register Iptr_type const *s2;
+       register unsigned long n;
+{
+       if (s1 < s2)
+               do {
+                       *s1++ = *s2++;
+               } while (--n);
+       else {
+               s1 += n;
+               s2 += n;
+               do {
+                       *--s1 = *--s2;
+               } while (--n);
+       }
+}
+#endif
+
+/*
+ * `line' contains pointers to the lines in the currently `edited' file.
+ * It is a 0-origin array that represents linelim-gapsize lines.
+ * line[0..gap-1] and line[gap+gapsize..linelim-1] contain pointers to lines.
+ * line[gap..gap+gapsize-1] contains garbage.
+ *
+ * Any @s in lines are duplicated.
+ * Lines are terminated by \n, or (for a last partial line only) by single @.
+ */
+static Iptr_type *line;
+static unsigned long gap, gapsize, linelim;
+
+
+       static void
+insertline(n, l)
+       unsigned long n;
+       Iptr_type l;
+/* Before line N, insert line L.  N is 0-origin.  */
+{
+       if (linelim-gapsize < n)
+           editLineNumberOverflow();
+       if (!gapsize)
+           line =
+               !linelim ?
+                       tnalloc(Iptr_type, linelim = gapsize = 1024)
+               : (
+                       gap = gapsize = linelim,
+                       trealloc(Iptr_type, line, linelim <<= 1)
+               );
+       if (n < gap)
+           movelines(line+n+gapsize, line+n, gap-n);
+       else if (gap < n)
+           movelines(line+gap, line+gap+gapsize, n-gap);
+
+       line[n] = l;
+       gap = n + 1;
+       gapsize--;
+}
+
+       static void
+deletelines(n, nlines)
+       unsigned long n, nlines;
+/* Delete lines N through N+NLINES-1.  N is 0-origin.  */
+{
+       unsigned long l = n + nlines;
+       if (linelim-gapsize < l  ||  l < n)
+           editLineNumberOverflow();
+       if (l < gap)
+           movelines(line+l+gapsize, line+l, gap-l);
+       else if (gap < n)
+           movelines(line+gap, line+gap+gapsize, n-gap);
+
+       gap = n;
+       gapsize += nlines;
+}
+
+       static void
+snapshotline(f, l)
+       register FILE *f;
+       register Iptr_type l;
+{
+       register int c;
+       do {
+               if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
+                       return;
+               aputc(c, f);
+       } while (c != '\n');
+}
+
+       void
+snapshotedit(f)
+       FILE *f;
+/* Copy the current state of the edits to F.  */
+{
+       register Iptr_type *p, *lim, *l=line;
+       for (p=l, lim=l+gap;  p<lim;  )
+               snapshotline(f, *p++);
+       for (p+=gapsize, lim=l+linelim;  p<lim;  )
+               snapshotline(f, *p++);
+}
+
+       static void
+finisheditline(fin, fout, l, delta)
+       RILE *fin;
+       FILE *fout;
+       Iptr_type l;
+       struct hshentry const *delta;
+{
+       Iseek(fin, l);
+       if (expandline(fin, fout, delta, true, (FILE*)0)  <  0)
+               faterror("finisheditline internal error");
+}
+
+       void
+finishedit(delta, outfile, done)
+       struct hshentry const *delta;
+       FILE *outfile;
+       int done;
+/*
+ * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
+ * But do nothing unless DONE is set (which means we are on the last pass).
+ */
+{
+       if (done) {
+               openfcopy(outfile);
+               outfile = fcopy;
+               if (!delta)
+                       snapshotedit(outfile);
+               else {
+                       register Iptr_type *p, *lim, *l = line;
+                       register RILE *fin = finptr;
+                       Iptr_type here = Itell(fin);
+                       for (p=l, lim=l+gap;  p<lim;  )
+                               finisheditline(fin, outfile, *p++, delta);
+                       for (p+=gapsize, lim=l+linelim;  p<lim;  )
+                               finisheditline(fin, outfile, *p++, delta);
+                       Iseek(fin, here);
+               }
+       }
+}
+
+/* Open a temporary FILENAME for output, truncating any previous contents.  */
+#   define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK)
+#else /* !large_memory */
+    static FILE *
+fopen_update_truncate(filename)
+    char const *filename;
+{
+#      if bad_fopen_wplus
+               if (un_link(filename) != 0)
+                       efaterror(filename);
+#      endif
+       return fopen(filename, FOPEN_WPLUS_WORK);
+}
+#endif
+
+
+       void
+openfcopy(f)
+       FILE *f;
+{
+       if (!(fcopy = f)) {
+               if (!resultfile)
+                       resultfile = maketemp(2);
+               if (!(fcopy = fopen_update_truncate(resultfile)))
+                       efaterror(resultfile);
+       }
+}
+
+
+#if !large_memory
+
+       static void
+swapeditfiles(outfile)
+       FILE *outfile;
+/* Function: swaps resultfile and editfile, assigns fedit=fcopy,
+ * and rewinds fedit for reading.  Set fcopy to outfile if nonnull;
+ * otherwise, set fcopy to be resultfile opened for reading and writing.
+ */
+{
+       char const *tmpptr;
+
+       editline = 0;  linecorr = 0;
+       if (fseek(fcopy, 0L, SEEK_SET) != 0)
+               Oerror();
+       fedit = fcopy;
+        tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
+       openfcopy(outfile);
+}
+
+       void
+snapshotedit(f)
+       FILE *f;
+/* Copy the current state of the edits to F.  */
+{
+       finishedit((struct hshentry *)nil, (FILE*)0, false);
+       fastcopy(fedit, f);
+       Irewind(fedit);
+}
+
+       void
+finishedit(delta, outfile, done)
+       struct hshentry const *delta;
+       FILE *outfile;
+       int done;
+/* copy the rest of the edit file and close it (if it exists).
+ * if delta!=nil, perform keyword substitution at the same time.
+ * If DONE is set, we are finishing the last pass.
+ */
+{
+       register RILE *fe;
+       register FILE *fc;
+
+       fe = fedit;
+       if (fe) {
+               fc = fcopy;
+                if (delta!=nil) {
+                       while (1 < expandline(fe,fc,delta,false,(FILE*)0))
+                               ;
+                } else {
+                       fastcopy(fe,fc);
+                }
+               Ifclose(fe);
+        }
+       if (!done)
+               swapeditfiles(outfile);
+}
+#endif
+
+
+
+#if large_memory
+#      define copylines(upto,delta) (editline = (upto))
+#else
+       static void
+copylines(upto,delta)
+       register unsigned long upto;
+       struct hshentry const *delta;
+/*
+ * Copy input lines editline+1..upto from fedit to fcopy.
+ * If delta != nil, keyword expansion is done simultaneously.
+ * editline is updated. Rewinds a file only if necessary.
+ */
+{
+       register int c;
+       declarecache;
+       register FILE *fc;
+       register RILE *fe;
+
+       if (upto < editline) {
+                /* swap files */
+               finishedit((struct hshentry *)nil, (FILE*)0, false);
+                /* assumes edit only during last pass, from the beginning*/
+        }
+       fe = fedit;
+       fc = fcopy;
+       if (editline < upto)
+           if (delta)
+               do {
+                       if (expandline(fe,fc,delta,false,(FILE*)0) <= 1)
+                               editLineNumberOverflow();
+               } while (++editline < upto);
+           else {
+               setupcache(fe); cache(fe);
+               do {
+                       do {
+                               cachegeteof(c, editLineNumberOverflow(););
+                               aputc(c, fc);
+                       } while (c != '\n');
+               } while (++editline < upto);
+               uncache(fe);
+           }
+}
+#endif
+
+
+
+       void
+xpandstring(delta)
+       struct hshentry const *delta;
+/* Function: Reads a string terminated by SDELIM from finptr and writes it
+ * to fcopy. Double SDELIM is replaced with single SDELIM.
+ * Keyword expansion is performed with data from delta.
+ * If foutptr is nonnull, the string is also copied unchanged to foutptr.
+ */
+{
+       while (1 < expandline(finptr,fcopy,delta,true,foutptr))
+               ;
+}
+
+
+       void
+copystring()
+/* Function: copies a string terminated with a single SDELIM from finptr to
+ * fcopy, replacing all double SDELIM with a single SDELIM.
+ * If foutptr is nonnull, the string also copied unchanged to foutptr.
+ * editline is incremented by the number of lines copied.
+ * Assumption: next character read is first string character.
+ */
+{      register c;
+       declarecache;
+       register FILE *frew, *fcop;
+       register int amidline;
+       register RILE *fin;
+
+       fin = finptr;
+       setupcache(fin); cache(fin);
+       frew = foutptr;
+       fcop = fcopy;
+       amidline = false;
+       for (;;) {
+               GETC(frew,c);
+               switch (c) {
+                   case '\n':
+                       ++editline;
+                       ++rcsline;
+                       amidline = false;
+                       break;
+                   case SDELIM:
+                       GETC(frew,c);
+                       if (c != SDELIM) {
+                               /* end of string */
+                               nextc = c;
+                               editline += amidline;
+                               uncache(fin);
+                               return;
+                       }
+                       /* fall into */
+                   default:
+                       amidline = true;
+                       break;
+                }
+               aputc(c,fcop);
+        }
+}
+
+
+       void
+enterstring()
+/* Like copystring, except the string is put into the edit data structure.  */
+{
+#if !large_memory
+       editfile = 0;
+       fedit = 0;
+       editline = linecorr = 0;
+       resultfile = maketemp(1);
+       if (!(fcopy = fopen_update_truncate(resultfile)))
+               efaterror(resultfile);
+       copystring();
+#else
+       register int c;
+       declarecache;
+       register FILE *frew;
+       register unsigned long e, oe;
+       register int amidline, oamidline;
+       register Iptr_type optr;
+       register RILE *fin;
+
+       e = 0;
+       gap = 0;
+       gapsize = linelim;
+       fin = finptr;
+       setupcache(fin); cache(fin);
+       advise_access(fin, MADV_NORMAL);
+       frew = foutptr;
+       amidline = false;
+       for (;;) {
+               optr = cachetell();
+               GETC(frew,c);
+               oamidline = amidline;
+               oe = e;
+               switch (c) {
+                   case '\n':
+                       ++e;
+                       ++rcsline;
+                       amidline = false;
+                       break;
+                   case SDELIM:
+                       GETC(frew,c);
+                       if (c != SDELIM) {
+                               /* end of string */
+                               nextc = c;
+                               editline = e + amidline;
+                               linecorr = 0;
+                               uncache(fin);
+                               return;
+                       }
+                       /* fall into */
+                   default:
+                       amidline = true;
+                       break;
+               }
+               if (!oamidline)
+                       insertline(oe, optr);
+       }
+#endif
+}
+
+
+
+
+       void
+#if large_memory
+edit_string()
+#else
+  editstring(delta)
+       struct hshentry const *delta;
+#endif
+/*
+ * Read an edit script from finptr and applies it to the edit file.
+#if !large_memory
+ * The result is written to fcopy.
+ * If delta!=nil, keyword expansion is performed simultaneously.
+ * If running out of lines in fedit, fedit and fcopy are swapped.
+ * editfile is the name of the file that goes with fedit.
+#endif
+ * If foutptr is set, the edit script is also copied verbatim to foutptr.
+ * Assumes that all these files are open.
+ * resultfile is the name of the file that goes with fcopy.
+ * Assumes the next input character from finptr is the first character of
+ * the edit script. Resets nextc on exit.
+ */
+{
+        int ed; /* editor command */
+        register int c;
+       declarecache;
+       register FILE *frew;
+#      if !large_memory
+               register FILE *f;
+               unsigned long line_lim = ULONG_MAX;
+               register RILE *fe;
+#      endif
+       register unsigned long i;
+       register RILE *fin;
+#      if large_memory
+               register unsigned long j;
+#      endif
+       struct diffcmd dc;
+
+        editline += linecorr; linecorr=0; /*correct line number*/
+       frew = foutptr;
+       fin = finptr;
+       setupcache(fin);
+       initdiffcmd(&dc);
+       while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
+#if !large_memory
+               if (line_lim <= dc.line1)
+                       editLineNumberOverflow();
+               else
+#endif
+               if (!ed) {
+                       copylines(dc.line1-1, delta);
+                        /* skip over unwanted lines */
+                       i = dc.nlines;
+                       linecorr -= i;
+                       editline += i;
+#                      if large_memory
+                           deletelines(editline+linecorr, i);
+#                      else
+                           fe = fedit;
+                           do {
+                                /*skip next line*/
+                               do {
+                                   Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } );
+                               } while (c != '\n');
+                           } while (--i);
+#                      endif
+               } else {
+                       copylines(dc.line1, delta); /*copy only; no delete*/
+                       i = dc.nlines;
+#                      if large_memory
+                               j = editline+linecorr;
+#                      endif
+                       linecorr += i;
+#if !large_memory
+                       f = fcopy;
+                       if (delta)
+                           do {
+                               switch (expandline(fin,f,delta,true,frew)) {
+                                   case 0: case 1:
+                                       if (i==1)
+                                           return;
+                                       /* fall into */
+                                   case -1:
+                                       editEndsPrematurely();
+                               }
+                           } while (--i);
+                       else
+#endif
+                       {
+                           cache(fin);
+                           do {
+#                              if large_memory
+                                   insertline(j++, cachetell());
+#                              endif
+                               for (;;) {
+                                   GETC(frew, c);
+#                                  if !large_memory
+                                       aputc(c, f);
+#                                  endif
+                                   if (c == '\n')
+                                       break;
+                                   if (c==SDELIM) {
+                                       GETC(frew, c);
+                                       if (c!=SDELIM) {
+                                           if (--i)
+                                               editEndsPrematurely();
+                                           nextc = c;
+                                           uncache(fin);
+                                           return;
+                                       }
+                                   }
+                               }
+                               ++rcsline;
+                           } while (--i);
+                           uncache(fin);
+                       }
+                }
+}
+
+
+
+/* The rest is for keyword expansion */
+
+
+
+       int
+expandline(infile, outfile, delta, delimstuffed, frewfile)
+       RILE *infile;
+       FILE *outfile, *frewfile;
+       struct hshentry const *delta;
+       int delimstuffed;
+/*
+ * Read a line from INFILE and write it to OUTFILE.
+ * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
+ * Keyword expansion is performed with data from delta.
+ * If FREWFILE is set, copy the line unchanged to FREWFILE.
+ * DELIMSTUFFED must be true if FREWFILE is set.
+ * Yields -1 if no data is copied, 0 if an incomplete line is copied,
+ * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
+ */
+{
+       register c;
+       declarecache;
+       register FILE *out, *frew;
+       register char * tp;
+       register int e, ds, r;
+       char const *tlim;
+       static struct buf keyval;
+        enum markers matchresult;
+
+       setupcache(infile); cache(infile);
+       out = outfile;
+       frew = frewfile;
+       ds = delimstuffed;
+       bufalloc(&keyval, keylength+3);
+       e = 0;
+       r = -1;
+
+        for (;;) {
+           if (ds) {
+               GETC(frew, c);
+           } else
+               cachegeteof(c, goto uncache_exit;);
+           for (;;) {
+               switch (c) {
+                   case SDELIM:
+                       if (ds) {
+                           GETC(frew, c);
+                           if (c != SDELIM) {
+                                /* end of string */
+                                nextc=c;
+                               goto uncache_exit;
+                           }
+                       }
+                       /* fall into */
+                   default:
+                       aputc(c,out);
+                       r = 0;
+                       break;
+
+                   case '\n':
+                       rcsline += ds;
+                       aputc(c,out);
+                       r = 2;
+                       goto uncache_exit;
+
+                   case KDELIM:
+                       r = 0;
+                        /* check for keyword */
+                        /* first, copy a long enough string into keystring */
+                       tp = keyval.string;
+                       *tp++ = KDELIM;
+                       for (;;) {
+                           if (ds) {
+                               GETC(frew, c);
+                           } else
+                               cachegeteof(c, goto keystring_eof;);
+                           if (tp < keyval.string+keylength+1)
+                               switch (ctab[c]) {
+                                   case LETTER: case Letter:
+                                       *tp++ = c;
+                                       continue;
+                                   default:
+                                       break;
+                               }
+                           break;
+                        }
+                       *tp++ = c; *tp = '\0';
+                       matchresult = trymatch(keyval.string+1);
+                       if (matchresult==Nomatch) {
+                               tp[-1] = 0;
+                               aputs(keyval.string, out);
+                               continue;   /* last c handled properly */
+                       }
+
+                       /* Now we have a keyword terminated with a K/VDELIM */
+                       if (c==VDELIM) {
+                             /* try to find closing KDELIM, and replace value */
+                             tlim = keyval.string + keyval.size;
+                             for (;;) {
+                                     if (ds) {
+                                       GETC(frew, c);
+                                     } else
+                                       cachegeteof(c, goto keystring_eof;);
+                                     if (c=='\n' || c==KDELIM)
+                                       break;
+                                     *tp++ =c;
+                                     if (tlim <= tp)
+                                         tp = bufenlarge(&keyval, &tlim);
+                                     if (c==SDELIM && ds) { /*skip next SDELIM */
+                                               GETC(frew, c);
+                                               if (c != SDELIM) {
+                                                       /* end of string before closing KDELIM or newline */
+                                                       nextc = c;
+                                                       goto keystring_eof;
+                                               }
+                                     }
+                             }
+                             if (c!=KDELIM) {
+                                   /* couldn't find closing KDELIM -- give up */
+                                   *tp = 0;
+                                   aputs(keyval.string, out);
+                                   continue;   /* last c handled properly */
+                             }
+                       }
+                       /* now put out the new keyword value */
+                       keyreplace(matchresult,delta,out);
+                       e = 1;
+                       break;
+                }
+               break;
+           }
+        }
+
+    keystring_eof:
+       *tp = 0;
+       aputs(keyval.string, out);
+    uncache_exit:
+       uncache(infile);
+       return r + e;
+}
+
+
+char const ciklog[ciklogsize] = "checked in with -k by ";
+
+       static void
+keyreplace(marker,delta,out)
+       enum markers marker;
+       register struct hshentry const *delta;
+       register FILE *out;
+/* function: outputs the keyword value(s) corresponding to marker.
+ * Attributes are derived from delta.
+ */
+{
+       register char const *sp, *cp, *date;
+       register char c;
+       register size_t cs, cw, ls;
+       char const *sp1;
+       char datebuf[datesize];
+       int RCSv;
+
+       sp = Keyword[(int)marker];
+
+       if (Expand == KEY_EXPAND) {
+               aprintf(out, "%c%s%c", KDELIM, sp, KDELIM);
+               return;
+       }
+
+        date= delta->date;
+       RCSv = RCSversion;
+
+       if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND)
+               aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM,
+                       marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
+               );
+
+        switch (marker) {
+        case Author:
+               aputs(delta->author, out);
+                break;
+        case Date:
+               aputs(date2str(date,datebuf), out);
+                break;
+        case Id:
+       case Header:
+               aprintf(out, "%s %s %s %s %s",
+                         marker==Id || RCSv<VERSION(4)
+                       ? basename(RCSfilename)
+                       : getfullRCSname(),
+                       delta->num,
+                       date2str(date, datebuf),
+                       delta->author,
+                         RCSv==VERSION(3) && delta->lockedby ? "Locked"
+                       : delta->state
+               );
+               if (delta->lockedby!=nil)
+                   if (VERSION(5) <= RCSv) {
+                       if (locker_expansion || Expand==KEYVALLOCK_EXPAND)
+                           aprintf(out, " %s", delta->lockedby);
+                   } else if (RCSv == VERSION(4))
+                       aprintf(out, " Locker: %s", delta->lockedby);
+                break;
+        case Locker:
+               if (delta->lockedby)
+                   if (
+                               locker_expansion
+                       ||      Expand == KEYVALLOCK_EXPAND
+                       ||      RCSv <= VERSION(4)
+                   )
+                       aputs(delta->lockedby, out);
+                break;
+        case Log:
+        case RCSfile:
+               aputs(basename(RCSfilename), out);
+                break;
+        case Revision:
+               aputs(delta->num, out);
+                break;
+        case Source:
+               aputs(getfullRCSname(), out);
+                break;
+        case State:
+               aputs(delta->state, out);
+                break;
+       default:
+               break;
+        }
+       if (Expand == KEYVAL_EXPAND  ||  Expand == KEYVALLOCK_EXPAND) {
+               afputc(' ', out);
+               afputc(KDELIM, out);
+       }
+       if (marker == Log) {
+               sp = delta->log.string;
+               ls = delta->log.size;
+               if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
+                       return;
+               afputc('\n', out);
+               cp = Comment.string;
+               cw = cs = Comment.size;
+               awrite(cp, cs, out);
+               /* oddity: 2 spaces between date and time, not 1 as usual */
+               sp1 = strchr(date2str(date,datebuf), ' ');
+               aprintf(out, "Revision %s  %.*s %s  %s",
+                   delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author
+               );
+               /* Do not include state: it may change and is not updated.  */
+               /* Comment is the comment leader.  */
+               if (VERSION(5) <= RCSv)
+                   for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
+                       ;
+               for (;;) {
+                   afputc('\n', out);
+                   awrite(cp, cw, out);
+                   if (!ls)
+                       break;
+                   --ls;
+                   c = *sp++;
+                   if (c != '\n') {
+                       awrite(cp+cw, cs-cw, out);
+                       do {
+                           afputc(c,out);
+                           if (!ls)
+                               break;
+                           --ls;
+                           c = *sp++;
+                       } while (c != '\n');
+                   }
+               }
+       }
+}
+
+#if has_readlink
+       static int
+resolve_symlink(L)
+       struct buf *L;
+/*
+ * If L is a symbolic link, resolve it to the name that it points to.
+ * If unsuccessful, set errno and yield -1.
+ * If it points to an existing file, yield 1.
+ * Otherwise, set errno=ENOENT and yield 0.
+ */
+{
+       char *b, a[SIZEABLE_PATH];
+       int e;
+       size_t s;
+       ssize_t r;
+       struct buf bigbuf;
+       unsigned linkcount = MAXSYMLINKS + 1;
+
+       b = a;
+       s = sizeof(a);
+       bufautobegin(&bigbuf);
+       while ((r = readlink(L->string,b,s))  !=  -1)
+           if (r == s) {
+               bufalloc(&bigbuf, s<<1);
+               b = bigbuf.string;
+               s = bigbuf.size;
+           } else if (!--linkcount) {
+               errno = ELOOP;
+               return -1;
+           } else {
+               /* Splice symbolic link into L.  */
+               b[r] = '\0';
+               L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)]  =  '\0';
+               bufscat(L, b);
+           }
+       e = errno;
+       bufautoend(&bigbuf);
+       errno = e;
+       switch (e) {
+           case ENXIO:
+           case EINVAL: return 1;
+           case ENOENT: return 0;
+           default: return -1;
+       }
+}
+#endif
+
+       RILE *
+rcswriteopen(RCSbuf, status, mustread)
+       struct buf *RCSbuf;
+       struct stat *status;
+       int mustread;
+/*
+ * Create the lock file corresponding to RCSNAME.
+ * Then try to open RCSNAME for reading and yield its FILE* descriptor.
+ * Put its status into *STATUS too.
+ * MUSTREAD is true if the file must already exist, too.
+ * If all goes well, discard any previously acquired locks,
+ * and set frewrite to the FILE* descriptor of the lock file,
+ * which will eventually turn into the new RCS file.
+ */
+{
+       register char *tp;
+       register char const *sp, *RCSname, *x;
+       RILE *f;
+       size_t l;
+       int e, exists, fdesc, previouslock, r;
+       struct buf *dirt;
+       struct stat statbuf;
+
+       previouslock  =  frewrite != 0;
+       exists =
+#              if has_readlink
+                       resolve_symlink(RCSbuf);
+#              else
+                           stat(RCSbuf->string, &statbuf) == 0  ?  1
+                       :   errno==ENOENT ? 0 : -1;
+#              endif
+       if (exists < (mustread|previouslock))
+               /*
+                * There's an unusual problem with the RCS file;
+                * or the RCS file doesn't exist,
+                * and we must read or we already have a lock elsewhere.
+                */
+               return 0;
+
+       RCSname = RCSbuf->string;
+       sp = basename(RCSname);
+       l = sp - RCSname;
+       dirt = &dirtfname[previouslock];
+       bufscpy(dirt, RCSname);
+       tp = dirt->string + l;
+       x = rcssuffix(RCSname);
+#      if has_readlink
+           if (!x) {
+               error("symbolic link to non RCS filename `%s'", RCSname);
+               errno = EINVAL;
+               return 0;
+           }
+#      endif
+       if (*sp == *x) {
+               error("RCS filename `%s' incompatible with suffix `%s'", sp, x);
+               errno = EINVAL;
+               return 0;
+       }
+       /* Create a lock file whose name is a function of the RCS filename.  */
+       if (*x) {
+               /*
+                * The suffix is nonempty.
+                * The lock filename is the first char of of the suffix,
+                * followed by the RCS filename with last char removed.  E.g.:
+                *      foo,v   RCS filename with suffix ,v
+                *      ,foo,   lock filename
+                */
+               *tp++ = *x;
+               while (*sp)
+                       *tp++ = *sp++;
+               *--tp = 0;
+       } else {
+               /*
+                * The suffix is empty.
+                * The lock filename is the RCS filename
+                * with last char replaced by '_'.
+                */
+               while ((*tp++ = *sp++))
+                       ;
+               tp -= 2;
+               if (*tp == '_') {
+                       error("RCS filename `%s' ends with `%c'", RCSname, *tp);
+                       errno = EINVAL;
+                       return 0;
+               }
+               *tp = '_';
+       }
+
+       sp = tp = dirt->string;
+
+       f = 0;
+
+       /*
+       * good news:
+       *       open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic
+       *       according to Posix 1003.1-1990.
+       * bad news:
+       *       NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
+       * good news:
+       *       (O_TRUNC,READONLY) normally guarantees atomicity even with NFS.
+       * bad news:
+       *       If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity.
+       * good news:
+       *       Root-over-the-wire NFS access is rare for security reasons.
+       *       This bug has never been reported in practice with RCS.
+       * So we don't worry about this bug.
+       *
+       * An even rarer NFS bug can occur when clients retry requests.
+       * Suppose client A renames the lock file ",f," to "f,v"
+       * at about the same time that client B creates ",f,",
+       * and suppose A's first rename request is delayed, so A reissues it.
+       * The sequence of events might be:
+       *       A sends rename(",f,", "f,v")
+       *       B sends create(",f,")
+       *       A sends retry of rename(",f,", "f,v")
+       *       server receives, does, and acknowledges A's first rename()
+       *       A receives acknowledgment, and its RCS program exits
+       *       server receives, does, and acknowledges B's create()
+       *       server receives, does, and acknowledges A's retry of rename()
+       * This not only wrongly deletes B's lock, it removes the RCS file!
+       * Most NFS implementations have idempotency caches that usually prevent
+       * this scenario, but such caches are finite and can be overrun.
+       * This problem afflicts programs that use the traditional
+       * Unix method of using link() and unlink() to get and release locks,
+       * as well as RCS's method of using open() and rename().
+       * There is no easy workaround for either link-unlink or open-rename.
+       * Any new method based on lockf() seemingly would be incompatible with
+       * the old methods; besides, lockf() is notoriously buggy under NFS.
+       * Since this problem afflicts scads of Unix programs, but is so rare
+       * that nobody seems to be worried about it, we won't worry either.
+       */
+#      define READONLY (S_IRUSR|S_IRGRP|S_IROTH)
+#      if !open_can_creat
+#              define create(f) creat(f, READONLY)
+#      else
+#              define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY)
+#      endif
+
+       catchints();
+       ignoreints();
+
+       /*
+        * Create a lock file for an RCS file.  This should be atomic, i.e.
+        * if two processes try it simultaneously, at most one should succeed.
+        */
+       seteid();
+       fdesc = create(sp);
+       e = errno;
+       setrid();
+
+       if (fdesc < 0) {
+               if (e == EACCES  &&  stat(tp,&statbuf) == 0)
+                       /* The RCS file is busy.  */
+                       e = EEXIST;
+       } else {
+               dirtfmaker[0] = effective;
+               e = ENOENT;
+               if (exists) {
+                   f = Iopen(RCSname, FOPEN_R, status);
+                   e = errno;
+                   if (f && previouslock) {
+                       /* Discard the previous lock in favor of this one.  */
+                       Ozclose(&frewrite);
+                       seteid();
+                       if ((r = un_link(newRCSfilename)) != 0)
+                           e = errno;
+                       setrid();
+                       if (r != 0)
+                           enfaterror(e, newRCSfilename);
+                       bufscpy(&dirtfname[0], tp);
+                   }
+               }
+               if (!(frewrite = fdopen(fdesc, FOPEN_W))) {
+                   efaterror(newRCSfilename);
+               }
+       }
+
+       restoreints();
+
+       errno = e;
+       return f;
+}
+
+       void
+keepdirtemp(name)
+       char const *name;
+/* Do not unlink name, either because it's not there any more,
+ * or because it has already been unlinked.
+ */
+{
+       register int i;
+       for (i=DIRTEMPNAMES; 0<=--i; )
+               if (dirtfname[i].string == name) {
+                       dirtfmaker[i] = notmade;
+                       return;
+               }
+       faterror("keepdirtemp");
+}
+
+       char const *
+makedirtemp(name, n)
+       register char const *name;
+       int n;
+/*
+ * Have maketemp() do all the work if name is null.
+ * Otherwise, create a unique filename in name's dir using n and name
+ * and store it into the dirtfname[n].
+ * Because of storage in tfnames, dirtempunlink() can unlink the file later.
+ * Return a pointer to the filename created.
+ */
+{
+       register char *tp, *np;
+       register size_t dl;
+       register struct buf *bn;
+
+       if (!name)
+               return maketemp(n);
+       dl = dirlen(name);
+       bn = &dirtfname[n];
+       bufalloc(bn,
+#              if has_mktemp
+                       dl + 9
+#              else
+                       strlen(name) + 3
+#              endif
+       );
+       bufscpy(bn, name);
+       np = tp = bn->string;
+       tp += dl;
+       *tp++ = '_';
+       *tp++ = '0'+n;
+       catchints();
+#      if has_mktemp
+               VOID strcpy(tp, "XXXXXX");
+               if (!mktemp(np) || !*np)
+                   faterror("can't make temporary file name `%.*s%c_%cXXXXXX'",
+                       (int)dl, name, SLASH, '0'+n
+                   );
+#      else
+               /*
+                * Posix 1003.1-1990 has no reliable way
+                * to create a unique file in a named directory.
+                * We fudge here.  If the working file name is abcde,
+                * the temp filename is _Ncde where N is a digit.
+                */
+               name += dl;
+               if (*name) name++;
+               if (*name) name++;
+               VOID strcpy(tp, name);
+#      endif
+       dirtfmaker[n] = real;
+       return np;
+}
+
+       void
+dirtempunlink()
+/* Clean up makedirtemp() files.  May be invoked by signal handler. */
+{
+       register int i;
+       enum maker m;
+
+       for (i = DIRTEMPNAMES;  0 <= --i;  )
+           if ((m = dirtfmaker[i]) != notmade) {
+               if (m == effective)
+                   seteid();
+               VOID un_link(dirtfname[i].string);
+               if (m == effective)
+                   setrid();
+               dirtfmaker[i] = notmade;
+           }
+}
+
+
+       int
+#if has_prototypes
+chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode)
+  /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
+#else
+  chnamemod(fromp,from,to,mode) FILE **fromp; char const *from,*to; mode_t mode;
+#endif
+/*
+ * Rename a file (with optional stream pointer *FROMP) from FROM to TO.
+ * FROM already exists.
+ * Change its mode to MODE, before renaming if possible.
+ * If FROMP, close and clear *FROMP before renaming it.
+ * Unlink TO if it already exists.
+ * Return -1 on error (setting errno), 0 otherwise.
+ */
+{
+#      if bad_a_rename
+               /*
+                * This host is brain damaged.  A race condition is possible
+                * while the lock file is temporarily writable.
+                * There doesn't seem to be a workaround.
+                */
+               mode_t mode_while_renaming = mode|S_IWUSR;
+#      else
+#              define mode_while_renaming mode
+#      endif
+       if (fromp) {
+#              if has_fchmod
+                       if (fchmod(fileno(*fromp), mode_while_renaming) != 0)
+                               return -1;
+#              endif
+               Ozclose(fromp);
+       }
+#      if has_fchmod
+           else
+#      endif
+           if (chmod(from, mode_while_renaming) != 0)
+               return -1;
+
+#      if !has_rename || bad_b_rename
+               VOID un_link(to);
+               /*
+                * We need not check the result;
+                * link() or rename() will catch it.
+                * No harm is done if TO does not exist.
+                * However, there's a short window of inconsistency
+                * during which TO does not exist.
+                */
+#      endif
+
+       return
+#          if !has_rename
+               do_link(from,to) != 0  ?  -1  :  un_link(from)
+#          else
+                   rename(from, to) != 0
+#                  if has_NFS
+                       && errno != ENOENT
+#                  endif
+               ?  -1
+#              if bad_a_rename
+               :  mode != mode_while_renaming  ?  chmod(to, mode)
+#              endif
+               :  0
+#          endif
+       ;
+
+#      undef mode_while_renaming
+}
+
+
+
+       int
+findlock(delete, target)
+       int delete;
+       struct hshentry **target;
+/*
+ * Find the first lock held by caller and return a pointer
+ * to the locked delta; also removes the lock if DELETE.
+ * If one lock, put it into *TARGET.
+ * Return 0 for no locks, 1 for one, 2 for two or more.
+ */
+{
+       register struct lock *next, **trail, **found;
+
+       found = 0;
+       for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
+               if (strcmp(getcaller(), next->login)  ==  0) {
+                       if (found) {
+                               error("multiple revisions locked by %s; please specify one", getcaller());
+                               return 2;
+                       }
+                       found = trail;
+               }
+       if (!found)
+               return 0;
+       next = *found;
+       *target = next->delta;
+       if (delete) {
+               next->delta->lockedby = nil;
+               *found = next->nextlock;
+       }
+       return 1;
+}
+
+       int
+addlock(delta)
+       struct hshentry * delta;
+/*
+ * Add a lock held by caller to DELTA and yield 1 if successful.
+ * Print an error message and yield -1 if no lock is added because
+ * DELTA is locked by somebody other than caller.
+ * Return 0 if the caller already holds the lock.
+ */
+{
+       register struct lock *next;
+
+       next=Locks;
+       for (next = Locks;  next;  next = next->nextlock)
+               if (cmpnum(delta->num, next->delta->num) == 0)
+                       if (strcmp(getcaller(), next->login) == 0)
+                               return 0;
+                       else {
+                               error("revision %s already locked by %s",
+                                     delta->num, next->login
+                               );
+                               return -1;
+                       }
+       next = ftalloc(struct lock);
+       delta->lockedby = next->login = getcaller();
+       next->delta = delta;
+       next->nextlock = Locks;
+       Locks = next;
+       return 1;
+}
+
+
+       int
+addsymbol(num, name, rebind)
+       char const *num, *name;
+       int rebind;
+/*
+ * Associate with revision NUM the new symbolic NAME.
+ * If NAME already exists and REBIND is set, associate NAME with NUM;
+ * otherwise, print an error message and return false;
+ * Return true if successful.
+ */
+{
+       register struct assoc *next;
+
+       for (next = Symbols;  next;  next = next->nextassoc)
+               if (strcmp(name, next->symbol)  ==  0)
+                       if (rebind  ||  strcmp(next->num,num) == 0) {
+                               next->num = num;
+                               return true;
+                       } else {
+                               error("symbolic name %s already bound to %s",
+                                       name, next->num
+                               );
+                               return false;
+                       }
+       next = ftalloc(struct assoc);
+       next->symbol = name;
+       next->num = num;
+       next->nextassoc = Symbols;
+       Symbols = next;
+       return true;
+}
+
+
+
+       char const *
+getcaller()
+/* Get the caller's login name.  */
+{
+#      if has_setuid
+               return getusername(euid()!=ruid());
+#      else
+               return getusername(false);
+#      endif
+}
+
+
+       int
+checkaccesslist()
+/*
+ * Return true if caller is the superuser, the owner of the
+ * file, the access list is empty, or caller is on the access list.
+ * Otherwise, print an error message and return false.
+ */
+{
+       register struct access const *next;
+
+       if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
+               return true;
+
+       next = AccessList;
+       do {
+               if (strcmp(getcaller(), next->login)  ==  0)
+                       return true;
+       } while ((next = next->nextaccess));
+
+       error("user %s not on the access list", getcaller());
+       return false;
+}
+
+
+       int
+dorewrite(lockflag, changed)
+       int lockflag, changed;
+/*
+ * Do nothing if LOCKFLAG is zero.
+ * Prepare to rewrite an RCS file if CHANGED is positive.
+ * Stop rewriting if CHANGED is zero, because there won't be any changes.
+ * Fail if CHANGED is negative.
+ * Return true on success.
+ */
+{
+       int r, e;
+
+       if (lockflag)
+               if (changed) {
+                       if (changed < 0)
+                               return false;
+                       putadmin(frewrite);
+                       puttree(Head, frewrite);
+                       aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
+                       foutptr = frewrite;
+               } else {
+                       Ozclose(&frewrite);
+                       seteid();
+                       ignoreints();
+                       r = un_link(newRCSfilename);
+                       e = errno;
+                       keepdirtemp(newRCSfilename);
+                       restoreints();
+                       setrid();
+                       if (r != 0) {
+                               enerror(e, RCSfilename);
+                               return false;
+                       }
+               }
+       return true;
+}
+
+       int
+donerewrite(changed)
+       int changed;
+/*
+ * Finish rewriting an RCS file if CHANGED is nonzero.
+ * Return true on success.
+ */
+{
+       int r, e;
+
+       if (changed && !nerror) {
+               if (finptr) {
+                       fastcopy(finptr, frewrite);
+                       Izclose(&finptr);
+               }
+               if (1 < RCSstat.st_nlink)
+                       warn("breaking hard link to %s", RCSfilename);
+               seteid();
+               ignoreints();
+               r = chnamemod(&frewrite, newRCSfilename, RCSfilename,
+                       RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH)
+               );
+               e = errno;
+               keepdirtemp(newRCSfilename);
+               restoreints();
+               setrid();
+               if (r != 0) {
+                       enerror(e, RCSfilename);
+                       error("saved in %s", newRCSfilename);
+                       dirtempunlink();
+                       return false;
+               }
+       }
+       return true;
+}
+
+       void
+aflush(f)
+       FILE *f;
+{
+       if (fflush(f) != 0)
+               Oerror();
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsfcmp.c b/usr/src/contrib/rcs-V5.6/src/rcsfcmp.c
new file mode 100644 (file)
index 0000000..75a6bbc
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsfreeze.sh b/usr/src/contrib/rcs-V5.6/src/rcsfreeze.sh
new file mode 100644 (file)
index 0000000..a20715f
--- /dev/null
@@ -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/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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsgen.c b/usr/src/contrib/rcs-V5.6/src/rcsgen.c
new file mode 100644 (file)
index 0000000..9a6072e
--- /dev/null
@@ -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);
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/rcskeep.c b/usr/src/contrib/rcs-V5.6/src/rcskeep.c
new file mode 100644 (file)
index 0000000..1a0c78f
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcskeys.c b/usr/src/contrib/rcs-V5.6/src/rcskeys.c
new file mode 100644 (file)
index 0000000..82850a7
--- /dev/null
@@ -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);
+}
+
diff --git a/usr/src/contrib/rcs-V5.6/src/rcslex.c b/usr/src/contrib/rcs-V5.6/src/rcslex.c
new file mode 100644 (file)
index 0000000..51e31f3
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ *                     RCS file input
+ */
+/*********************************************************************************
+ *                     Lexical Analysis.
+ *                     hashtable, Lexinit, nextlex, getlex, getkey,
+ *                     getid, getnum, readstring, printstring, savestring,
+ *                     checkid, fatserror, error, faterror, warn, diagnose
+ *                     Testprogram: define LEXDB
+ *********************************************************************************
+ */
+
+/* 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: rcslex.c,v $
+ * Revision 5.11  1991/11/03  03:30:44  eggert
+ * Fix porting bug to ancient hosts lacking vfprintf.
+ *
+ * Revision 5.10  1991/10/07  17:32:46  eggert
+ * Support piece tables even if !has_mmap.
+ *
+ * Revision 5.9  1991/09/24  00:28:42  eggert
+ * Don't export errsay().
+ *
+ * Revision 5.8  1991/08/19  03:13:55  eggert
+ * Add eoflex(), mmap support.  Tune.
+ *
+ * Revision 5.7  1991/04/21  11:58:26  eggert
+ * Add MS-DOS support.
+ *
+ * Revision 5.6  1991/02/25  07:12:42  eggert
+ * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
+ *
+ * Revision 5.5  1990/12/04  05:18:47  eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.4  1990/11/19  20:05:28  hammer
+ * no longer gives warning about unknown keywords if -q is specified
+ *
+ * Revision 5.3  1990/11/01  05:03:48  eggert
+ * When ignoring unknown phrases, copy them to the output RCS file.
+ *
+ * Revision 5.2  1990/09/04  08:02:27  eggert
+ * Count RCS lines better.
+ *
+ * Revision 5.1  1990/08/29  07:14:03  eggert
+ * Work around buggy compilers with defective argument promotion.
+ *
+ * Revision 5.0  1990/08/22  08:12:55  eggert
+ * Remove compile-time limits; use malloc instead.
+ * Report errno-related errors with perror().
+ * Ansify and Posixate.  Add support for ISO 8859.
+ * Use better hash function.
+ *
+ * Revision 4.6  89/05/01  15:13:07  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.5  88/08/28  15:01:12  eggert
+ * Don't loop when writing error messages to a full filesystem.
+ * Flush stderr/stdout when mixing output.
+ * Yield exit status compatible with diff(1).
+ * Shrink stdio code size; allow cc -R; remove lint.
+ * 
+ * Revision 4.4  87/12/18  11:44:47  narten
+ * fixed to use "varargs" in "fprintf"; this is required if it is to
+ * work on a SPARC machine such as a Sun-4
+ * 
+ * Revision 4.3  87/10/18  10:37:18  narten
+ * Updating version numbers. Changes relative to 1.1 actually relative
+ * to version 4.1
+ * 
+ * Revision 1.3  87/09/24  14:00:17  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.2  87/03/27  14:22:33  jenkins
+ * Port to suns
+ * 
+ * Revision 4.1  83/03/25  18:12:51  wft
+ * Only changed $Header to $Id.
+ * 
+ * Revision 3.3  82/12/10  16:22:37  wft
+ * Improved error messages, changed exit status on error to 1.
+ *
+ * Revision 3.2  82/11/28  21:27:10  wft
+ * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
+ * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
+ * properly in case there is an IO-error (e.g., file system full).
+ *
+ * Revision 3.1  82/10/11  19:43:56  wft
+ * removed unused label out:;
+ * made sure all calls to getc() return into an integer, not a char.
+ */
+
+
+/*
+#define LEXDB
+*/
+/* version LEXDB is for testing the lexical analyzer. The testprogram
+ * reads a stream of lexemes, enters the revision numbers into the
+ * hashtable, and prints the recognized tokens. Keywords are recognized
+ * as identifiers.
+ */
+
+
+
+#include "rcsbase.h"
+
+libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $")
+
+static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
+
+enum tokens     nexttok;    /*next token, set by nextlex                    */
+
+int             hshenter;   /*if true, next suitable lexeme will be entered */
+                            /*into the symbol table. Handle with care.      */
+int             nextc;      /*next input character, initialized by Lexinit  */
+
+unsigned long  rcsline;    /*current line-number of input                  */
+int             nerror;     /*counter for errors                            */
+int             quietflag;  /*indicates quiet mode                          */
+RILE *         finptr;     /*input file descriptor                         */
+
+FILE *          frewrite;   /*file descriptor for echoing input             */
+
+FILE *         foutptr;    /* copy of frewrite, but 0 to suppress echo  */
+
+static struct buf tokbuf;   /* token buffer                                */
+
+char const *    NextString; /* next token                                  */
+
+/*
+ * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
+ * so hshsize should be odd.
+ * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
+ * Software--practice & experience 20, 2 (Feb 1990), 209-224.
+ */
+#ifndef hshsize
+#      define hshsize 511
+#endif
+
+static struct hshentry *hshtab[hshsize]; /*hashtable                       */
+
+static int ignored_phrases; /* have we ignored phrases in this RCS file? */
+
+    void
+warnignore()
+{
+    if (! (ignored_phrases|quietflag)) {
+       ignored_phrases = true;
+       warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
+    }
+}
+
+
+
+       static void
+lookup(str)
+       char const *str;
+/* Function: Looks up the character string pointed to by str in the
+ * hashtable. If the string is not present, a new entry for it is created.
+ * In any case, the address of the corresponding hashtable entry is placed
+ * into nexthsh.
+ */
+{
+       register unsigned ihash;  /* index into hashtable */
+       register char const *sp;
+       register struct hshentry *n, **p;
+
+        /* calculate hash code */
+       sp = str;
+        ihash = 0;
+       while (*sp)
+               ihash  =  (ihash<<2) + *sp++;
+       ihash %= hshsize;
+
+       for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
+               if (!(n = *p)) {
+                       /* empty slot found */
+                       *p = n = ftalloc(struct hshentry);
+                       n->num = fstr_save(str);
+                       n->nexthsh = nil;
+#                      ifdef LEXDB
+                               VOID printf("\nEntered: %s at %u ", str, ihash);
+#                      endif
+                       break;
+               } else if (strcmp(str, n->num) == 0)
+                       /* match found */
+                       break;
+       nexthsh = n;
+       NextString = n->num;
+}
+
+
+
+
+
+
+       void
+Lexinit()
+/* Function: Initialization of lexical analyzer:
+ * initializes the hashtable,
+ * initializes nextc, nexttok if finptr != 0
+ */
+{       register int            c;
+
+       for (c = hshsize;  0 <= --c;  ) {
+               hshtab[c] = nil;
+        }
+
+       nerror = 0;
+       if (finptr) {
+               foutptr = 0;
+               hshenter = true;
+               ignored_phrases = false;
+               rcsline = 1;
+               bufrealloc(&tokbuf, 2);
+               Iget(finptr, nextc);
+                nextlex();            /*initial token*/
+        }
+}
+
+
+
+
+
+
+
+       void
+nextlex()
+
+/* Function: Reads the next token and sets nexttok to the next token code.
+ * Only if hshenter is set, a revision number is entered into the
+ * hashtable and a pointer to it is placed into nexthsh.
+ * This is useful for avoiding that dates are placed into the hashtable.
+ * For ID's and NUM's, NextString is set to the character string.
+ * Assumption: nextc contains the next character.
+ */
+{       register c;
+       declarecache;
+       register FILE *frew;
+        register char * sp;
+       char const *limit;
+        register enum tokens d;
+       register RILE *fin;
+
+       fin=finptr; frew=foutptr;
+       setupcache(fin); cache(fin);
+       c = nextc;
+
+       for (;;) { switch ((d = ctab[c])) {
+
+       default:
+               fatserror("unknown character `%c'", c);
+               /*NOTREACHED*/
+
+        case NEWLN:
+               ++rcsline;
+#               ifdef LEXDB
+               afputc('\n',stdout);
+#               endif
+                /* Note: falls into next case */
+
+        case SPACE:
+               GETC(frew, c);
+               continue;
+
+        case DIGIT:
+               sp = tokbuf.string;
+               limit = sp + tokbuf.size;
+               *sp++ = c;
+               for (;;) {
+                       GETC(frew, c);
+                       if ((d=ctab[c])!=DIGIT && d!=PERIOD)
+                               break;
+                        *sp++ = c;         /* 1.2. and 1.2 are different */
+                       if (limit <= sp)
+                               sp = bufenlarge(&tokbuf, &limit);
+                }
+               *sp = 0;
+               if (hshenter)
+                       lookup(tokbuf.string);
+               else
+                       NextString = fstr_save(tokbuf.string);
+               d = NUM;
+               break;
+
+
+        case LETTER:
+       case Letter:
+               sp = tokbuf.string;
+               limit = sp + tokbuf.size;
+               *sp++ = c;
+               for (;;) {
+                       GETC(frew, c);
+                       if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
+                               break;
+                        *sp++ = c;
+                       if (limit <= sp)
+                               sp = bufenlarge(&tokbuf, &limit);
+                }
+               *sp = 0;
+               NextString = fstr_save(tokbuf.string);
+               d = ID;  /* may be ID or keyword */
+               break;
+
+        case SBEGIN: /* long string */
+               d = STRING;
+                /* note: only the initial SBEGIN has been read*/
+                /* read the string, and reset nextc afterwards*/
+               break;
+
+       case COLON:
+       case SEMI:
+               GETC(frew, c);
+               break;
+       } break; }
+       nextc = c;
+       nexttok = d;
+       uncache(fin);
+}
+
+       int
+eoflex()
+/*
+ * Yield true if we look ahead to the end of the input, false otherwise.
+ * nextc becomes undefined at end of file.
+ */
+{
+       register int c;
+       declarecache;
+       register FILE *fout;
+       register RILE *fin;
+
+       c = nextc;
+       fin = finptr;
+       fout = foutptr;
+       setupcache(fin); cache(fin);
+
+       for (;;) {
+               switch (ctab[c]) {
+                       default:
+                               nextc = c;
+                               uncache(fin);
+                               return false;
+
+                       case NEWLN:
+                               ++rcsline;
+                               /* fall into */
+                       case SPACE:
+                               cachegeteof(c, {uncache(fin);return true;});
+                               break;
+               }
+               if (fout)
+                       aputc(c, fout);
+       }
+}
+
+
+int getlex(token)
+enum tokens token;
+/* Function: Checks if nexttok is the same as token. If so,
+ * advances the input by calling nextlex and returns true.
+ * otherwise returns false.
+ * Doesn't work for strings and keywords; loses the character string for ids.
+ */
+{
+        if (nexttok==token) {
+                nextlex();
+                return(true);
+        } else  return(false);
+}
+
+       int
+getkeyopt(key)
+       char const *key;
+/* Function: If the current token is a keyword identical to key,
+ * advances the input by calling nextlex and returns true;
+ * otherwise returns false.
+ */
+{
+       if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
+                /* match found */
+                ffree1(NextString);
+                nextlex();
+                return(true);
+        }
+        return(false);
+}
+
+       void
+getkey(key)
+       char const *key;
+/* Check that the current input token is a keyword identical to key,
+ * and advance the input by calling nextlex.
+ */
+{
+       if (!getkeyopt(key))
+               fatserror("missing '%s' keyword", key);
+}
+
+       void
+getkeystring(key)
+       char const *key;
+/* Check that the current input token is a keyword identical to key,
+ * and advance the input by calling nextlex; then look ahead for a string.
+ */
+{
+       getkey(key);
+       if (nexttok != STRING)
+               fatserror("missing string after '%s' keyword", key);
+}
+
+
+       char const *
+getid()
+/* Function: Checks if nexttok is an identifier. If so,
+ * advances the input by calling nextlex and returns a pointer
+ * to the identifier; otherwise returns nil.
+ * Treats keywords as identifiers.
+ */
+{
+       register char const *name;
+        if (nexttok==ID) {
+                name = NextString;
+                nextlex();
+                return name;
+        } else  return nil;
+}
+
+
+struct hshentry * getnum()
+/* Function: Checks if nexttok is a number. If so,
+ * advances the input by calling nextlex and returns a pointer
+ * to the hashtable entry. Otherwise returns nil.
+ * Doesn't work if hshenter is false.
+ */
+{
+        register struct hshentry * num;
+        if (nexttok==NUM) {
+                num=nexthsh;
+                nextlex();
+                return num;
+        } else  return nil;
+}
+
+       struct cbuf
+getphrases(key)
+       char const *key;
+/* Get a series of phrases that do not start with KEY, yield resulting buffer.
+ * Stop when the next phrase starts with a token that is not an identifier,
+ * or is KEY.
+ * Assume !foutptr.
+ */
+{
+    declarecache;
+    register int c;
+    register char *p;
+    char const *limit;
+    register char const *ki, *kn;
+    struct cbuf r;
+    struct buf b;
+    register RILE *fin;
+
+    if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
+       r.string = 0;
+       r.size = 0;
+       return r;
+    } else {
+       warnignore();
+       fin = finptr;
+       setupcache(fin); cache(fin);
+       bufautobegin(&b);
+       bufscpy(&b, NextString);
+       ffree1(NextString);
+       p = b.string + strlen(b.string);
+       limit = b.string + b.size;
+       c = nextc;
+       for (;;) {
+           for (;;) {
+               if (limit <= p)
+                   p = bufenlarge(&b, &limit);
+               *p++ = c;
+               switch (ctab[c]) {
+                   default:
+                       fatserror("unknown character `%c'", c);
+                       /*NOTREACHED*/
+                   case NEWLN:
+                       ++rcsline;
+                       /* fall into */
+                   case COLON: case DIGIT: case LETTER: case Letter:
+                   case PERIOD: case SPACE:
+                       cacheget(c);
+                       continue;
+                   case SBEGIN: /* long string */
+                       for (;;) {
+                           for (;;) {
+                               if (limit <= p)
+                                   p = bufenlarge(&b, &limit);
+                               cacheget(c);
+                               *p++ = c;
+                               switch (c) {
+                                   case '\n':
+                                       ++rcsline;
+                                       /* fall into */
+                                   default:
+                                       continue;
+
+                                   case SDELIM:
+                                       break;
+                               }
+                               break;
+                           }
+                           cacheget(c);
+                           if (c != SDELIM)
+                               break;
+                           if (limit <= p)
+                               p = bufenlarge(&b, &limit);
+                           *p++ = c;
+                       }
+                       continue;
+                   case SEMI:
+                       cacheget(c);
+                       if (ctab[c] == NEWLN) {
+                           ++rcsline;
+                           if (limit <= p)
+                               p = bufenlarge(&b, &limit);
+                           *p++ = c;
+                           cacheget(c);
+                       }
+                       for (;;) {
+                           switch (ctab[c]) {
+                               case NEWLN:
+                                       ++rcsline;
+                                       /* fall into */
+                               case SPACE:
+                                       cacheget(c);
+                                       continue;
+
+                               default: break;
+                           }
+                           break;
+                       }
+                       break;
+               }
+               break;
+           }
+           switch (ctab[c]) {
+               case LETTER:
+               case Letter:
+                   for (kn = key;  c && *kn==c;  kn++)
+                       cacheget(c);
+                   if (!*kn)
+                       switch (ctab[c]) {
+                           case DIGIT: case LETTER: case Letter:
+                               break;
+                           default:
+                               nextc = c;
+                               NextString = fstr_save(key);
+                               nexttok = ID;
+                               uncache(fin);
+                               goto returnit;
+                       }
+                   for (ki=key; ki<kn; ) {
+                       if (limit <= p)
+                           p = bufenlarge(&b, &limit);
+                       *p++ = *ki++;
+                   }
+                   break;
+
+               default:
+                   nextc = c;
+                   uncache(fin);
+                   nextlex();
+                   goto returnit;
+           }
+       }
+    returnit:
+       return bufremember(&b, (size_t)(p - b.string));
+    }
+}
+
+
+       void
+readstring()
+/* skip over characters until terminating single SDELIM        */
+/* If foutptr is set, copy every character read to foutptr.    */
+/* Does not advance nextlex at the end.                        */
+{       register c;
+       declarecache;
+       register FILE *frew;
+       register RILE *fin;
+       fin=finptr; frew=foutptr;
+       setupcache(fin); cache(fin);
+       for (;;) {
+               GETC(frew, c);
+               switch (c) {
+                   case '\n':
+                       ++rcsline;
+                       break;
+
+                   case SDELIM:
+                       GETC(frew, c);
+                       if (c != SDELIM) {
+                               /* end of string */
+                               nextc = c;
+                               uncache(fin);
+                               return;
+                       }
+                       break;
+               }
+       }
+}
+
+
+       void
+printstring()
+/* Function: copy a string to stdout, until terminated with a single SDELIM.
+ * Does not advance nextlex at the end.
+ */
+{
+        register c;
+       declarecache;
+       register FILE *fout;
+       register RILE *fin;
+       fin=finptr;
+       fout = stdout;
+       setupcache(fin); cache(fin);
+       for (;;) {
+               cacheget(c);
+               switch (c) {
+                   case '\n':
+                       ++rcsline;
+                       break;
+                   case SDELIM:
+                       cacheget(c);
+                       if (c != SDELIM) {
+                                nextc=c;
+                               uncache(fin);
+                                return;
+                        }
+                       break;
+                }
+               aputc(c,fout);
+        }
+}
+
+
+
+       struct cbuf
+savestring(target)
+       struct buf *target;
+/* Copies a string terminated with SDELIM from file finptr to buffer target.
+ * Double SDELIM is replaced with SDELIM.
+ * If foutptr is set, the string is also copied unchanged to foutptr.
+ * Does not advance nextlex at the end.
+ * Yield a copy of *TARGET, except with exact length.
+ */
+{
+        register c;
+       declarecache;
+       register FILE *frew;
+       register char *tp;
+       register RILE *fin;
+       char const *limit;
+       struct cbuf r;
+
+       fin=finptr; frew=foutptr;
+       setupcache(fin); cache(fin);
+       tp = target->string;  limit = tp + target->size;
+       for (;;) {
+               GETC(frew, c);
+               switch (c) {
+                   case '\n':
+                       ++rcsline;
+                       break;
+                   case SDELIM:
+                       GETC(frew, c);
+                       if (c != SDELIM) {
+                                /* end of string */
+                                nextc=c;
+                               r.string = target->string;
+                               r.size = tp - r.string;
+                               uncache(fin);
+                               return r;
+                        }
+                       break;
+                }
+               if (tp == limit)
+                       tp = bufenlarge(target, &limit);
+               *tp++ = c;
+        }
+}
+
+
+       char *
+checkid(id, delimiter)
+       register char *id;
+       int delimiter;
+/*   Function:  check whether the string starting at id is an   */
+/*             identifier and return a pointer to the delimiter*/
+/*             after the identifier.  White space, delim and 0 */
+/*              are legal delimiters.  Aborts the program if not*/
+/*              a legal identifier. Useful for checking commands*/
+/*             If !delim, the only delimiter is 0.             */
+{
+        register enum  tokens  d;
+        register char    *temp;
+        register char    c,tc;
+       register char delim = delimiter;
+
+       temp = id;
+       if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) {
+           while ((d = ctab[(unsigned char)(c = *++id)])==LETTER
+               || d==Letter || d==DIGIT || d==IDCHAR
+           )
+               ;
+           if (c  &&  (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) {
+                /* append \0 to end of id before error message */
+                tc = c;
+                while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
+                *id = '\0';
+               faterror("invalid character %c in identifier `%s'",tc,temp);
+           }
+        } else {
+            /* append \0 to end of id before error message */
+            while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
+            *id = '\0';
+           faterror("identifier `%s' doesn't start with letter", temp);
+        }
+       return id;
+}
+
+       void
+checksid(id)
+       char *id;
+/* Check whether the string ID is an identifier.  */
+{
+       VOID checkid(id, 0);
+}
+
+
+       static RILE *
+#if has_mmap && large_memory
+fd2_RILE(fd, filename, status)
+#else
+fd2RILE(fd, filename, mode, status)
+       char const *mode;
+#endif
+       int fd;
+       char const *filename;
+       register struct stat *status;
+{
+       struct stat st;
+
+       if (!status)
+               status = &st;
+       if (fstat(fd, status) != 0)
+               efaterror(filename);
+       if (!S_ISREG(status->st_mode)) {
+               error("`%s' is not a regular file", filename);
+               VOID close(fd);
+               errno = EINVAL;
+               return 0;
+       } else {
+
+#          if ! (has_mmap && large_memory)
+               FILE *stream;
+               if (!(stream = fdopen(fd, mode)))
+                       efaterror(filename);
+#          endif
+
+#          if !large_memory
+               return stream;
+#          else
+#              define RILES 3
+               {
+                       static RILE rilebuf[RILES];
+
+                       register RILE *f;
+                       size_t s = status->st_size;
+
+                       if (s != status->st_size)
+                               faterror("`%s' is enormous", filename);
+                       for (f = rilebuf;  f->base;  f++)
+                               if (f == rilebuf+RILES)
+                                       faterror("too many RILEs");
+                       if (!s) {
+                               static unsigned char dummy;
+                               f->base = &dummy;
+                       } else {
+#                          if has_mmap
+                               if (
+                                   (f->base = (unsigned char *)mmap(
+                                       (caddr_t)0, s, PROT_READ, MAP_SHARED,
+                                       fd, (off_t)0
+                                   )) == (unsigned char *)-1
+                               )
+                                       efaterror("mmap");
+#                          else
+                               f->base = tnalloc(unsigned char, s);
+#                          endif
+                       }
+                       f->ptr = f->base;
+                       f->lim = f->base + s;
+#                      if has_mmap
+                           f->fd = fd;
+#                      else
+                           f->readlim = f->base;
+                           f->stream = stream;
+#                      endif
+                       if_advise_access(s, f, MADV_SEQUENTIAL);
+                       return f;
+               }
+#          endif
+       }
+}
+
+#if !has_mmap && large_memory
+       int
+Igetmore(f)
+       register RILE *f;
+{
+       register fread_type r;
+       register size_t s = f->lim - f->readlim;
+
+       if (BUFSIZ < s)
+               s = BUFSIZ;
+       if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
+               testIerror(f->stream);
+               f->lim = f->readlim;  /* The file might have shrunk!  */
+               return 0;
+       }
+       f->readlim += r;
+       return 1;
+}
+#endif
+
+#if has_madvise && has_mmap && large_memory
+       void
+advise_access(f, advice)
+       register RILE *f;
+       int advice;
+{
+       if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0)
+               efaterror("madvise");
+}
+#endif
+
+       RILE *
+#if has_mmap && large_memory
+I_open(filename, status)
+#else
+Iopen(filename, mode, status)
+       char const *mode;
+#endif
+       char const *filename;
+       struct stat *status;
+/* Open FILENAME for reading, yield its descriptor, and set *STATUS.  */
+{
+       int fd;
+
+       if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0)
+               return 0;
+#      if has_mmap && large_memory
+               return fd2_RILE(fd, filename, status);
+#      else
+               return fd2RILE(fd, filename, mode, status);
+#      endif
+}
+
+
+#if !large_memory
+#      define Iclose(f) fclose(f)
+#else
+               static int
+       Iclose(f)
+               register RILE *f;
+       {
+#          if has_mmap
+               size_t s = f->lim - f->base;
+               if (s  &&  munmap((caddr_t)f->base, s) != 0)
+                       return -1;
+               f->base = 0;
+               return close(f->fd);
+#          else
+               tfree(f->base);
+               f->base = 0;
+               return fclose(f->stream);
+#          endif
+       }
+#endif
+
+
+static int Oerrloop;
+
+       exiting void
+Oerror()
+{
+       if (Oerrloop)
+               exiterr();
+       Oerrloop = true;
+       efaterror("output error");
+}
+
+exiting void Ieof() { fatserror("unexpected end of file"); }
+exiting void Ierror() { efaterror("input error"); }
+void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
+void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
+
+void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
+void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
+void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
+void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
+
+#if !large_memory
+       void
+testIeof(f)
+       FILE *f;
+{
+       testIerror(f);
+       if (feof(f))
+               Ieof();
+}
+void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
+#endif
+
+void eflush()
+{
+       if (fflush(stderr) != 0  &&  !Oerrloop)
+               Oerror();
+}
+
+void oflush()
+{
+       if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
+               Oerror();
+}
+
+       static exiting void
+fatcleanup(already_newline)
+       int already_newline;
+{
+       VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
+       exiterr();
+}
+
+static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
+static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
+
+void eerror(s) char const *s; { enerror(errno,s); }
+
+       void
+enerror(e,s)
+       int e;
+       char const *s;
+{
+       errsay();
+       errno = e;
+       perror(s);
+       eflush();
+}
+
+exiting void efaterror(s) char const *s; { enfaterror(errno,s); }
+
+       exiting void
+enfaterror(e,s)
+       int e;
+       char const *s;
+{
+       fatsay();
+       errno = e;
+       perror(s);
+       fatcleanup(true);
+}
+
+#if has_prototypes
+       void
+error(char const *format,...)
+#else
+       /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
+#endif
+/* non-fatal error */
+{
+       va_list args;
+       errsay();
+       vararg_start(args, format);
+       fvfprintf(stderr, format, args);
+       va_end(args);
+       afputc('\n',stderr);
+       eflush();
+}
+
+#if has_prototypes
+       exiting void
+fatserror(char const *format,...)
+#else
+       /*VARARGS1*/ exiting void
+       fatserror(format, va_alist) char const *format; va_dcl
+#endif
+/* fatal syntax error */
+{
+       va_list args;
+       oflush();
+       VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline);
+       vararg_start(args, format);
+       fvfprintf(stderr, format, args);
+       va_end(args);
+       fatcleanup(false);
+}
+
+#if has_prototypes
+       exiting void
+faterror(char const *format,...)
+#else
+       /*VARARGS1*/ exiting void faterror(format, va_alist)
+       char const *format; va_dcl
+#endif
+/* fatal error, terminates program after cleanup */
+{
+       va_list args;
+       fatsay();
+       vararg_start(args, format);
+       fvfprintf(stderr, format, args);
+       va_end(args);
+       fatcleanup(false);
+}
+
+#if has_prototypes
+       void
+warn(char const *format,...)
+#else
+       /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
+#endif
+/* prints a warning message */
+{
+       va_list args;
+       oflush();
+       aprintf(stderr,"%s warning: ",cmdid);
+       vararg_start(args, format);
+       fvfprintf(stderr, format, args);
+       va_end(args);
+       afputc('\n',stderr);
+       eflush();
+}
+
+       void
+redefined(c)
+       int c;
+{
+       warn("redefinition of -%c option", c);
+}
+
+#if has_prototypes
+       void
+diagnose(char const *format,...)
+#else
+       /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
+#endif
+/* prints a diagnostic message */
+/* Unlike the other routines, it does not append a newline. */
+/* This lets some callers suppress the newline, and is faster */
+/* in implementations that flush stderr just at the end of each printf. */
+{
+       va_list args;
+        if (!quietflag) {
+               oflush();
+               vararg_start(args, format);
+               fvfprintf(stderr, format, args);
+               va_end(args);
+               eflush();
+        }
+}
+
+
+
+       void
+afputc(c, f)
+/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower.
+ */
+       int c;
+       register FILE *f;
+{
+       aputc(c,f);
+}
+
+
+       void
+aputs(s, iop)
+       char const *s;
+       FILE *iop;
+/* Function: Put string s on file iop, abort on error.
+ */
+{
+#if has_fputs
+       if (fputs(s, iop) < 0)
+               Oerror();
+#else
+       awrite(s, strlen(s), iop);
+#endif
+}
+
+
+
+       void
+#if has_prototypes
+fvfprintf(FILE *stream, char const *format, va_list args)
+#else
+       fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
+#endif
+/* like vfprintf, except abort program on error */
+{
+#if has_vfprintf
+       if (vfprintf(stream, format, args) < 0)
+#else
+#      if has__doprintf
+               _doprintf(stream, format, args);
+#      else
+#      if has__doprnt
+               _doprnt(format, args, stream);
+#      else
+               int *a = (int *)args;
+               VOID fprintf(stream, format,
+                       a[0], a[1], a[2], a[3], a[4],
+                       a[5], a[6], a[7], a[8], a[9]
+               );
+#      endif
+#      endif
+       if (ferror(stream))
+#endif
+               Oerror();
+}
+
+#if has_prototypes
+       void
+aprintf(FILE *iop, char const *fmt, ...)
+#else
+       /*VARARGS2*/ void
+aprintf(iop, fmt, va_alist)
+FILE *iop;
+char const *fmt;
+va_dcl
+#endif
+/* Function: formatted output. Same as fprintf in stdio,
+ * but aborts program on error
+ */
+{
+       va_list ap;
+       vararg_start(ap, fmt);
+       fvfprintf(iop, fmt, ap);
+       va_end(ap);
+}
+
+
+
+#ifdef LEXDB
+/* test program reading a stream of lexemes and printing the tokens.
+ */
+
+
+
+       int
+main(argc,argv)
+int argc; char * argv[];
+{
+        cmdid="lextest";
+        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();
+       while (!eoflex()) {
+        switch (nexttok) {
+
+        case ID:
+                VOID printf("ID: %s",NextString);
+                break;
+
+        case NUM:
+               if (hshenter)
+                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
+                else
+                   VOID printf("NUM, unentered: %s",NextString);
+                hshenter = !hshenter; /*alternate between dates and numbers*/
+                break;
+
+        case COLON:
+                VOID printf("COLON"); break;
+
+        case SEMI:
+                VOID printf("SEMI"); break;
+
+        case STRING:
+                readstring();
+                VOID printf("STRING"); break;
+
+        case UNKN:
+                VOID printf("UNKN"); break;
+
+        default:
+                VOID printf("DEFAULT"); break;
+        }
+        VOID printf(" | ");
+        nextlex();
+        }
+       exitmain(EXIT_SUCCESS);
+}
+
+exiting void exiterr() { _exit(EXIT_FAILURE); }
+
+
+#endif
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsmap.c b/usr/src/contrib/rcs-V5.6/src/rcsmap.c
new file mode 100644 (file)
index 0000000..0e7b23c
--- /dev/null
@@ -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
+};
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsmerge.c b/usr/src/contrib/rcs-V5.6/src/rcsmerge.c
new file mode 100644 (file)
index 0000000..e5d4394
--- /dev/null
@@ -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);
+}
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsrev.c b/usr/src/contrib/rcs-V5.6/src/rcsrev.c
new file mode 100644 (file)
index 0000000..ce11f54
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcssyn.c b/usr/src/contrib/rcs-V5.6/src/rcssyn.c
new file mode 100644 (file)
index 0000000..31086c2
--- /dev/null
@@ -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
+
diff --git a/usr/src/contrib/rcs-V5.6/src/rcstest b/usr/src/contrib/rcs-V5.6/src/rcstest
new file mode 100644 (file)
index 0000000..e0b6c82
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rcsutil.c b/usr/src/contrib/rcs-V5.6/src/rcsutil.c
new file mode 100644 (file)
index 0000000..c523ccf
--- /dev/null
@@ -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
diff --git a/usr/src/contrib/rcs-V5.6/src/rlog.c b/usr/src/contrib/rcs-V5.6/src/rlog.c
new file mode 100644 (file)
index 0000000..b18b0c9
--- /dev/null
@@ -0,0 +1,1204 @@
+/*
+ *                       RLOG    operation
+ */
+/*****************************************************************************
+ *                       print contents of RCS files
+ *****************************************************************************
+ */
+
+/* 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: rlog.c,v $
+ * Revision 5.9  1991/09/17  19:07:40  eggert
+ * Getscript() didn't uncache partial lines.
+ *
+ * Revision 5.8  1991/08/19  03:13:55  eggert
+ * Revision separator is `:', not `-'.
+ * Check for missing and duplicate logs.  Tune.
+ * Permit log messages that do not end in newline (including empty logs).
+ *
+ * Revision 5.7  1991/04/21  11:58:31  eggert
+ * Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.6  1991/02/26  17:07:17  eggert
+ * Survive RCS files with missing logs.
+ * strsave -> str_save (DG/UX name clash)
+ *
+ * Revision 5.5  1990/11/01  05:03:55  eggert
+ * Permit arbitrary data in logs and comment leaders.
+ *
+ * Revision 5.4  1990/10/04  06:30:22  eggert
+ * Accumulate exit status across files.
+ *
+ * Revision 5.3  1990/09/11  02:41:16  eggert
+ * Plug memory leak.
+ *
+ * Revision 5.2  1990/09/04  08:02:33  eggert
+ * Count RCS lines better.
+ *
+ * Revision 5.0  1990/08/22  08:13:48  eggert
+ * Remove compile-time limits; use malloc instead.  Add setuid support.
+ * Switch to GMT.
+ * Report dates in long form, to warn about dates past 1999/12/31.
+ * Change "added/del" message to make room for the longer dates.
+ * Don't generate trailing white space.  Add -V.  Ansify and Posixate.
+ *
+ * Revision 4.7  89/05/01  15:13:48  narten
+ * changed copyright header to reflect current distribution rules
+ * 
+ * Revision 4.6  88/08/09  19:13:28  eggert
+ * Check for memory exhaustion; don't access freed storage.
+ * Shrink stdio code size; remove lint.
+ * 
+ * Revision 4.5  87/12/18  11:46:38  narten
+ * more lint cleanups (Guy Harris)
+ * 
+ * Revision 4.4  87/10/18  10:41:12  narten
+ * Updating version numbers
+ * Changes relative to 1.1 actually relative to 4.2
+ * 
+ * Revision 1.3  87/09/24  14:01:10  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.2  87/03/27  14:22:45  jenkins
+ * Port to suns
+ * 
+ * Revision 4.2  83/12/05  09:18:09  wft
+ * changed rewriteflag to external.
+ * 
+ * Revision 4.1  83/05/11  16:16:55  wft
+ * Added -b, updated getnumericrev() accordingly.
+ * Replaced getpwuid() with getcaller().
+ * 
+ * Revision 3.7  83/05/11  14:24:13  wft
+ * Added options -L and -R;
+ * Fixed selection bug with -l on multiple files.
+ * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
+ * 
+ * Revision 3.6  82/12/24  15:57:53  wft
+ * shortened output format.
+ *
+ * Revision 3.5  82/12/08  21:45:26  wft
+ * removed call to checkaccesslist(); used DATEFORM to format all dates;
+ * removed unused variables.
+ *
+ * Revision 3.4  82/12/04  13:26:25  wft
+ * Replaced getdelta() with gettree(); removed updating of field lockedby.
+ *
+ * Revision 3.3  82/12/03  14:08:20  wft
+ * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
+ * Fixed printing of nil, removed printing of Suffix,
+ * added shortcut if no revisions are printed, disambiguated struct members.
+ *
+ * Revision 3.2  82/10/18  21:09:06  wft
+ * call to curdir replaced with getfullRCSname(),
+ * fixed call to getlogin(), cosmetic changes on output,
+ * changed conflicting long identifiers.
+ *
+ * Revision 3.1  82/10/13  16:07:56  wft
+ * fixed type of variables receiving from getc() (char -> int).
+ */
+
+
+
+#include "rcsbase.h"
+
+struct  lockers {                     /* lockers in locker option; stored   */
+     char const                * login;      /* lockerlist                         */
+     struct     lockers * lockerlink;
+     }  ;
+
+struct  stateattri {                  /* states in state option; stored in  */
+     char const                * status;     /* statelist                          */
+     struct  stateattri * nextstate;
+     }  ;
+
+struct  authors {                     /* login names in author option;      */
+     char const                * login;      /* stored in authorlist               */
+     struct     authors * nextauthor;
+     }  ;
+
+struct Revpairs{                      /* revision or branch range in -r     */
+     unsigned            numfld;     /* option; stored in revlist          */
+     char const                * strtrev;
+     char const                * endrev;
+     struct  Revpairs   * rnext;
+     } ;
+
+struct Datepairs{                     /* date range in -d option; stored in */
+     char               strtdate[datesize];   /* duelst and datelist      */
+     char               enddate[datesize];
+     struct  Datepairs  * dnext;
+     };
+
+static char extractdelta P((struct hshentry const*));
+static int checkrevpair P((char const*,char const*));
+static struct hshentry const *readdeltalog P((void));
+static unsigned extdate P((struct hshentry*));
+static void cleanup P((void));
+static void exttree P((struct hshentry*));
+static void getauthor P((char*));
+static void getdatepair P((char*));
+static void getlocker P((char*));
+static void getnumericrev P((void));
+static void getrevpairs P((char*));
+static void getscript P((struct hshentry*));
+static void getstate P((char*));
+static void putabranch P((struct hshentry const*));
+static void putadelta P((struct hshentry const*,struct hshentry const*,int));
+static void putforest P((struct branchhead const*));
+static void putree P((struct hshentry const*));
+static void putrunk P((void));
+static void recentdate P((struct hshentry const*,struct Datepairs*));
+static void trunclocks P((void));
+
+static char const *insDelFormat;
+static int branchflag; /*set on -b */
+static int exitstatus;
+static int lockflag;
+static struct Datepairs *datelist, *duelst;
+static struct Revpairs *revlist, *Revlst;
+static struct authors *authorlist;
+static struct lockers *lockerlist;
+static struct stateattri *statelist;
+
+
+mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
+{
+       static char const cmdusage[] =
+               "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
+
+       register FILE *out;
+       char *a, **newargv;
+       struct Datepairs *currdate;
+       char const *accessListString, *accessFormat, *commentFormat;
+       char const *headFormat, *symbolFormat;
+       struct access const *curaccess;
+       struct assoc const *curassoc;
+       struct hshentry const *delta;
+       struct lock const *currlock;
+       int descflag, selectflag;
+       int onlylockflag;  /* print only files with locks */
+       int onlyRCSflag;  /* print only RCS file name */
+       unsigned revno;
+
+        descflag = selectflag = true;
+       onlylockflag = onlyRCSflag = false;
+       out = stdout;
+       suffixes = X_DEFAULT;
+
+       argc = getRCSINIT(argc, argv, &newargv);
+       argv = newargv;
+       while (a = *++argv,  0<--argc && *a++=='-') {
+               switch (*a++) {
+
+               case 'L':
+                       onlylockflag = true;
+                       break;
+
+               case 'R':
+                       onlyRCSflag =true;
+                       break;
+
+                case 'l':
+                        lockflag = true;
+                       getlocker(a);
+                        break;
+
+                case 'b':
+                        branchflag = true;
+                        break;
+
+                case 'r':
+                       getrevpairs(a);
+                        break;
+
+                case 'd':
+                       getdatepair(a);
+                        break;
+
+                case 's':
+                       getstate(a);
+                        break;
+
+                case 'w':
+                       getauthor(a);
+                        break;
+
+                case 'h':
+                       descflag = false;
+                        break;
+
+                case 't':
+                        selectflag = false;
+                        break;
+
+               case 'q':
+                       /* This has no effect; it's here for consistency.  */
+                       quietflag = true;
+                       break;
+
+               case 'x':
+                       suffixes = a;
+                       break;
+
+               case 'V':
+                       setRCSversion(*argv);
+                       break;
+
+                default:
+                       faterror("unknown option: %s%s", *argv, cmdusage);
+
+                };
+        } /* end of option processing */
+
+       if (argc<1) faterror("no input file%s", cmdusage);
+
+       if (! (descflag|selectflag)) {
+               warn("-t overrides -h.");
+               descflag = true;
+       }
+
+       if (RCSversion < VERSION(5)) {
+           accessListString = "\naccess list:   ";
+           accessFormat = "  %s";
+           commentFormat = "\ncomment leader:  \"";
+           headFormat = "\nRCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
+           insDelFormat = "  lines added/del: %lu/%lu";
+           symbolFormat = "  %s: %s;";
+       } else {
+           accessListString = "\naccess list:";
+           accessFormat = "\n\t%s";
+           commentFormat = "\ncomment leader: \"";
+           headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
+           insDelFormat = "  lines: +%lu -%lu";
+           symbolFormat = "\n\t%s: %s";
+       }
+
+        /* now handle all filenames */
+        do {
+           ffree();
+
+           if (pairfilenames(argc, argv, rcsreadopen, true, false)  <=  0)
+               continue;
+
+            /* now RCSfilename contains the name of the RCS file, and finptr
+             * the file descriptor. Workfilename contains the name of the
+             * working file.
+             */
+
+           /* Keep only those locks given by -l.  */
+           if (lockflag)
+               trunclocks();
+
+            /* do nothing if -L is given and there are no locks*/
+           if (onlylockflag && !Locks)
+               continue;
+
+           if ( onlyRCSflag ) {
+               aprintf(out, "%s\n", RCSfilename);
+               continue;
+           }
+            /*   print RCS filename , working filename and optional
+                 administrative information                         */
+            /* could use getfullRCSname() here, but that is very slow */
+           aprintf(out, headFormat, RCSfilename, workfilename,
+                   Head ? " " : "",  Head ? Head->num : "",
+                   Dbranch ? " " : "",  Dbranch ? Dbranch : "",
+                   StrictLocks ? " strict" : ""
+           );
+            currlock = Locks;
+            while( currlock ) {
+               aprintf(out, symbolFormat, currlock->login,
+                                currlock->delta->num);
+                currlock = currlock->nextlock;
+            }
+            if (StrictLocks && RCSversion<VERSION(5))
+               aputs("  strict", out);
+
+           aputs(accessListString, out);      /*  print access list  */
+            curaccess = AccessList;
+            while(curaccess) {
+               aprintf(out, accessFormat, curaccess->login);
+                curaccess = curaccess->nextaccess;
+            }
+
+           aputs("\nsymbolic names:", out);   /*  print symbolic names   */
+           for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
+               aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
+           aputs(commentFormat, out);
+           awrite(Comment.string, Comment.size, out);
+           aputs("\"\n", out);
+           if (VERSION(5)<=RCSversion  ||  Expand != KEYVAL_EXPAND)
+               aprintf(out, "keyword substitution: %s\n",
+                       expand_names[Expand]
+               );
+
+            gettree();
+
+           aprintf(out, "total revisions: %u", TotalDeltas);
+
+           revno = 0;
+
+           if (Head  &&  selectflag & descflag) {
+
+               getnumericrev();    /* get numeric revision or branch names */
+
+               exttree(Head);
+
+               /*  get most recently date of the dates pointed by duelst  */
+               currdate = duelst;
+               while( currdate) {
+                   VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0);
+                   recentdate(Head, currdate);
+                   currdate = currdate->dnext;
+               }
+
+               revno = extdate(Head);
+
+               aprintf(out, ";\tselected revisions: %u", revno);
+           }
+
+           afputc('\n',out);
+           if (descflag) {
+               aputs("description:\n", out);
+               getdesc(true);
+           }
+           if (revno) {
+               while (! (delta = readdeltalog())->selector  ||  --revno)
+                   ;
+               if (delta->next && countnumflds(delta->num)==2)
+                   /* Read through delta->next to get its insertlns.  */
+                   while (readdeltalog() != delta->next)
+                       ;
+               putrunk();
+               putree(Head);
+           }
+           aputs("=============================================================================\n",out);
+       } while (cleanup(),
+                ++argv, --argc >= 1);
+       Ofclose(out);
+       exitmain(exitstatus);
+}
+
+       static void
+cleanup()
+{
+       if (nerror) exitstatus = EXIT_FAILURE;
+       Izclose(&finptr);
+}
+
+#if lint
+#      define exiterr rlogExit
+#endif
+       exiting void
+exiterr()
+{
+       _exit(EXIT_FAILURE);
+}
+
+
+
+       static void
+putrunk()
+/*  function:  print revisions chosen, which are in trunk      */
+
+{
+       register struct hshentry const *ptr;
+
+       for (ptr = Head;  ptr;  ptr = ptr->next)
+               putadelta(ptr, ptr->next, true);
+}
+
+
+
+       static void
+putree(root)
+       struct hshentry const *root;
+/*   function: print delta tree (not including trunk) in reverse
+               order on each branch                                        */
+
+{
+        if ( root == nil ) return;
+
+        putree(root->next);
+
+        putforest(root->branches);
+}
+
+
+
+
+       static void
+putforest(branchroot)
+       struct branchhead const *branchroot;
+/*   function:  print branches that has the same direct ancestor    */
+{
+
+        if ( branchroot == nil ) return;
+
+        putforest(branchroot->nextbranch);
+
+        putabranch(branchroot->hsh);
+        putree(branchroot->hsh);
+}
+
+
+
+
+       static void
+putabranch(root)
+       struct hshentry const *root;
+/*   function  :  print one branch     */
+
+{
+
+        if ( root == nil) return;
+
+        putabranch(root->next);
+
+        putadelta(root, root, false);
+}
+
+
+
+
+
+       static void
+putadelta(node,editscript,trunk)
+       register struct hshentry const *node, *editscript;
+       int trunk;
+/*  function: Print delta node if node->selector is set.        */
+/*      editscript indicates where the editscript is stored     */
+/*      trunk indicated whether this node is in trunk           */
+{
+       static char emptych[] = EMPTYLOG;
+
+       register FILE *out;
+       char const *s;
+       size_t n;
+       struct branchhead const *newbranch;
+       struct buf branchnum;
+       char datebuf[datesize];
+
+       if (!node->selector)
+            return;
+
+       out = stdout;
+       aprintf(out,
+               "----------------------------\nrevision %s", node->num
+       );
+        if ( node->lockedby )
+          aprintf(out, "\tlocked by: %s;", node->lockedby);
+
+       aprintf(out, "\ndate: %s;  author: %s;  state: %s;",
+               date2str(node->date, datebuf),
+               node->author, node->state
+       );
+
+        if ( editscript )
+           if(trunk)
+             aprintf(out, insDelFormat,
+                             editscript->deletelns, editscript->insertlns);
+           else
+             aprintf(out, insDelFormat,
+                             editscript->insertlns, editscript->deletelns);
+
+        newbranch = node->branches;
+        if ( newbranch ) {
+          bufautobegin(&branchnum);
+          aputs("\nbranches:", out);
+           while( newbranch ) {
+               getbranchno(newbranch->hsh->num, &branchnum);
+               aprintf(out, "  %s;", branchnum.string);
+                newbranch = newbranch->nextbranch;
+           }
+          bufautoend(&branchnum);
+        }
+
+       afputc('\n', out);
+       s = node->log.string;
+       if (!(n = node->log.size)) {
+               s = emptych;
+               n = sizeof(emptych)-1;
+       }
+       awrite(s, n, out);
+       if (s[n-1] != '\n')
+               afputc('\n', out);
+}
+
+
+
+
+
+       static struct hshentry const *
+readdeltalog()
+/*  Function : get the log message and skip the text of a deltatext node.
+ *            Return the delta found.
+ *             Assumes the current lexeme is not yet in nexttok; does not
+ *             advance nexttok.
+ */
+{
+        register struct  hshentry  * Delta;
+       struct buf logbuf;
+       struct cbuf cb;
+
+       if (eoflex())
+               fatserror("missing delta log");
+        nextlex();
+       if (!(Delta = getnum()))
+               fatserror("delta number corrupted");
+       getkeystring(Klog);
+       if (Delta->log.string)
+               fatserror("duplicate delta log");
+       bufautobegin(&logbuf);
+       cb = savestring(&logbuf);
+       Delta->log = bufremember(&logbuf, cb.size);
+
+        nextlex();
+       while (nexttok==ID && strcmp(NextString,Ktext)!=0)
+               ignorephrase();
+       getkeystring(Ktext);
+        Delta->insertlns = Delta->deletelns = 0;
+        if ( Delta != Head)
+                getscript(Delta);
+        else
+                readstring();
+       return Delta;
+}
+
+
+       static void
+getscript(Delta)
+struct    hshentry   * Delta;
+/*   function:  read edit script of Delta and count how many lines added  */
+/*              and deleted in the script                                 */
+
+{
+        int ed;   /*  editor command  */
+       declarecache;
+       register RILE *fin;
+        register  int   c;
+       register unsigned long i;
+       struct diffcmd dc;
+
+       fin = finptr;
+       setupcache(fin);
+       initdiffcmd(&dc);
+       while (0  <=  (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
+           if (!ed)
+                 Delta->deletelns += dc.nlines;
+           else {
+                 /*  skip scripted lines  */
+                i = dc.nlines;
+                Delta->insertlns += i;
+                cache(fin);
+                do {
+                    for (;;) {
+                       cacheget(c);
+                       switch (c) {
+                           default:
+                               continue;
+                           case SDELIM:
+                               cacheget(c);
+                               if (c == SDELIM)
+                                   continue;
+                               if (--i)
+                                   fatserror("unexpected end to edit script");
+                               nextc = c;
+                               uncache(fin);
+                               return;
+                           case '\n':
+                               break;
+                       }
+                       break;
+                    }
+                    ++rcsline;
+                } while (--i);
+                uncache(fin);
+            }
+}
+
+
+
+
+
+
+
+       static void
+exttree(root)
+struct hshentry  *root;
+/*  function: select revisions , starting with root             */
+
+{
+       struct branchhead const *newbranch;
+
+        if (root == nil) return;
+
+       root->selector = extractdelta(root);
+       root->log.string = nil;
+        exttree(root->next);
+
+        newbranch = root->branches;
+        while( newbranch ) {
+            exttree(newbranch->hsh);
+            newbranch = newbranch->nextbranch;
+        }
+}
+
+
+
+
+       static void
+getlocker(argv)
+char    * argv;
+/*   function : get the login names of lockers from command line   */
+/*              and store in lockerlist.                           */
+
+{
+        register char c;
+        struct   lockers   * newlocker;
+        argv--;
+        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                 c == '\n' || c == ';')  ;
+        if (  c == '\0') {
+            lockerlist=nil;
+            return;
+        }
+
+        while( c != '\0' ) {
+           newlocker = talloc(struct lockers);
+            newlocker->lockerlink = lockerlist;
+            newlocker->login = argv;
+            lockerlist = newlocker;
+            while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' '
+                       && c != '\t' && c != '\n' && c != ';') ;
+            *argv = '\0';
+            if ( c == '\0' ) return;
+            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                     c == '\n' || c == ';')  ;
+        }
+}
+
+
+
+       static void
+getauthor(argv)
+char   *argv;
+/*   function:  get the author's name from command line   */
+/*              and store in authorlist                   */
+
+{
+        register    c;
+        struct     authors  * newauthor;
+
+        argv--;
+        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                 c == '\n' || c == ';')  ;
+        if ( c == '\0' ) {
+           authorlist = talloc(struct authors);
+           authorlist->login = getusername(false);
+            authorlist->nextauthor  = nil;
+            return;
+        }
+
+        while( c != '\0' ) {
+           newauthor = talloc(struct authors);
+            newauthor->nextauthor = authorlist;
+            newauthor->login = argv;
+            authorlist = newauthor;
+            while( ( c = *++argv) != ',' && c != '\0' && c != ' '
+                     && c != '\t' && c != '\n' && c != ';') ;
+            * argv = '\0';
+            if ( c == '\0') return;
+            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                     c == '\n' || c == ';')  ;
+        }
+}
+
+
+
+
+       static void
+getstate(argv)
+char   * argv;
+/*   function :  get the states of revisions from command line  */
+/*               and store in statelist                         */
+
+{
+        register  char  c;
+        struct    stateattri    *newstate;
+
+        argv--;
+        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                 c == '\n' || c == ';')  ;
+        if ( c == '\0'){
+           warn("missing state attributes after -s options");
+            return;
+        }
+
+        while( c != '\0' ) {
+           newstate = talloc(struct stateattri);
+            newstate->nextstate = statelist;
+            newstate->status = argv;
+            statelist = newstate;
+            while( (c = (*++argv)) != ',' && c != '\0' && c != ' '
+                    && c != '\t' && c != '\n' && c != ';')  ;
+            *argv = '\0';
+            if ( c == '\0' ) return;
+            while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                     c == '\n' || c == ';')  ;
+        }
+}
+
+
+
+       static void
+trunclocks()
+/*  Function:  Truncate the list of locks to those that are held by the  */
+/*             id's on lockerlist. Do not truncate if lockerlist empty.  */
+
+{
+       struct lockers const *plocker;
+        struct lock     * plocked,  * nextlocked;
+
+        if ( (lockerlist == nil) || (Locks == nil)) return;
+
+        /* shorten Locks to those contained in lockerlist */
+        plocked = Locks;
+        Locks = nil;
+        while( plocked != nil) {
+            plocker = lockerlist;
+            while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0))
+                plocker = plocker->lockerlink;
+            nextlocked = plocked->nextlock;
+            if ( plocker != nil) {
+                plocked->nextlock = Locks;
+                Locks = plocked;
+            }
+            plocked = nextlocked;
+        }
+}
+
+
+
+       static void
+recentdate(root, pd)
+       struct hshentry const *root;
+       struct Datepairs *pd;
+/*  function:  Finds the delta that is closest to the cutoff date given by   */
+/*             pd among the revisions selected by exttree.                   */
+/*             Successively narrows down the interval given by pd,           */
+/*             and sets the strtdate of pd to the date of the selected delta */
+{
+       struct branchhead const *newbranch;
+
+       if ( root == nil) return;
+       if (root->selector) {
+             if ( cmpnum(root->date, pd->strtdate) >= 0 &&
+                  cmpnum(root->date, pd->enddate) <= 0)
+               VOID strcpy(pd->strtdate, root->date);
+        }
+
+        recentdate(root->next, pd);
+        newbranch = root->branches;
+        while( newbranch) {
+           recentdate(newbranch->hsh, pd);
+           newbranch = newbranch->nextbranch;
+       }
+}
+
+
+
+
+
+
+       static unsigned
+extdate(root)
+struct  hshentry        * root;
+/*  function:  select revisions which are in the date range specified     */
+/*             in duelst  and datelist, start at root                     */
+/* Yield number of revisions selected, including those already selected.  */
+{
+       struct branchhead const *newbranch;
+       struct Datepairs const *pdate;
+       unsigned revno;
+
+       if (!root)
+           return 0;
+
+        if ( datelist || duelst) {
+            pdate = datelist;
+            while( pdate ) {
+                if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){
+                   if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0)
+                        break;
+                }
+                pdate = pdate->dnext;
+            }
+            if ( pdate == nil) {
+                pdate = duelst;
+               for (;;) {
+                  if (!pdate) {
+                       root->selector = false;
+                       break;
+                  }
+                   if ( cmpnum(root->date, pdate->strtdate) == 0)
+                      break;
+                   pdate = pdate->dnext;
+                }
+            }
+        }
+       revno = root->selector + extdate(root->next);
+
+        newbranch = root->branches;
+        while( newbranch ) {
+          revno += extdate(newbranch->hsh);
+           newbranch = newbranch->nextbranch;
+        }
+       return revno;
+}
+
+
+
+       static char
+extractdelta(pdelta)
+       struct hshentry const *pdelta;
+/*  function:  compare information of pdelta to the authorlist, lockerlist,*/
+/*             statelist, revlist and yield true if pdelta is selected.    */
+
+{
+       struct lock const *plock;
+       struct stateattri const *pstate;
+       struct authors const *pauthor;
+       struct Revpairs const *prevision;
+       unsigned length;
+
+       if ((pauthor = authorlist)) /* only certain authors wanted */
+           while (strcmp(pauthor->login, pdelta->author) != 0)
+               if (!(pauthor = pauthor->nextauthor))
+                   return false;
+       if ((pstate = statelist)) /* only certain states wanted */
+           while (strcmp(pstate->status, pdelta->state) != 0)
+               if (!(pstate = pstate->nextstate))
+                   return false;
+       if (lockflag) /* only locked revisions wanted */
+           for (plock = Locks;  ;  plock = plock->nextlock)
+               if (!plock)
+                   return false;
+               else if (plock->delta == pdelta)
+                   break;
+       if ((prevision = Revlst)) /* only certain revs or branches wanted */
+           for (;;) {
+                length = prevision->numfld;
+               if (
+                   countnumflds(pdelta->num) == length+(length&1) &&
+                   0 <= compartial(pdelta->num, prevision->strtrev, length) &&
+                   0 <= compartial(prevision->endrev, pdelta->num, length)
+               )
+                    break;
+               if (!(prevision = prevision->rnext))
+                   return false;
+            }
+       return true;
+}
+
+
+
+       static void
+getdatepair(argv)
+   char   * argv;
+/*  function:  get time range from command line and store in datelist if    */
+/*             a time range specified or in duelst if a time spot specified */
+
+{
+        register   char         c;
+        struct     Datepairs    * nextdate;
+       char const              * rawdate;
+       int                     switchflag;
+
+        argv--;
+        while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
+                 c == '\n' || c == ';')  ;
+        if ( c == '\0' ) {
+           warn("missing date/time after -d");
+            return;
+        }
+
+        while( c != '\0' )  {
+           switchflag = false;
+           nextdate = talloc(struct Datepairs);
+            if ( c == '<' ) {   /*   case: -d <date   */
+                c = *++argv;
+                (nextdate->strtdate)[0] = '\0';
+           } else if (c == '>') { /* case: -d'>date' */
+               c = *++argv;
+               (nextdate->enddate)[0] = '\0';
+               switchflag = true;
+           } else {
+                rawdate = argv;
+               while( c != '<' && c != '>' && c != ';' && c != '\0')
+                    c = *++argv;
+                *argv = '\0';
+               if ( c == '>' ) switchflag=true;
+               str2date(rawdate,
+                        switchflag ? nextdate->enddate : nextdate->strtdate);
+               if ( c == ';' || c == '\0') {  /*  case: -d date  */
+                   VOID strcpy(nextdate->enddate,nextdate->strtdate);
+                    nextdate->dnext = duelst;
+                    duelst = nextdate;
+                   goto end;
+               } else {
+                   /*   case:   -d date<  or -d  date>; see switchflag */
+                   while ( (c= *++argv) == ' ' || c=='\t' || c=='\n');
+                   if ( c == ';' || c == '\0') {
+                       /* second date missing */
+                       if (switchflag)
+                           *nextdate->strtdate= '\0';
+                       else
+                           *nextdate->enddate= '\0';
+                       nextdate->dnext = datelist;
+                       datelist = nextdate;
+                       goto end;
+                   }
+                }
+            }
+            rawdate = argv;
+           while( c != '>' && c != '<' && c != ';' && c != '\0')
+               c = *++argv;
+            *argv = '\0';
+           str2date(rawdate,
+                    switchflag ? nextdate->strtdate : nextdate->enddate);
+            nextdate->dnext = datelist;
+           datelist = nextdate;
+     end:
+           if ( c == '\0')  return;
+            while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n');
+        }
+}
+
+
+
+       static void
+getnumericrev()
+/*  function:  get the numeric name of revisions which stored in revlist  */
+/*             and then stored the numeric names in Revlst                */
+/*             if branchflag, also add default branch                     */
+
+{
+        struct  Revpairs        * ptr, *pt;
+       unsigned n;
+       struct buf s, e;
+       char const *lrev;
+       struct buf const *rstart, *rend;
+
+        Revlst = nil;
+        ptr = revlist;
+       bufautobegin(&s);
+       bufautobegin(&e);
+        while( ptr ) {
+           n = 0;
+           rstart = &s;
+           rend = &e;
+
+           switch (ptr->numfld) {
+
+             case 1: /* -r rev */
+               if (expandsym(ptr->strtrev, &s)) {
+                   rend = &s;
+                   n = countnumflds(s.string);
+                   if (!n  &&  (lrev = tiprev())) {
+                       bufscpy(&s, lrev);
+                       n = countnumflds(lrev);
+                   }
+                }
+               break;
+
+             case 2: /* -r rev- */
+               if (expandsym(ptr->strtrev, &s)) {
+                   bufscpy(&e, s.string);
+                   n = countnumflds(s.string);
+                   (n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
+                }
+               break;
+
+             case 3: /* -r -rev */
+               if (expandsym(ptr->endrev, &e)) {
+                   if ((n = countnumflds(e.string)) < 2)
+                       bufscpy(&s, ".1");
+                   else {
+                       bufscpy(&s, e.string);
+                       VOID strcpy(strrchr(s.string,'.'), ".1");
+                   }
+                }
+               break;
+
+             default: /* -r rev1-rev2 */
+               if (
+                       expandsym(ptr->strtrev, &s)
+                   &&  expandsym(ptr->endrev, &e)
+                   &&  checkrevpair(s.string, e.string)
+               ) {
+                   n = countnumflds(s.string);
+                   /* Swap if out of order.  */
+                   if (compartial(s.string,e.string,n) > 0) {
+                       rstart = &e;
+                       rend = &s;
+                   }
+               }
+               break;
+           }
+
+           if (n) {
+               pt = ftalloc(struct Revpairs);
+               pt->numfld = n;
+               pt->strtrev = fstr_save(rstart->string);
+               pt->endrev = fstr_save(rend->string);
+                pt->rnext = Revlst;
+                Revlst = pt;
+           }
+           ptr = ptr->rnext;
+        }
+        /* Now take care of branchflag */
+       if (branchflag && (Dbranch||Head)) {
+           pt = ftalloc(struct Revpairs);
+           pt->strtrev = pt->endrev =
+               Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
+           pt->rnext=Revlst; Revlst=pt;
+           pt->numfld = countnumflds(pt->strtrev);
+        }
+       bufautoend(&s);
+       bufautoend(&e);
+}
+
+
+
+       static int
+checkrevpair(num1,num2)
+       char const *num1, *num2;
+/*  function:  check whether num1, num2 are legal pair,i.e.
+    only the last field are different and have same number of
+    fields( if length <= 2, may be different if first field)   */
+
+{
+       unsigned length = countnumflds(num1);
+
+       if (
+                       countnumflds(num2) != length
+               ||      2 < length  &&  compartial(num1, num2, length-1) != 0
+       ) {
+           error("invalid branch or revision pair %s : %s", num1, num2);
+            return false;
+        }
+
+        return true;
+}
+
+
+
+       static void
+getrevpairs(argv)
+register     char    * argv;
+/*  function:  get revision or branch range from command line, and   */
+/*             store in revlist                                      */
+
+{
+        register    char    c;
+        struct      Revpairs  * nextrevpair;
+       int separator;
+
+       c = *argv;
+
+       /* Support old ambiguous '-' syntax; this will go away.  */
+       if (strchr(argv,':'))
+           separator = ':';
+       else {
+           if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
+               warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
+           separator = '-';
+       }
+
+       for (;;) {
+           while (c==' ' || c=='\t' || c=='\n')
+               c = *++argv;
+           nextrevpair = talloc(struct Revpairs);
+            nextrevpair->rnext = revlist;
+            revlist = nextrevpair;
+           nextrevpair->numfld = 1;
+           nextrevpair->strtrev = argv;
+           for (;;  c = *++argv) {
+               switch (c) {
+                   default:
+                       continue;
+                   case '\0': case ' ': case '\t': case '\n':
+                   case ',': case ';':
+                       break;
+                   case ':': case '-':
+                       if (c == separator)
+                           break;
+                       continue;
+               }
+               break;
+           }
+           *argv = '\0';
+           while (c==' ' || c=='\t' || c=='\n')
+               c = *++argv;
+           if (c == separator) {
+                while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ;
+               nextrevpair->endrev = argv;
+               for (;;  c = *++argv) {
+                   switch (c) {
+                       default:
+                           continue;
+                       case '\0': case ' ': case '\t': case '\n':
+                       case ',': case ';':
+                           break;
+                       case ':': case '-':
+                           if (c == separator)
+                               continue;
+                           break;
+                   }
+                   break;
+               }
+               *argv = '\0';
+               while (c==' ' || c=='\t' || c =='\n')
+                   c = *++argv;
+               nextrevpair->numfld =
+                   !nextrevpair->endrev[0] ? 2 /* -rrev- */ :
+                   !nextrevpair->strtrev[0] ? 3 /* -r-rev */ :
+                   4 /* -rrev1-rev2 */;
+            }
+           if (!c)
+               break;
+           if (c!=',' && c!=';')
+               error("missing `,' near `%c%s'", c, argv+1);
+       }
+}