diff options
65 files changed, 33456 insertions, 0 deletions
diff --git a/gnu/usr.bin/rcs/CREDITS b/gnu/usr.bin/rcs/CREDITS new file mode 100644 index 00000000000..590c3659dec --- /dev/null +++ b/gnu/usr.bin/rcs/CREDITS @@ -0,0 +1,24 @@ +RCS was designed and built by Walter F. Tichy of Purdue University. +RCS version 3 was released in 1983. + +Adam Hammer, Thomas Narten, and Daniel Trinkle of Purdue supported RCS through +version 4.3, released in 1990. Guy Harris of Sun contributed many porting +fixes. Paul Eggert of System Development Corporation contributed bug fixes +and tuneups. Jay Lepreau contributed 4.3BSD support. + +Paul Eggert of Twin Sun wrote the changes for RCS versions 5.5 and 5.6 (1991). +Rich Braun of Kronos and Andy Glew of Intel contributed ideas for new options. +Bill Hahn of Stratus contributed ideas for setuid support. +Ideas for piece tables came from Joe Berkovitz of Stratus and Walter F. Tichy. +Matt Cross of Stratus contributed test case ideas. +Adam Hammer of Purdue QAed. + +Paul Eggert wrote most of the changes for this version of RCS, +currently in beta test. K. Richard Pixley of Cygnus Support +contributed several bug fixes. Robert Lupton of Princeton +and Daniel Trinkle contributed ideas for $Name expansion. +Brendan Kehoe of Cygnus Support suggested rlog's -N option. +Paul D. Smith of Data General suggested improvements in option +and error processing. Adam Hammer of Purdue QAed. + +$Id: CREDITS,v 1.1 1996/08/12 04:07:29 millert Exp $ diff --git a/gnu/usr.bin/rcs/ChangeLog b/gnu/usr.bin/rcs/ChangeLog new file mode 100644 index 00000000000..691929ea8ec --- /dev/null +++ b/gnu/usr.bin/rcs/ChangeLog @@ -0,0 +1,196 @@ +Fri Jun 16 06:19:24 1995 Paul Eggert <eggert@twinsun.com> + + Version 5.7 released. + * INSTALL.RCS (TESTPREFIX): Remove. + * INSTALL.RCS, Makefile.in, NEWS: Update FSF address. + * Makefile.in (dist): Distribute REFS. + * NEWS: Add brief summary. + * README: Add REFS. Omit experimental distributions. + * REFS: Initial revision + * configure.in (DIFF_FAILURE, DIFF_SUCCESS): + Fix typo: values were interchanged. + +Mon Jun 5 08:31:43 1995 Paul Eggert <eggert@twinsun.com> + + * INSTALL.RCS: Warn about cross-compiling. + +Thu Jun 1 16:23:43 1995 Paul Eggert <eggert@twinsun.com> + + * NEWS: -kb is now implemented. diff3 -A is no longer the default. + Describe Mach style memory mapping, new installation procedure, + Posix 1003.1b-1993 compatibility, + and log prefix compatibility hack for C-style Logs. + Add possible changes for new option syntax, symbolic links, texinfo. + + * INSTALL.RCS: Renamed from src/INSTALL. + Building and installation now use autoconf and follow GNU standards. + Add --with-diffutils, PIC, X_DEFAULT. + Remove text_work_stdio. + Warn about HP-UX 8.07, HP-UX 9.*, and Solaris 2.4 mmap bugs. + Warn about GCC 2.5.8 Intel x86 -O bug. + Warn about NFS UDP checksum bug. + + * README: + Replace src/INSTALL with INSTALL (generic GNU) and INSTALL.RCS. + + * configure.in: Initial revision + + * Makefile.in: + Renamed from Makefile; autoconf now preprocesses this file. + Complete rewrite to follow GNU makefile standards. + + * rcs.ms: Use new log prefix scheme in example. + +Sun Mar 20 05:25:56 1994 Paul Eggert <eggert@twinsun.com> + + * Makefile.in: Distribute rcsfile.5in as well as rcsfile.5. + gzip -9. make clean before make dist. + +Thu Mar 17 14:05:48 1994 Paul Eggert <eggert@twinsun.com> + + * NEWS: Describe 5.6.7 news. + * INSTALL: GNU diff -> GNU diffutils. Recommend it. + +Tue Nov 9 17:40:15 1993 Paul Eggert <eggert@twinsun.com> + + * NEWS: Use ISO 8601 format. + +Wed Nov 3 17:54:40 1993 Paul Eggert <eggert@twinsun.com> + + * Makefile.in: Update for RCS 5.6.5. + * INSTALL, README: Update for RCS 5.6.5. + * CREDITS, NEWS: New files, taken from README. + +Sun Jan 17 18:17:11 1993 Paul Eggert <eggert@twinsun.com> + + * README: Add -z, rcsmerge -A, $Name, white space in file names. + This is a pre-5.6.5 snapshot, just before splitting into + README, INSTALL, NEWS. + +Tue Jul 28 16:12:45 1992 Paul Eggert <eggert@twinsun.com> + + * INSTALL: Add BINDIR. conf.error -> conf.err. + HP-UX 8.07 crashes mmap. Add setuid build advice. + * rcs.ms: Remove nonportable .PS, .PE. + * Makefile.in: Simplify and regularize. + * README: Update for 5.6.4: identifiers can contain `.' or + start with a digit, -V. ci -i -j. rcs -l now asks. rcsdiff + headers; -rN -rN optimization. Add projects: -z, sccstorcs, + rlog examples, etc. + +Mon Feb 17 23:02:05 1992 Paul Eggert <eggert@twinsun.com> + + * INSTALL: Recommend `tail -f a.h' for slowpokes. + * README: Describe changes in RCS 5.6.3. + +Fri Jan 24 18:44:19 1992 Paul Eggert <eggert@twinsun.com> + + * INSTALL: Add DESTRCSDIR. + * README: Add project to add an option to rcsclean to clean + directories recursively. + +Mon Jan 6 02:42:34 1992 Paul Eggert <eggert@twinsun.com> + + * README: 5.6 -> 5.6.1; add some suggestions + * INSTALL: Add comment about SunOS 4.0.3. + +Sun Nov 3 01:09:19 1991 Paul Eggert <eggert@twinsun.com> + + * README: Add brief intro. Add rcsck, compressed, DRCS suggestions. + +Mon Oct 7 17:32:46 1991 Paul Eggert <eggert@twinsun.com> + + * INSTALL: Explain rcsclean, large_memory. + * README: Fix typo; mention NFS problems. + +Tue Sep 24 00:28:38 1991 Paul Eggert <eggert@twinsun.com> + + * INSTALL, README: Put MS-DOS support into a separate distribution. + +Tue Sep 10 22:15:46 1991 Paul Eggert <eggert@twinsun.com> + + * INSTALL: Fix make directions. + * README: Upgrade notice for RCS 5.6 beta 5. + +Mon Aug 19 03:13:55 1991 Paul Eggert <eggert@twinsun.com> + + * README: Describe changes for DOS, -M, -r$, rcs -m, aborted + checkins, piece tables, and symlinks. + * INSTALL: Improve advice for DOS, and for testing before + installing. Describe bad_unlink_nfs, text_work_stdio, + TZ_must_be_set. + +Wed Jun 5 16:35:30 1991 Paul Eggert <eggert@twinsun.com> + + * COPYING: GPL version 2 + +Sun Apr 21 12:01:28 1991 Paul Eggert <eggert@twinsun.com> + + * README: RCS 5.6 (beta 3). + * INSTALL: New file. + +Thu Feb 28 19:18:44 1991 Paul Eggert <eggert@twinsun.com> + + * README: Add more ci advice. + +Mon Feb 25 07:12:29 1991 Paul Eggert <eggert@twinsun.com> + + * README: Introduce RCS 5.6. Remove setgid support. + Improve installation and testing instructions. + +Thu Jan 3 10:57:28 1991 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Merge fixes from RCS 4.3. + +Thu Dec 13 06:54:04 1990 Paul Eggert <eggert@twinsun.com> + + * README: Adjust to GNU diff 1.15. + +Thu Nov 1 05:03:19 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Bring back descriptions of rcsclean and rcsfreeze. + Add pointer to GNU MAKE. Fix troff glitches. + * README: Improve installation instructions. Add -I and new -t + behavior. Describe setid behavior on old hosts. Add some more + projects. + +Sat Sep 15 01:33:22 1990 Paul Eggert <eggert@twinsun.com> + + * README: rcstest -> src/rcstest + +Tue Sep 11 02:41:02 1990 Paul Eggert <eggert@twinsun.com> + + * README: Improve the description of changes. + +Tue Sep 4 08:02:07 1990 Paul Eggert <eggert@twinsun.com> + + * README: Don't parse two-digit years, because it won't work + after 1999/12/31. Lines consisting of a single `.' are now + handled correctly. + +Wed Aug 29 07:12:54 1990 Paul Eggert <eggert@twinsun.com> + + * README: Add -kkvl, *merge -q, rcstest. Fix typos. + +Wed Aug 22 08:08:09 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Fix troff glitches. + * README: Describe changes for RCS 5.0. + +Wed May 23 06:56:31 1990 Paul Eggert <eggert@twinsun.com> + + * README: Describe additions to RCS version 5. + +Fri Mar 30 01:40:33 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Fix some more minor troff glitches. + +Thu Mar 22 07:10:13 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Permit dates past 1999/12/31. + Fix some glitches in troff-RCS interactions. + +Thu Jan 11 16:32:50 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.ms: Use GMT, not local time, so people in different + timezones can collaborate. diff --git a/gnu/usr.bin/rcs/INSTALL b/gnu/usr.bin/rcs/INSTALL new file mode 100644 index 00000000000..95d84c820fb --- /dev/null +++ b/gnu/usr.bin/rcs/INSTALL @@ -0,0 +1,176 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/gnu/usr.bin/rcs/INSTALL.RCS b/gnu/usr.bin/rcs/INSTALL.RCS new file mode 100644 index 00000000000..aca5cbbaeff --- /dev/null +++ b/gnu/usr.bin/rcs/INSTALL.RCS @@ -0,0 +1,351 @@ +Installation instructions specific to RCS + + $Id: INSTALL.RCS,v 1.1 1996/08/12 04:07:31 millert Exp $ + + Copyright 1991, 1992, 1993, 1994, 1995 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, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +__________ + +This file contains installation instructions specific to RCS. +Please see the file INSTALL for generic installation instructions. + + +__________ + +Prerequisites and compatibility issues + +RCS requires a diff that supports the -n option. +Get GNU diffutils (version 2.7 or later) if your diff lacks -n. + +RCS works best with a diff that supports -a and -L, +and a diff3 that supports -A, -E and -m. +GNU diffutils supports these options. + +RCS version 5 reads RCS files written by any RCS version released since 1982. +It also writes RCS files that these older versions of RCS can read, +unless you use one of the following new features: + + checkin dates after 1999-12-31 + checking in non-text files + identifiers containing `.' or non-Ascii bytes, or starting with a digit + rcs -bX, where X is nonempty + rcs -kX, where X is not `kv' + RCS files that exceed hardcoded limits in older RCS versions + +A working file written by RCS 5.5 or later contains four-digit years in its +keyword strings. If you check out a working file with RCS 5.5 or later, +an older RCS version's `ci -k' may insist on two-digit years. +Similarly, a working file written with -zZONE contains times +in its keyword strings that older `ci -k's may not understand. +Work around this with `co -V4 -z', or edit the working file. + +RCS should run on any host that conforms to the Posix 1003.1-1990 standard. +It also runs on a wide variety of non-Posix hosts. + + +__________ + +Configuration and installation + +See INSTALL for general instructions on configuring and building RCS. +RCS's `configure' script has the option `--with-diffutils', +and is affected by environment variables; +see `Configuration environment' below. + +If your system type is in the following list, +look for the corresponding strings in the notes below before configuring. + + system type - identifiers + ------------------------ + AIX 3.2 - --prefix=/usr has_seteuid + GCC 2.5.8 Intel x86 - CFLAGS + HP/Apollo DomainOS - has_vfork + HP-UX 8.07 and 9.* - has_mmap + Solaris 2.4 - has_mmap + SCO Unix V.3.2 - has_rename + Ultrix - has_seteuid + +RCS configuration is a two-step process: +general configuration as described in INSTALL, +and the build of src/conf.h, which is done by `make'. +The second step runs several test programs, so if you are cross-compiling, +create a src/conf.h appropriate for the target host before invoking `make'. + +If making src/conf.h fails, look in src/conf.err to see what went wrong. +Check the resulting src/conf.h for plausibility, +e.g. by running `diff src/conf.heg src/conf.h'; see ``src/conf.h notes'' below. +If src/conf.h is wrong, and the mistake isn't listed in ``src/conf.h notes'', +there is a bug in src/conf.sh; please report it. +You can patch src/conf.h if you're in a hurry, but it's better to fix it; +look at src/a.h and src/conf.err for ideas. +If all else fails, copy src/conf.heg to src/conf.h and edit it by hand. + +If installation succeeds, make `installcheck'; +if this fails, make `installdebug' for detailed info. + +If you want to test RCS before installing it, +build it from scratch with `cd src; make RCSPREFIX= bindir=. installcheck'. +Be sure rebuild RCS without these options before actually 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. + + +---- + +Configuration note + +Do not configure with --prefix=/ or --prefix=/usr in AIX. +An AIX boot shell script (/etc/rc.boot4 in AIX 3.2) invokes `merge', +meaning /etc/merge, and fails if RCS merge is installed in /bin or /usr/bin. +IBM says that installing new programs into /usr/bin is a customer error (!). + + +__________ + +Configuration environment + +The configuration procedure normally inspects the current host +to determine how RCS is to be built. +The environment variables listed in this section override this default. +If you configure with the option `--with-diffutils', +unset environment variables whose names start with `DIFF' +are assumed to have values appropriate for a GNU diffutils +installed into the same location as RCS; +this is a recommended configuration. + +CC is the name of your C compiler. + +CPPFLAGS are C preprocessor options. + +CFLAGS are C compiler options that do not affect correctness, +typically options that affect optimization or debugging. +Omit -O if your compiler's optimizer is not trustworthy (e.g. GCC 2.5.8 x86). +If your ancient BSD compiler has the -R option, -R can improve performance by +making all initialized data read-only (not just string literals); +modern compilers don't need this, since they have `const'. + +DIFF is the name of your diff program. +It's normally best to use GNU diffutils. +If DIFF is not an absolute pathname, setuid execution cannot be used, +and execution may be a bit slower. +If you change DIFF after starting a build, +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 instead use diffutils, or something like /usr/lib/rdiff. + +DIFFFLAGS 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 and diff3 understand the -L LABEL option +for labeling context diff output, 0 otherwise. +This option was introduced with GNU diffutils 2.1. + +DIFF_SUCCESS, DIFF_FAILURE, and 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 <stdlib.h>'s EXIT_SUCCESS and EXIT_FAILURE +but this doesn't work on some broken hosts. + +DIFF3 is the name of the diff3 program. +With GNU diffutils, this is simply its user-visible diff3 program. +But with traditional diff3 it is the name of the undocumented diff3 auxiliary, +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 -L 0 -L 1 -L 2 /dev/null /dev/null -' +should output `x'. + +ED is the name of the standard Unix line editor. +It is used only if DIFF3_BIN is 0. + +INSTALL is the command that installs commands, e.g. `../install-sh'. +INSTALL_DATA installs data, and +INSTALL_PROGRAM installs programs. + +PIC is the name of your pic program, configured to avoid extensions +so that a portable man page is generated. +This is typically GNU pic with the `-n' option, +or traditional pic with `-D'. +It is used only if you edit the documentation or make `maintainer-clean'. + +prefix and exec_prefix establish the binary installation directory; +they are affected by the --prefix and --exec-prefix option of `configure'. + +SENDMAIL is a comma-separated list of strings (using C syntax) +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. + + +__________ + +src/Makefile notes + +Many of the src/Makefile variables are set by `configure' as described above. +The notes below describe variables that may need to be edited by hand +in unusual installations. + +ALL_CFLAGS are all the options passed to the C compiler. + +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. + +DEFS are configuration options for the C preprocessor. +It should include any extra -D and -I options needed on your system. + +LDFLAGS are the loader flags you need, e.g. -i, -n, -s, -x. + +LIBOBJS are any other object files you need to link. + +LIBS 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. + +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. + +o is the filename extension your host uses for object files. +It includes the `.'. It is typically `.o' on Unix hosts. + +x is the filename extension your host uses for executables. +It includes any `.'. It is empty on Unix hosts, +which traditionally lack extensions on executables. + + +__________ + +src/conf.h notes + +See src/conf.sh for details about the definitions in src/conf.h. +Comments below cover unusual situations requiring hand patches to src/conf.h. + +bad_NFS_rename - Some buggy NFS file servers (e.g. some NAC releases) +can report that rename(A,B) succeeded even though it failed. +Set bad_NFS_rename to nonzero to work around the problem. +Warning: the workaround introduces a rare race condition +that can falsely report I/O errors; +this is why the workaround is disabled unless you specify otherwise. + +const - Some hosts support `const' but complain about it, perhaps because +system headers are wrong. If you can't stand the complaints, +try `#define const /*empty*/'. + +has_mmap - For speed, RCS uses the `mmap' system call +if it is available and the Mach `map_fd' system call is not. +Unfortunately, many mmap implementations are broken. +src/conf.sh guesses based on tests and on mmap bugs reported by RCS users; +you may want to double-check its results. +For instance, mmap does not work properly in HP-UX 8 or 9, or in Solaris 2.4, +without kernel patches; see src/conf.sh for details. +We don't know details about the bugs, so we can't test for them automatically. + +has_NFS - Set this if the target host might use NFS. +NFS's ``stateless server'' protocol has well-known problems with +the non-idempotent operations link(), rename(), and unlink(). +For example, unlink() can run twice on the NFS server, +causing the client to think that the unlink failed with errno==ENOENT. +has_NFS enables code that works around these problems. +However, has_NFS does not work around NFS implementation bugs; +if your NFS implementation is buggy, get another! +For example, make sure that your NFS uses UDP checksums, if it uses UDP. +Modern systems checksum by default; ask your vendor if you're not sure. + +has_rename - This should be 0 in SCO Unix V.3.2. Its NFS rename() is broken, +but if you run src/conf.sh in a non-NFS filesystem, it thinks rename() works. + +has_seteuid - You have to worry about this only if you plan to run RCS setuid. +has_seteuid 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. +On some older systems (e.g. SunOS 3.5) seteuid doesn't allow this. +One must be root to test this reliably, so src/conf.sh just guesses. +If has_seteuid==0, perhaps you can use setreuid instead (see below). +Otherwise, the next best thing is saved setuid semantics +(a Posix 1003.1-1990 option), because this fails only if you run as root. +You may need to compile with `cc -systype sysv' (some Mips OS variants) +or `cc -YPOSIX' (some Ultrix variants) for best results here. +Don't run RCS setuid under AIX 3.2 if you use NFS, since it's badly broken. +To see the AIX 3.2 bug, run the following program setuid X where the NFS file +"abc" already exists, owned by X, permission -rw-r--r--, and where +the invoker is not X. The program fails with "fclose: Permission denied". + #include <stdio.h> + int main() { + FILE *f = fopen("abc", "w"); + setuid(getuid()); + fputc('\n', f); + if (fclose(f) != 0) + perror("fclose"); + } + +has_setreuid - You have to worry about this only if you plan to run RCS setuid +and if has_seteuid==0. On some older BSDish systems, the setreuid system +call lets you swap real and effective users even if one of them is root. +One must be root to test this reliably, so src/conf.sh always guesses that +it doesn't work. Set it to 1 by hand if you know that it works. + +has_vfork - This should be 0 in some variants of HP/Apollo DomainOS. +(The `uname -a' command reported `10.3.5 sys5.3' on one such variant.) +Its vfork causes a system crash; you'll have to change src/conf.sh to output +`#define has_vfork 0' without actually trying vfork. + +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. + +_POSIX_SOURCE must be #defined in a strict Standard C environment, +because otherwise <stdio.h> cannot define useful identifiers 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. +You may also have to pay attention to other symbols, e.g. _XOPEN_SOURCE. + +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. + +X_DEFAULT - This is normally ",v/" on Unix hosts, and "" on hosts that +do not allow commas in file names (e.g. DOS). diff --git a/gnu/usr.bin/rcs/Makefile.bsd-wrapper b/gnu/usr.bin/rcs/Makefile.bsd-wrapper new file mode 100644 index 00000000000..d595ded83d8 --- /dev/null +++ b/gnu/usr.bin/rcs/Makefile.bsd-wrapper @@ -0,0 +1,42 @@ +# $OpenBSD: Makefile.bsd-wrapper,v 1.1 1996/08/12 04:07:32 millert Exp $ + +MAN= man/ci.1 man/co.1 man/ident.1 man/merge.1 man/rcs.1 man/rcsclean.1 \ + man/rcsdiff.1 man/rcsfile.5 man/rcsfreeze.1 man/rcsintro.1 \ + man/rcsmerge.1 man/rlog.1 +GNUCFLAGS= CFLAGS="${CFLAGS}" +CLEANFILES= man/rcsfile.5 src/conf.h + +all: config.status + ${MAKE} ${GNUCFLAGS} LDFLAGS=${LDSTATIC} + +.FORCE: .IGNORE + +config: .FORCE + -rm -f config.cache + sh ${.CURDIR}/configure --with-diffutils --prefix=/usr + +config.status: + sh ${.CURDIR}/configure --with-diffutils --prefix=/usr + +install: maninstall + ${MAKE} ${GNUCFLAGS} prefix=${DESTDIR}/usr \ + bindir=${DESTDIR}/usr/bin INSTALL_MAN= install + install ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/src/rcsfreeze.sh ${DESTDIR}/usr/bin/rcsfreeze + +clean cleandir: + -@if [ -e Makefile ]; then ${MAKE} distclean; fi + rm -f ${CLEANFILES} + +depend: + # Nothing here so far... + +lint: + # Nothing here so far... + +tags: + # Nothing here so far... + +.include <bsd.obj.mk> +.include <bsd.subdir.mk> +.include <bsd.man.mk> diff --git a/gnu/usr.bin/rcs/Makefile.in b/gnu/usr.bin/rcs/Makefile.in new file mode 100644 index 00000000000..cea8e861d52 --- /dev/null +++ b/gnu/usr.bin/rcs/Makefile.in @@ -0,0 +1,101 @@ +# Master makefile for RCS + +# $Id: Makefile.in,v 1.1 1996/08/12 04:07:33 millert Exp $ + +# Copyright 1995 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, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + +srcdir = @srcdir@ +VPATH = @srcdir@ + +@SET_MAKE@ +SHELL = /bin/sh + +# Documentation directories; special handling +INSTALL_MAN = man +# All other subdirs: +SUBDIRS = src $(INSTALL_MAN) + +FLAGS_TO_PASS = \ + CC='$(CC)' \ + CFLAGS='$(CFLAGS)' \ + LDFLAGS='$(LDFLAGS)' \ + LIBS='$(LIBS)' \ + MAKE='$(MAKE)' \ + bindir='$(bindir)' \ + mandir='$(mandir)' \ + prefix='$(prefix)' \ + exec_prefix='$(exec_prefix)' + +default :: all + +standard_GNU_targets = \ + all install uninstall clean distclean mostlyclean maintainer-clean \ + TAGS info dvi check + +maintainer-clean :: + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +$(standard_GNU_targets) installcheck installdebug :: + @for subdir in $(SUBDIRS); do \ + ( cd $$subdir && $(MAKE) $(FLAGS_TO_PASS) $@ ) || exit 1; \ + done + +dist :: $(srcdir)/configure + cd man && $(MAKE) rcsfile.5 + cd src && $(MAKE) TAGS + $(MAKE) distclean + set -x && \ + d=rcs-`sed -n <src/version.c \ + 's/.*version_string[^"]*"\([0-9.]*\).*/\1/p' \ + ` && \ + rm -fr $$d && \ + mkdir $$d $$d/man $$d/src && \ + ln *.ms ChangeLog configure configure.in COPYING CREDITS \ + INSTALL INSTALL.RCS install-sh \ + Makefile.in mkinstalldirs NEWS README REFS $$d && \ + (cd man && ln *.[0-9] *.[0-9]in \ + ChangeLog COPYING Makefile.in ../$$d/man) && \ + (cd src && ln *.[ch] *.heg *.sh ChangeLog COPYING \ + Makefile.in rcstest TAGS ../$$d/src) && \ + tar -cbf 1 - $$d | gzip -9 >$$d.tar.gz && \ + rm -fr $$d + +$(srcdir)/configure : configure.in + cd $(srcdir) && autoconf + +config.status : configure + ./config.status --recheck + +Makefile : Makefile.in config.status + ./config.status + +clean :: clean. +clean. :: + rm -f confdefs* conftest* core core.* *.core + +distclean maintainer-clean :: distclean. +distclean. :: clean. + rm -f Makefile config.cache config.log config.status diff --git a/gnu/usr.bin/rcs/NEWS b/gnu/usr.bin/rcs/NEWS new file mode 100644 index 00000000000..00c6e94b381 --- /dev/null +++ b/gnu/usr.bin/rcs/NEWS @@ -0,0 +1,548 @@ +Recent changes to RCS (and possible future changes) + + $Id: NEWS,v 1.1 1996/08/12 04:07:34 millert Exp $ + + Copyright 1991, 1992, 1993, 1994, 1995 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, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + +Here is a brief summary of user-visible changes since 5.6. + + New options: + `-kb' supports binary files. + `-T' preserves the modification time of RCS files. + `-V' prints the version number. + `-zLT' causes RCS to use local time in working files and logs. + `rcsclean -n' outputs what rcsclean would do, without actually doing it. + `rlog -N' omits symbolic names. + There is a new keyword `Name'. + Inserted log lines now have the same prefix as the preceding `$Log' line. + +Most changes for RCS version 5.7 are to fix bugs and improve portability. +RCS now conforms to GNU configuration standards and to Posix 1003.1b-1993. + + +Features new to RCS version 5.7, and possibly incompatible +in minor ways with previous practice, include: + + Inserted log lines now have the same prefix as the preceding `$Log' line. + E.g. if a $Log line starts with `// $Log', log lines are prefixed with `// '. + RCS still records the (now obsolescent) comment leader inside RCS files, + but it ignores the comment leader unless it is emulating older RCS versions. + If you plan to access a file with both old and new versions of RCS, + make sure its comment leader matches its `$Log' line prefix. + For backwards compatibility with older versions of RCS, + if the log prefix is `/*' or `(*' surrounded by optional white space, + inserted log lines contain ` *' instead of `/*' or `(*'; + however, this usage is obsolescent and should not be relied on. + + $Log string `Revision' times now use the same format as other times. + + Log lines are now inserted even if -kk is specified; this simplifies merging. + + ci's -rR option (with a nonempty R) now just specifies a revision number R. + In some beta versions, it also reestablished the default behavior of + releasing a lock and removing the working file. + Now, only the bare -r option does this. + + With an empty extension, any appearance of a directory named `RCS' + in a pathname identifies the pathname as being that of an RCS file. + For example, `a/RCS/b/c' is now an RCS file with an empty extension. + Formerly, `RCS' had to be the last directory in the pathname. + + rlog's -d option by default now uses exclusive time ranges. + E.g. `rlog -d"<T"' now excludes revisions whose times equal T exactly. + Use `rlog -d"<=T"' to get the old behavior. + + merge now takes up to three -L options, one for each input file. + Formerly, it took at most two -L options, for the 1st and 3rd input files. + + `rcs' now requires at least one option; this is for future expansion. + +Other features new to RCS version 5.7 include: + + merge and rcsmerge now pass -A, -E, and -e options to the subsidiary diff3. + + rcs -kb acts like rcs -ko, except it uses binary I/O on working files. + This makes no difference under Posix or Unix, but it does matter elsewhere. + With -kb in effect, rcsmerge refuses to merge; + this avoids common problems with CVS merging. + + The following is for future use by GNU Emacs 19's version control package: + + rcs's new -M option causes it to not send mail when you break somebody + else's lock. This is not meant for casual use; see rcs(1). + + ci's new -i option causes an error if the RCS file already exists. + Similarly, -j causes an error if the RCS file does not already exist. + + The new keyword `Name' is supported; its value is the name, if any, + used to check out the revision. E.g. `co -rN foo' causes foo's + $Name...$ keyword strings to end in `: N $'. + + The new -zZONE option causes RCS to output dates and times using ISO 8601 + format with ZONE as the time zone, and to use ZONE as the default time + zone for input. Its most common use is the -zLT option, which causes RCS + to use local time externally. You can also specify foreign time zones; + e.g. -z+05:30 causes RCS to use India time (5 hours 30 minutes east of UTC). + This option does not affect RCS files themselves, which always use UTC; + it affects only output (e.g. rlog output, keyword expansion, diff -c times) + and interpretation of options (e.g. the -d option of ci, co, and rlog). + Bare -z restores the default behavior of UTC with no time zone indication, + and the traditional RCS date separator `/' instead of the ISO 8601 `-'. + RCSINIT may contain a -z option. ci -k parses UTC offsets. + + The new -T option of ci, co, rcs, and rcsclean preserves the modification + time of the RCS file unless a revision is added or removed. + ci -T sets the RCS file's modification time to the new revision's time + if the former precedes the latter and there is a new revision; + otherwise, it preserves the RCS file's modification time. + Use this option with care, as it can confuse `make'; see ci(1). + + The new -N option of rlog omits symbolic names from the output. + + A revision number that starts with `.' is considered to be relative to + the default branch (normally the trunk). A branch number followed by `.' + stands for the last revision on that branch. + + If someone else already holds the lock, rcs -l now asks whether you want + to break it, instead of immediately reporting an error. + + ci now always unlocks a revision like 3.5 if you check in a revision + like 3.5.2.1 that is the first of a new branch of that revision. + Formerly it was inconsistent. + + File names may now contain tab, newline, space, and '$'. + They are represented in keyword strings with \t, \n, \040, and \044. + \ in a file name is now represented by \\ in a keyword string. + + Identifiers may now start with a digit and (unless they are symbolic names) + may contain `.'. This permits author names like `john.doe' and `4tran'. + + A bare -V option now prints the current version number. + + rcsdiff outputs more readable context diff headers if diff -L works. + + rcsdiff -rN -rN now suppresses needless checkout and comparison + of identical revisions. + + Error messages now contain the names of files to which they apply. + + Mach style memory mapping is now supported. + + The installation procedure now conforms to the GNU coding standards. + + When properly configured, RCS now strictly conforms to Posix 1003.1b-1993. + + +Features new to RCS version 5.6 include: + + Security holes have been plugged; setgid use is no longer supported. + + co can retrieve old revisions much more efficiently. + To generate the Nth youngest revision on the trunk, + the old method used up to N passes through copies of the working file; + the new method uses a piece table to generate the working file in one pass. + + When ci finds no changes in the working file, + it automatically reverts to the previous revision unless -f is given. + + RCS follows symbolic links to RCS files instead of breaking them, + and warns when it breaks hard links to RCS files. + + `$' stands for the revision number taken from working file keyword strings. + E.g. if F contains an Id keyword string, + `rcsdiff -r$ F' compares F to its checked-in revision, and + `rcs -nL:$ F' gives the symbolic name L to F's revision. + + co and ci's new -M option sets the modification time + of the working file to be that of the revision. + Without -M, ci now tries to avoid changing the working file's + modification time if its contents are unchanged. + + rcs's new -m option changes the log message of an old revision. + + RCS is portable to hosts that do not permit `,' in filenames. + (`,' is not part of the Posix portable filename character set.) + A new -x option specifies extensions other than `,v' for RCS files. + The Unix default is `-x,v/', so that the working file `w' corresponds + to the first file in the list `RCS/w,v', `w,v', `RCS/w' that works. + The non-Unix default is `-x', so that only `RCS/w' is tried. + Eventually, the Unix default should change to `-x/,v' + to encourage interoperability among all Posix hosts. + + A new RCSINIT environment variable specifies defaults for options like -x. + + The separator for revision ranges has been changed from `-' to `:', because + the range `A-B' is ambiguous if `A', `B' and `A-B' are all symbolic names. + E.g. the old `rlog -r1.5-1.7' is now `rlog -r1.5:1.7'; ditto for `rcs -o'. + For a while RCS will still support (but warn about) the old `-' separator. + + RCS manipulates its lock files using a method that is more reliable under NFS. + + +Features new to RCS version 5 include: + + RCS can check in arbitrary files, not just text files, if diff -a works. + RCS can merge lines containing just a single `.' if diff3 -m works. + GNU diff supports the -a and -m options. + + RCS can now be used as a setuid program. + See ci(1) for how users can employ setuid copies of ci, co, and rcsclean. + Setuid privileges yield extra security if the effective user owns RCS files + and directories, and if only the effective user can write RCS directories. + RCS uses the real user for all accesses other than writing RCS directories. + As described in ci(1), there are three levels of setuid support. + + 1. Setuid works fully if the seteuid() system call lets any + process switch back and forth between real and effective users, + as specified in Posix 1003.1a Draft 5. + + 2. On hosts with saved setuids (a Posix 1003.1-1990 option) and without + a modern seteuid(), setuid works unless the real or effective user is root. + + 3. On hosts that lack both modern seteuid() and saved setuids, + setuid does not work, and RCS uses the effective user for all accesses; + formerly it was inconsistent. + + New options to co, rcsdiff, and rcsmerge give more flexibility to keyword + substitution. + + -kkv substitutes the default `$Keyword: value $' for keyword strings. + However, a locker's name is inserted only as a file is being locked, + i.e. by `ci -l' and `co -l'. This is normally the default. + + -kkvl acts like -kkv, except that a locker's name is always inserted + if the given revision is currently locked. This was the default in + version 4. It is now the default only with when using rcsdiff to + compare a revision to a working file whose mode is that of a file + checked out for changes. + + -kk substitutes just `$Keyword$', which helps to ignore keyword values + when comparing revisions. + + -ko retrieves the old revision's keyword string, thus bypassing keyword + substitution. + + -kv retrieves just `value'. This can ease the use of keyword values, but + it is dangerous because it causes RCS to lose track of where the keywords + are, so for safety the owner write permission of the working file is + turned off when -kv is used; to edit the file later, check it out again + without -kv. + + rcs -ko sets the default keyword substitution to be in the style of co -ko, + and similarly for the other -k options. This can be useful with file + formats that cannot tolerate changing the lengths of keyword strings. + However it also renders a RCS file readable only by RCS version 5 or later. + Use rcs -kkv to restore the usual default substitution. + + RCS can now be used by development groups that span time zone boundaries. + All times are now displayed in UTC, and UTC is the default time zone. + To use local time with co -d, append ` LT' to the time. + When interchanging RCS files with sites running older versions of RCS, + time stamp discrepancies may prevent checkins; to work around this, + use `ci -d' with a time slightly in the future. + + Dates are now displayed using four-digit years, not two-digit years. + Years given in -d options must now have four digits. + This change is required for RCS to continue to work after 1999/12/31. + The form of dates in version 5 RCS files will not change until 2000/01/01, + so in the meantime RCS files can still be interchanged with sites + running older versions of RCS. To make room for the longer dates, + rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'. + + To help prevent diff programs that are broken or have run out of memory + from trashing an RCS file, ci now checks diff output more carefully. + + ci -k now handles the Log keyword, so that checking in a file + with -k does not normally alter the file's contents. + + RCS no longer outputs white space at the ends of lines + unless the original working file had it. + For consistency with other keywords, + a space, not a tab, is now output after `$Log:'. + Rlog now puts lockers and symbolic names on separate lines in the output + to avoid generating lines that are too long. + A similar fix has been made to lists in the RCS files themselves. + + RCS no longer outputs the string `Locker: ' when expanding Header or Id + keywords. This saves space and reverts back to version 3 behavior. + + The default branch is not put into the RCS file unless it is nonempty. + Therefore, files generated by RCS version 5 can be read by RCS version 3 + unless they use the default branch feature introduced in version 4. + This fixes a compatibility problem introduced by version 4. + + RCS can now emulate older versions of RCS; see `co -V'. + This may be useful to overcome compatibility problems + due to the above changes. + + Programs like Emacs can now interact with RCS commands via a pipe: + the new -I option causes ci, co, and rcs to run interactively, + even if standard input is not a terminal. + These commands now accept multiple inputs from stdin separated by `.' lines. + + ci now silently ignores the -t option if the RCS file already exists. + This simplifies some shell scripts and improves security in setuid sites. + + Descriptive text may be given directly in an argument of the form -t-string. + + The character set for symbolic names has been upgraded + from Ascii to ISO 8859. + + rcsdiff now passes through all options used by GNU diff; + this is a longer list than 4.3BSD diff. + + merge's new -L option gives tags for merge's overlap report lines. + This ability used to be present in a different, undocumented form; + the new form is chosen for compatibility with GNU diff3's -L option. + + rcsmerge and merge now have a -q option, just like their siblings do. + + rcsclean's new -n option outputs what rcsclean would do, + without actually doing it. + + RCS now attempts to ignore parts of an RCS file that look like they come + from a future version of RCS. + + When properly configured, RCS now strictly conforms with Posix 1003.1-1990. + RCS can still be compiled in non-Posix traditional Unix environments, + and can use common BSD and USG extensions to Posix. + RCS is a conforming Standard C program, and also compiles under traditional C. + + Arbitrary limits on internal table sizes have been removed. + The only limit now is the amount of memory available via malloc(). + + File temporaries, lock files, signals, and system call return codes + are now handled more cleanly, portably, and quickly. + Some race conditions have been removed. + + A new compile-time option RCSPREFIX lets administrators avoid absolute path + names for subsidiary programs, trading speed for flexibility. + + The configuration procedure is now more automatic. + + Snooping has been removed. + + +Version 4 was the first version distributed by FSF. +Beside bug fixes, features new to RCS version 4 include: + + The notion of default branch has been added; see rcs -b. + + +Version 3 was included in the 4.3BSD distribution. + + +Here are some possible future changes for RCS: + + Bring back sccstorcs. + + Add an option to `rcsmerge' so that it can use an arbitrary program + to do the 3-way merge, instead of the default `merge'. + Likewise for `rcsdiff' and `diff'. It should be possible to pass + arbitrary options to these programs, and to the subsidiary `co's. + + Add format options for finer control over the output of ident and rlog. + E.g. there should be an easy way for rlog to output lines like + `src/main.c 2.4 wft', one for each locked revision. + rlog options should have three orthogonal types: selecting files, + selecting revisions, and selecting rlog format. + + Add format options for finer control over the output of keyword strings. + E.g. there should be some way to prepend @(#), and there should be some + way to change $ to some other character to disable further substitution. + These options should make the resulting files uneditable, like -kv. + + Add long options, e.g. `--version'. Unfortunately RCS's option syntax + is incompatible with getopt. Perhaps the best way is to overload `rcs', e.g. + `rcs diff --keyword-substitution=old file' instead of `rcsdiff -ko file'. + + Add a way to put only the interesting part of the path into the $Header + keyword expansion. + + rlog -rM:N should work even if M and N have different numbers of fields, + so long as M is an ancestor of N or vice versa. + + rcs should evaluate options in order; this allows rcs -oS -nS. + + rcs should be able to fix minor mistakes in checkin dates and authors. + + Be able to redo your most recent checkin with minor changes. + + co -u shouldn't complain about a writable working file if it won't change + its contents. + + Configure the Makefile automatically, as well as conf.h. + + Add a new option to rcs that behaves like -o, but that doesn't lose the + nonempty log messages, but instead merges them with the next revision + if it exists, perhaps with a 1-line header containing author, date, etc. + + Add a `-' option to take the list of pathnames from standard input. + Perhaps the pathnames should be null-terminated, not newline-terminated, + so that pathnames that contain newlines are handled properly. + + Permit multiple option-pathname pairs, e.g. co -r1.4 a -r1.5 b. + + Add options to allow arbitrary combinations of working file names + with RCS file names -- they shouldn't have to match. + + Add an option to break a symbolic link to an RCS file, + instead of breaking the hard link that it points to. + + Add ways to specify the earliest revision, the most recent revision, + the earliest or latest revision on a particular branch, and + the parent or child of some other revision. + + If a user has multiple locks, perhaps ci should fall back on ci -k's + method to figure out which revision to use. + + Symbolic names need not refer to existing branches and revisions. + rcs(1)'s BUGS section says this is a bug. Is it? If so, it should be fixed. + + Add an option to rcs -o so that old log messages are not deleted if + the next undeleted revision exists, but are merely appended to the log + message of that revision. + + ci -k should be able to get keyword values from the first `$Log' entry. + + Add an option to rcsclean to clean directories recursively. + + Write an rcsck program that repairs corrupted RCS files, + much as fsck repairs corrupted file systems. + For example, it should remove stale lock files. + + Clean up the source code with a consistent indenting style. + + Update the date parser to use the more modern getdate.y by Bellovin, + Salz, and Berets, or the even more modern getdate by Moraes. None of + these getdate implementations are as robust as RCS's old warhorse in + avoiding problems like arithmetic overflow, so they'll have to be + fixed first. + + Break up the code into a library so that it's easier to write new programs + that manipulate RCS files, and so that useless code is removed from the + existing programs. For example, the rcs command contains unnecessary + keyword substitution baggage, and the merge command can be greatly pruned. + + Make it easier to use your favorite text editor to edit log messages, + etc. instead of having to type them in irretrievably at the terminal. + + Let the user specify a search path for default branches, + e.g. to use L as the default branch if it works, and M otherwise. + Let the user require that at least one entry in the default branch path works. + Let the user say that later entries in the default branch path are read only, + i.e. one cannot check in changes to them. + This should be an option settable by RCSINIT. + + Add a way for a user to see which revisions affected which lines. + + Have `rlog -nN F' print just the revision number that N translates to. + E.g. `rlog -nB. F' would print the highest revision on the branch B. + Use this to add an option -bB to rcsbranch, to freeze the named branch. + This should interact well with default branches. + + Add a co option that prints the revision number before each line, + as SCCS's `get -m' does. + +The following projects require a change to RCS file format. + + Allow keyword expansion to be changed on a per-revision basis, + not on a per-file basis as now. This would allow -ko to be used + on imported revisions, with the default -kkv otherwise. + + When two or more branches are merged, record all the ancestors + of the new revision. The hard part of this is keeping track of all + the ancestors of a working file while it's checked out. + + Add loose locking, which is like non-strict but applies to all users, + not just the owner of the RCS file. + + Be able to store RCS files in compressed format. + Don't bother to use a .Z extension that would exceed file name length limits; + just look at the magic number. + + Add locker commentary, e.g. `co -l -m"checkout to fix merge bug" foo' + to tell others why you checked out `foo'. + Also record the time when the revision was locked, + and perhaps the working pathname (if applicable). + + Let the user mark an RCS revision as deleted; checking out such a revision + would result in no working file. Similarly, using `co -d' with a date either + before the initial revision or after the file was marked deleted should + remove the working file. For extra credit, extend the notion of `deleted' to + include `renamed'. RCS should support arbitrary combinations of renaming and + deletion, e.g. renaming A to B and B to A, checking in new revisions to both + files, and then renaming them back. + + Be able to check in an entire directory structure into a single RCS file. + + Use a better scheme for locking revisions; the current scheme requires + changing the RCS file just to lock or unlock a revision. + The new scheme should coexist as well as possible with older versions of RCS, + and should avoid the rare NFS bugs mentioned in rcsedit.c. + E.g. if there's a reliable lockd running, RCS should use it + instead of relying on NFS. + + Add rcs options for changing keyword names, e.g. XConsortium instead of Id. + + Add a `$Description' keyword; but this may be tricky, since descriptions can + contain newlines and $s. + + Add a `$Copyright' keyword that expands to a copyright notice. + + Add frozen branches a la SCCS. In general, be able to emulate all of + SCCS, so that an SCCS-to-RCS program can be practical. For example, + there should be an equivalent to the SCCS prt command. + + Add support for distributed RCS, where widely separated + users cannot easily access each others' RCS files, + and must periodically distribute and reconcile new revisions. + + Be able to create empty branches. + + Be able to store just deltas from a read-only principal copy, + e.g. from source on CD-ROM. + + Improve RCS's method for storing binary files. + Although it is more efficient than SCCS's, + the diff algorithm is still line oriented, + and often generates long output for minor changes to an executable file. + + From the user's point of view, it would be best if + RCS detected and handled binary files without human intervention, + switching expansion methods as needed from revision to revision. + + Allow RCS to determine automagically whether -ko or -kb should be the default + by inspecting the file's contents or name. The magic should be optional + and user-programmable. + + Extend the grammar of RCS files so that keywords need not be in a fixed order. + + Internationalize messages; unfortunately, there's no common standard yet. + This requires a change in RCS file format because of the + `empty log message' and `checked in with -k' hacks inside RCS files. + + Add documentation in texinfo format. diff --git a/gnu/usr.bin/rcs/README b/gnu/usr.bin/rcs/README new file mode 100644 index 00000000000..d1fd820682e --- /dev/null +++ b/gnu/usr.bin/rcs/README @@ -0,0 +1,28 @@ +This directory contains sources and documentation for RCS. + +RCS, the Revision Control System, manages multiple revisions of files. +RCS can store, retrieve, log, identify, and merge revisions. +It is useful for files that are revised frequently, +e.g. programs, documentation, graphics, and papers. + +See the following files and directories for more information. + + COPYING - copying conditions + CREDITS - authorship information + INSTALL - generic installation instructions + INSTALL.RCS - installation instructions specific to RCS + NEWS - recent changes, and possible future changes + REFS - references to RCS and related free software and documentation + man - sources for manual page entries + rcs.ms - troff source for the paper `RCS--A System for Version Control' + rcs_func.ms - brief overview + src - sources for programs + src/version.c - the version number of this release + +If you lack troff, you can get GNU groff to format the documentation. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +$Id: README,v 1.1 1996/08/12 04:07:35 millert Exp $ diff --git a/gnu/usr.bin/rcs/REFS b/gnu/usr.bin/rcs/REFS new file mode 100644 index 00000000000..44b99bf4950 --- /dev/null +++ b/gnu/usr.bin/rcs/REFS @@ -0,0 +1,90 @@ +Here are references to RCS and related free software and documentation. +Some of this information changes often; see the Frequently Asked Questions +for more up-to-date references. + + $Id: REFS,v 1.1 1996/08/12 04:07:35 millert Exp $ + + +Frequently Asked Questions (FAQs) + +<http://www.qucis.queensu.ca/Software-Engineering/> +<ftp://rtfm.mit.edu//pub/usenet-by-hierarchy/comp/software-eng/> + for software engineering; e.g. see + <http://www.qucis.queensu.ca/Software-Engineering/blurb/rcs>. + +<http://www.iac.honeywell.com/Pub/Tech/CM/CMFAQ.html> +<ftp://rtfm.mit.edu//pub/usenet-by-hierarchy/comp/software/config-mgmt/> + for configuration management + +<http://www.winternet.com/~zoo/cvs/FAQ.txt> +<ftp://ftp.odi.com/pub/users/dgg/FAQ.gz> + for CVS (see below) + + +RCS and related GNU project software + +<ftp://ftp.cs.purdue.edu/pub/RCS/> + The RCS project distribution directory also contains beta versions, + ports, and prebuilt documentation. + +<ftp://prep.ai.mit.edu/pub/gnu/> + The GNU project distribution directory contains: + diffutils-N-tar.gz + the latest diffutils release; recommended for RCS + emacs-N-tar.gz + The latest Emacs release contains VC, a version-control package + that makes RCS easier to use. + make-N-tar.gz + GNU Make, which can automatically build from RCS files. + rcs-N-tar.gz + the latest RCS release + cvs-N-tar.gz + the latest official CVS release (see below) + +<ftp://ftp.leo.org/pub/comp/os/os2/gnu/devtools/> DOS, OS/2 ports +<ftp://ftp.cc.utexas.edu/microlib/nt/gnu/> NT port + + +CVS + +CVS, the Concurrent Versions System, keeps tracks of source changes +made by groups of developers working on the same files concurrently, +allowing them to resync as needed. + +<http://www.winternet.com/~zoo/cvs/> +<http://www.loria.fr/~molli/cvs-index.html> + These pages have useful information about CVS. + +<ftp://prep.ai.mit.edu/pub/gnu/cvs-1.3.tar.gz> + CVS 1.3 is the latest released version. + +<ftp://ftp.delos.com/pub/cvs/alpha/cvs-1.4A2.tar.gz> + CVS 1.4 is in alpha test, but it is recommended if you are installing CVS + for the first time, or on a recent operating system. + +<ftp://ftp-os2.cdrom.com/pub/os2/unix/> DOS, OS/2 ports +<ftp://ftp.cc.utexas.edu/microlib/nt/gnu/> NT port + +<ftp://ftp.cyclic.com/pub/cvs/> + Cyclic CVS adds network transparency to CVS; it supports efficient, + reliable, and authenticated repository access via TCP/IP. + + +Other software that uses RCS + +<ftp://ftp.nau.edu/pub/Aegis/> + Aegis manages revisions, baselines, mandatory reviews, and mandatory testing. + +<ftp://ftp.vix.com/pub/patches/csu/> + BCS, the Baseline Configuration System, + manages revisions, baselines, and staging areas. + +<ftp://riftp.osf.org/pub/ode/> + ODE, the Open Software Foundation Development Environment, + manages revisions, builds, and sandboxes. + OSF uses it for their own development. + +<ftp://bellcore.com/pub/Odin/> + Odin, a `make' replacement, can build directly from arbitrary revisions + without requiring checkouts of working copies. It also handles + parallel builds on multiple remote hosts and of multiple variants. diff --git a/gnu/usr.bin/rcs/configure b/gnu/usr.bin/rcs/configure new file mode 100644 index 00000000000..83d6e1ad628 --- /dev/null +++ b/gnu/usr.bin/rcs/configure @@ -0,0 +1,1221 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.4 +# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-diffutils assume GNU diffutils is similarly installed" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE + +# Initialize some other variables. +subdirs= + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -build | --build | --buil | --bui | --bu | --b) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=PREFIX install architecture-dependent files in PREFIX + [same as prefix] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +--enable and --with options recognized:$ac_help +EOF + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.4" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=src/rcsbase.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5' + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +# Set up simple `diff' test. +echo 0 >conftest0 +echo 0 >conftest0c +echo 1 >conftest1 +cat >conftestok <<'EOF' +d1 1 +a1 1 +1 +EOF + +# Check whether --with-diffutils or --without-diffutils was given. +withval="$with_diffutils" +if test -n "$withval"; then + with_diffutils=$withval +else + with_diffutils=no + +fi + + +case $with_diffutils in +yes) + : ${DIFF='$(bindir)/diff'} + : ${DIFF3=${DIFF}3} + : ${DIFF3_BIN=1} + : ${DIFFFLAGS=-an} + : ${DIFF_FAILURE=1} + : ${DIFF_L=1} + : ${DIFF_SUCCESS=0} + : ${DIFF_TROUBLE=2} +esac + +# Set DIFF to the name of the `diff' program to be run. +# On some systems, the RCS-compatible diff program is called `rdiff'; +# use it if it works and `diff' doesn't. + +echo $ac_n "checking diff basename""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_prog_diff'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_prog_diff=$DIFF + case $rcs_cv_prog_diff in + '') + for i in diff /usr/lib/rdiff rdiff + do + sh -c "exec $i -n conftest0 conftest1" >conftestout 2>/dev/null + case $? in + 1) + if cmp -s conftestok conftestout + then rcs_cv_prog_diff=$i; break + fi + ;; + esac + done + ;; + esac + +fi + +DIFF=$rcs_cv_prog_diff +case $DIFF in +'') { echo "configure: error: cannot find RCS-compatible diff" 1>&2; exit 1; };; +esac +echo "$ac_t""$DIFF" 1>&6 +# Extract the first word of "$DIFF", so it can be a program name with args. +set dummy $DIFF; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_DIFF'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$DIFF" in + /*) + ac_cv_path_DIFF="$DIFF" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_DIFF="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_DIFF" && ac_cv_path_DIFF="$DIFF" + ;; +esac +fi +DIFF="$ac_cv_path_DIFF" +if test -n "$DIFF"; then + echo "$ac_t""$DIFF" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +# Set DIFF_SUCCESS, DIFF_FAILURE, DIFF_TROUBLE to diff's exit status +# when it finds no differences, some differences, or trouble. + +echo $ac_n "checking diff success status""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_status_diff_success'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_status_diff_success=$DIFF_SUCCESS + case $rcs_cv_status_diff_success in + '') + # We can't use `$DIFF conftest0 conftest0', + # since buggy NEXTSTEP 3.0 diff silently yields exit status 2 for this. + $DIFF conftest0 conftest0c >/dev/null 2>&1 + rcs_cv_status_diff_success=$? + ;; + esac + +fi + +DIFF_SUCCESS=$rcs_cv_status_diff_success +echo "$ac_t""$DIFF_SUCCESS" 1>&6 +# + +echo $ac_n "checking diff failure status""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_status_diff_failure'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_status_diff_failure=$DIFF_FAILURE + case $rcs_cv_status_diff_failure in + '') + $DIFF conftest0 conftest1 >/dev/null 2>&1 + rcs_cv_status_diff_failure=$? + ;; + esac + +fi + +DIFF_FAILURE=$rcs_cv_status_diff_failure +echo "$ac_t""$DIFF_FAILURE" 1>&6 +# + +echo $ac_n "checking diff trouble status""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_status_diff_trouble'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_status_diff_trouble=$DIFF_TROUBLE + case $rcs_cv_status_diff_trouble in + '') + $DIFF conftest0 no/such/file >/dev/null 2>&1 + rcs_cv_status_diff_trouble=$? + ;; + esac + +fi + +DIFF_TROUBLE=$rcs_cv_status_diff_trouble +echo "$ac_t""$DIFF_TROUBLE" 1>&6 + +# Set DIFFFLAGS to the options of the `diff' program to be run. +# Use -an if possible, -n otherwise. + +echo $ac_n "checking diff options for RCS""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_options_diff'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_options_diff=$DIFFFLAGS + case $rcs_cv_options_diff in + '') + rcs_cv_options_diff=-n + $DIFF -an conftest0 conftest1 >conftestout 2>conftestout2 + case $? in + 1) + if cmp -s conftestok conftestout && test ! -s conftestout2 + then rcs_cv_options_diff=-an + fi + ;; + esac + ;; + esac + +fi + +DIFFFLAGS=$rcs_cv_options_diff +echo "$ac_t""$DIFFFLAGS" 1>&6 + +# Set DIFF_L to 1 if diff understands the L option, 0 otherwise. + +echo $ac_n "checking diff -L""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_options_diff_l'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_options_diff_l=$DIFF_L + case $rcs_cv_options_diff_l in + '') + rcs_cv_options_diff_l=0 + $DIFF -c -L 0 -L 1 conftest0 conftest1 >conftestout 2>/dev/null + case $? in + 1) + if cmp -s - conftestout <<'EOF' +*** 0 +--- 1 +*************** +*** 1 **** +! 0 +--- 1 ---- +! 1 +EOF + then rcs_cv_options_diff_l=1 + fi + ;; + esac + ;; + esac + +fi + +DIFF_L=$rcs_cv_options_diff_l +case $DIFF_L in +1) echo "$ac_t""yes" 1>&6;; +*) echo "$ac_t""no" 1>&6;; +esac + +# Set DIFF3 to the name of the diff3 program. +# In some systems (e.g. BSD/OS 2.0), diffutils diff3 lives in /usr/libexec. +diff3PATH=$PATH:/usr/libexec + +echo $ac_n "checking diff3 -m""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'rcs_cv_prog_diff3_bin'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + rcs_cv_prog_diff3_bin=$DIFF3 + case $rcs_cv_prog_diff3_bin in + '') + PATH=$diff3PATH sh -c "exec diff3 -E -m -L 0 -L 1 -L 2 conftest0 conftest1 /dev/null" >conftestout 2>/dev/null + case $? in + 1) + if cmp -s - conftestout <<'EOF' +<<<<<<< 0 +0 +======= +>>>>>>> 2 +EOF + then rcs_cv_prog_diff3_bin=diff3 + fi + ;; + esac + ;; + esac + +fi + +case $rcs_cv_prog_diff3_bin in +?*) + echo "$ac_t""yes" 1>&6 + ac_save_path=$PATH + PATH=$diff3PATH + # Extract the first word of "$rcs_cv_prog_diff3_bin", so it can be a program name with args. +set dummy $rcs_cv_prog_diff3_bin; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_DIFF3'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$DIFF3" in + /*) + ac_cv_path_DIFF3="$DIFF3" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_DIFF3="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_DIFF3" && ac_cv_path_DIFF3="$rcs_cv_prog_diff3_bin" + ;; +esac +fi +DIFF3="$ac_cv_path_DIFF3" +if test -n "$DIFF3"; then + echo "$ac_t""$DIFF3" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + PATH=$ac_save_path + ;; +'') + echo "$ac_t""no" 1>&6 + echo $ac_n "checking diff3 library program""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'rcs_cv_path_diff3_lib'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + $DIFF conftest0 conftest1 >conftest01 + $DIFF /dev/null conftest1 >conftestn1 + for i in /usr/*lib*/*diff3*; do + sh -c "exec $i -E conftest01 conftestn1 conftest0 /dev/null conftest1" >conftestout 2>/dev/null + # The exit status is arbitrary! Test the output a bit. + if + grep '^<<* *conftest0$' conftestout >/dev/null 2>&1 && + grep '^>>* *conftest1$' conftestout >/dev/null 2>&1 && + grep '^0a$' conftestout >/dev/null 2>&1 + then + rcs_cv_path_diff3_lib=$i + break + fi + done + +fi + + DIFF3=$rcs_cv_path_diff3_lib + case $DIFF3 in + '') { echo "configure: error: cannot find a working diff3 library program" 1>&2; exit 1; };; + ?*) echo "$ac_t""$DIFF3" 1>&6;; + esac + ;; +esac + + +case $DIFF3_BIN in +'') + case $rcs_cv_prog_diff3_bin in + '') DIFF3_BIN=0;; + ?*) DIFF3_BIN=1;; + esac + ;; +esac + +# Clean up simple `diff' test. +rm -f conftest* + +# Extract the first word of "ed", so it can be a program name with args. +set dummy ed; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_ED'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$ED" in + /*) + ac_cv_path_ED="$ED" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_ED="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_ED" && ac_cv_path_ED="ed" + ;; +esac +fi +ED="$ac_cv_path_ED" +if test -n "$ED"; then + echo "$ac_t""$ED" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +ac_save_path=$PATH +PATH=/usr/lib:/usr/bin:/bin:/usr/sbin:/sbin:$PATH +for ac_prog in sendmail mail mailx +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_SENDMAIL'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$SENDMAIL" in + /*) + ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_SENDMAIL="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +SENDMAIL="$ac_cv_path_SENDMAIL" +if test -n "$SENDMAIL"; then + echo "$ac_t""$SENDMAIL" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$SENDMAIL" && break +done + +PATH=$ac_save_path +case $SENDMAIL in +?*) SENDMAIL=\"$SENDMAIL\" +esac + +# Use the GNU pic -n option if available; it avoids GNU extensions, +# which is need for proper operation to generate a portable man page. +# Similarly, if using traditional pic, use its -D option. +for ac_prog in "pic -n" "gpic -n" "pic -D" "pic" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_PIC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$PIC"; then + ac_cv_prog_PIC="$PIC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_PIC="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +PIC="$ac_cv_prog_PIC" +if test -n "$PIC"; then + echo "$ac_t""$PIC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$PIC" && break +done +test -n "$PIC" || PIC="pic" + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 +if test $ac_cv_prog_gcc = yes; then + GCC=yes + if test "${CFLAGS+set}" != set; then + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_gcc_g=yes +else + ac_cv_prog_gcc_g=no +fi +rm -f conftest* + +fi + echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6 + if test $ac_cv_prog_gcc_g = yes; then + CFLAGS="-g -O" + else + CFLAGS="-O" + fi + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_ifs" + # As a last resort, use the slow shell script. + test -z "$ac_cv_path_install" && ac_cv_path_install="$ac_install_sh" +fi + INSTALL="$ac_cv_path_install" +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +set dummy ${MAKE-make}; ac_make=$2 +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \ + >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.4" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile man/Makefile src/Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@DIFF@%$DIFF%g +s%@DIFF_SUCCESS@%$DIFF_SUCCESS%g +s%@DIFF_FAILURE@%$DIFF_FAILURE%g +s%@DIFF_TROUBLE@%$DIFF_TROUBLE%g +s%@DIFFFLAGS@%$DIFFFLAGS%g +s%@DIFF_L@%$DIFF_L%g +s%@DIFF3@%$DIFF3%g +s%@DIFF3_BIN@%$DIFF3_BIN%g +s%@ED@%$ED%g +s%@SENDMAIL@%$SENDMAIL%g +s%@PIC@%$PIC%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@SET_MAKE@%$SET_MAKE%g + +CEOF +EOF +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile man/Makefile src/Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust relative srcdir, etc. for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file +fi; done +rm -f conftest.subs + + + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/gnu/usr.bin/rcs/configure.in b/gnu/usr.bin/rcs/configure.in new file mode 100644 index 00000000000..e1e18ac66b8 --- /dev/null +++ b/gnu/usr.bin/rcs/configure.in @@ -0,0 +1,262 @@ +# Configure template for RCS +# $Id: configure.in,v 1.1 1996/08/12 04:07:37 millert Exp $ +# Copyright 1995 Paul Eggert +# Process this file with autoconf to produce a configure script. + +AC_INIT(src/rcsbase.h) + +# Set up simple `diff' test. +echo 0 >conftest0 +echo 0 >conftest0c +echo 1 >conftest1 +cat >conftestok <<'EOF' +d1 1 +a1 1 +1 +EOF + +AC_ARG_WITH(diffutils, + [ --with-diffutils assume GNU diffutils is similarly installed], + [with_diffutils=$withval], + [with_diffutils=no] +) + +case $with_diffutils in +yes) + : ${DIFF='$(bindir)/diff'} + : ${DIFF3=${DIFF}3} + : ${DIFF3_BIN=1} + : ${DIFFFLAGS=-an} + : ${DIFF_FAILURE=1} + : ${DIFF_L=1} + : ${DIFF_SUCCESS=0} + : ${DIFF_TROUBLE=2} +esac + +# Set DIFF to the name of the `diff' program to be run. +# On some systems, the RCS-compatible diff program is called `rdiff'; +# use it if it works and `diff' doesn't. +AC_SUBST(DIFF) +AC_MSG_CHECKING([diff basename]) +AC_CACHE_VAL(rcs_cv_prog_diff, [ + rcs_cv_prog_diff=$DIFF + case $rcs_cv_prog_diff in + '') + for i in diff /usr/lib/rdiff rdiff + do + sh -c "exec $i -n conftest0 conftest1" >conftestout 2>/dev/null + case $? in + 1) + if cmp -s conftestok conftestout + then rcs_cv_prog_diff=$i; break + fi + ;; + esac + done + ;; + esac +]) +DIFF=$rcs_cv_prog_diff +case $DIFF in +'') AC_MSG_ERROR(cannot find RCS-compatible diff);; +esac +AC_MSG_RESULT($DIFF) +AC_PATH_PROG(DIFF, $DIFF, $DIFF) + +# Set DIFF_SUCCESS, DIFF_FAILURE, DIFF_TROUBLE to diff's exit status +# when it finds no differences, some differences, or trouble. +AC_SUBST(DIFF_SUCCESS) +AC_MSG_CHECKING([diff success status]) +AC_CACHE_VAL(rcs_cv_status_diff_success, [ + rcs_cv_status_diff_success=$DIFF_SUCCESS + case $rcs_cv_status_diff_success in + '') + # We can't use `$DIFF conftest0 conftest0', + # since buggy NEXTSTEP 3.0 diff silently yields exit status 2 for this. + $DIFF conftest0 conftest0c >/dev/null 2>&1 + rcs_cv_status_diff_success=$? + ;; + esac +]) +DIFF_SUCCESS=$rcs_cv_status_diff_success +AC_MSG_RESULT($DIFF_SUCCESS) +# +AC_SUBST(DIFF_FAILURE) +AC_MSG_CHECKING([diff failure status]) +AC_CACHE_VAL(rcs_cv_status_diff_failure, [ + rcs_cv_status_diff_failure=$DIFF_FAILURE + case $rcs_cv_status_diff_failure in + '') + $DIFF conftest0 conftest1 >/dev/null 2>&1 + rcs_cv_status_diff_failure=$? + ;; + esac +]) +DIFF_FAILURE=$rcs_cv_status_diff_failure +AC_MSG_RESULT($DIFF_FAILURE) +# +AC_SUBST(DIFF_TROUBLE) +AC_MSG_CHECKING([diff trouble status]) +AC_CACHE_VAL(rcs_cv_status_diff_trouble, [ + rcs_cv_status_diff_trouble=$DIFF_TROUBLE + case $rcs_cv_status_diff_trouble in + '') + $DIFF conftest0 no/such/file >/dev/null 2>&1 + rcs_cv_status_diff_trouble=$? + ;; + esac +]) +DIFF_TROUBLE=$rcs_cv_status_diff_trouble +AC_MSG_RESULT($DIFF_TROUBLE) + +# Set DIFFFLAGS to the options of the `diff' program to be run. +# Use -an if possible, -n otherwise. +AC_SUBST(DIFFFLAGS) +AC_MSG_CHECKING([diff options for RCS]) +AC_CACHE_VAL(rcs_cv_options_diff, [ + rcs_cv_options_diff=$DIFFFLAGS + case $rcs_cv_options_diff in + '') + rcs_cv_options_diff=-n + $DIFF -an conftest0 conftest1 >conftestout 2>conftestout2 + case $? in + 1) + if cmp -s conftestok conftestout && test ! -s conftestout2 + then rcs_cv_options_diff=-an + fi + ;; + esac + ;; + esac +]) +DIFFFLAGS=$rcs_cv_options_diff +AC_MSG_RESULT($DIFFFLAGS) + +# Set DIFF_L to 1 if diff understands the L option, 0 otherwise. +AC_SUBST(DIFF_L) +AC_MSG_CHECKING([diff -L]) +AC_CACHE_VAL(rcs_cv_options_diff_l, [ + rcs_cv_options_diff_l=$DIFF_L + case $rcs_cv_options_diff_l in + '') + rcs_cv_options_diff_l=0 + $DIFF -c -L 0 -L 1 conftest0 conftest1 >conftestout 2>/dev/null + case $? in + 1) + if cmp -s - conftestout <<'EOF' +*** 0 +--- 1 +*************** +*** 1 **** +! 0 +--- 1 ---- +! 1 +EOF + then rcs_cv_options_diff_l=1 + fi + ;; + esac + ;; + esac +]) +DIFF_L=$rcs_cv_options_diff_l +case $DIFF_L in +1) AC_MSG_RESULT(yes);; +*) AC_MSG_RESULT(no);; +esac + +# Set DIFF3 to the name of the diff3 program. +# In some systems (e.g. BSD/OS 2.0), diffutils diff3 lives in /usr/libexec. +diff3PATH=$PATH:/usr/libexec +AC_SUBST(DIFF3) +AC_MSG_CHECKING([diff3 -m]) +AC_CACHE_VAL(rcs_cv_prog_diff3_bin, [ + rcs_cv_prog_diff3_bin=$DIFF3 + case $rcs_cv_prog_diff3_bin in + '') + PATH=$diff3PATH sh -c "exec diff3 -E -m -L 0 -L 1 -L 2 conftest0 conftest1 /dev/null" >conftestout 2>/dev/null + case $? in + 1) + if cmp -s - conftestout <<'EOF' +<<<<<<< 0 +0 +======= +>>>>>>> 2 +EOF + then rcs_cv_prog_diff3_bin=diff3 + fi + ;; + esac + ;; + esac +]) +case $rcs_cv_prog_diff3_bin in +?*) + AC_MSG_RESULT(yes) + ac_save_path=$PATH + PATH=$diff3PATH + AC_PATH_PROG(DIFF3, $rcs_cv_prog_diff3_bin, $rcs_cv_prog_diff3_bin) + PATH=$ac_save_path + ;; +'') + AC_MSG_RESULT(no) + AC_MSG_CHECKING([diff3 library program]) + dnl We can't use AC_PATH_PROG since we don't want to inspect /bin, + dnl and AC_PATH_PROG uses `test'. + AC_CACHE_VAL(rcs_cv_path_diff3_lib, [ + $DIFF conftest0 conftest1 >conftest01 + $DIFF /dev/null conftest1 >conftestn1 + for i in /usr/*lib*/*diff3*; do + sh -c "exec $i -E conftest01 conftestn1 conftest0 /dev/null conftest1" >conftestout 2>/dev/null + # The exit status is arbitrary! Test the output a bit. + if + grep '^<<* *conftest0$' conftestout >/dev/null 2>&1 && + grep '^>>* *conftest1$' conftestout >/dev/null 2>&1 && + grep '^0a$' conftestout >/dev/null 2>&1 + then + rcs_cv_path_diff3_lib=$i + break + fi + done + ]) + DIFF3=$rcs_cv_path_diff3_lib + case $DIFF3 in + '') AC_MSG_ERROR(cannot find a working diff3 library program);; + ?*) AC_MSG_RESULT($DIFF3);; + esac + ;; +esac + +AC_SUBST(DIFF3_BIN) +case $DIFF3_BIN in +'') + case $rcs_cv_prog_diff3_bin in + '') DIFF3_BIN=0;; + ?*) DIFF3_BIN=1;; + esac + ;; +esac + +# Clean up simple `diff' test. +rm -f conftest* + +AC_PATH_PROG(ED, ed, ed) + +ac_save_path=$PATH +PATH=/usr/lib:/usr/bin:/bin:/usr/sbin:/sbin:$PATH +AC_PATH_PROGS(SENDMAIL, sendmail mail mailx) +PATH=$ac_save_path +case $SENDMAIL in +?*) SENDMAIL=\"$SENDMAIL\" +esac + +# Use the GNU pic -n option if available; it avoids GNU extensions, +# which is need for proper operation to generate a portable man page. +# Similarly, if using traditional pic, use its -D option. +AC_CHECK_PROGS(PIC, "pic -n" "gpic -n" "pic -D" "pic", pic) + +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +AC_OUTPUT(Makefile man/Makefile src/Makefile) diff --git a/gnu/usr.bin/rcs/install-sh b/gnu/usr.bin/rcs/install-sh new file mode 100644 index 00000000000..89fc9b098b8 --- /dev/null +++ b/gnu/usr.bin/rcs/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/gnu/usr.bin/rcs/man/ChangeLog b/gnu/usr.bin/rcs/man/ChangeLog new file mode 100644 index 00000000000..fd801d8b597 --- /dev/null +++ b/gnu/usr.bin/rcs/man/ChangeLog @@ -0,0 +1,214 @@ +Fri Jun 16 06:19:24 1995 Paul Eggert <eggert@twinsun.com> + + * ci.1, rlog.1: Fix typos in -zLT example. + * Makefile.in: Update FSF address. + +Mon Jun 5 08:28:35 1995 Paul Eggert <eggert@twinsun.com> + + * rcs.1, rcsfile.5in: Fix minor troff glitches. + +Thu Jun 1 16:23:43 1995 Paul Eggert <eggert@twinsun.com> + + * Makefile.in: + Renamed from Makefile; autoconf now preprocesses this file. + Complete rewrite to follow GNU makefile standards. + + * co.1: Fix troff glitches. + Describe -kb, compatibility hack for C-style Logs. + + * ci.1, co.1, rlog.1: + Use `-08' style time zone, not `-0800', for ISO 8601 conformance. + + * rcs.1: Require at least one option. + + * rcsmerge.1: Do not merge if -kb. diff3 -A is no longer the default. + * merge.1: diff3 -A is no longer the default. + Add bug: binary files are merged. + +Sun Mar 20 04:52:58 1994 Paul Eggert <eggert@twinsun.com> + + * rcsfile.5in: Renamed from rcsfile.5. + * Makefile: Run off rcsfile.5 with pic. Use INSTALL, not install. + +Thu Mar 17 14:05:48 1994 Paul Eggert <eggert@twinsun.com> + + * ci.1: Clarify that `rev' must already be defined. + * co.1: Describe new comment leader behavior. + $Log is inserted even with -kk. Suggest rcs -V. + * rcs.1: Describe new comment leader behavior. + * rcsfile.5: Add pic support. + * rlog.1: -d'<DATE' is now exclusive. + +Tue Nov 9 17:40:15 1993 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1, rlog.1: Use ISO 8601 format. + * ident.1: Add -V. + * merge.1: -V now prints version on stdout and exits. + +Wed Nov 3 17:42:27 1993 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1, ident.1, merge.1, rcs.1, rcsclean.1, rcsdiff.1, + rcsfile.5, rcsintro.1, rcsmerge.1, rlog.1: Update for RCS 5.6.5. + +Tue Jul 28 16:12:44 1992 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1, rcs.1, rcsclean.1, rcsdiff.1, rcsmerge.1, rlog.1: + Add plain -V. + * co.1: Remove -d BUG. + * ci.1: Add -i, -j. + * ident.1: Fix wording. + * rcs.1: rcs -l now lets you break locks. + * rcsfile.5: Identifiers may now start with a digit and + (unless they are symbolic names) may contain `.'. + * Makefile: Install rcsclean man page. + +Mon Feb 17 23:02:05 1992 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1, rcsclean.1, rcs.1: Add -T. + * ident.1: Give a better C example. + Describe behavior on non-keywords like XConsortium. + * ci.1: Give -r the funny meaning only if it's bare. + RCS just needs to be somewhere in an RCS file's path, not at the end. + * co.1, ident.1, merge.1, rcs.1, rcsfile.5, rcsclean.1, rcsintro.1: + Fix troff lint. + +Mon Jan 27 16:46:02 1992 Paul Eggert <eggert@twinsun.com> + + * rcs.1: Add -M. + +Fri Jan 24 18:44:19 1992 Paul Eggert <eggert@twinsun.com> + + * rcsclean.1: Fix old comment about reverting to previous revision. + +Mon Jan 6 02:21:14 1992 Paul Eggert <eggert@twinsun.com> + + * ci.1: real user -> real or effective user + +Sun Nov 3 01:09:19 1991 Paul Eggert <eggert@twinsun.com> + + * rcsclean.1: Don't remove checked-out files unless -u succeeds. + +Mon Oct 7 17:32:46 1991 Paul Eggert <eggert@twinsun.com> + + * ci.1: Explain -f more clearly. + +Thu Sep 26 23:16:17 1991 Paul Eggert <eggert@twinsun.com> + + * rcs.1: rev 's -> rev's + +Thu Aug 22 06:50:48 1991 Paul Eggert <eggert@twinsun.com> + + * rlog.1: rcs -o -> rlog -r (fix typo). + +Mon Aug 19 03:13:55 1991 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1: Describe changes for -M and -r$. + * ci.1: Describe permissions more carefully. + Specify temporary file names. + Describe aborted checkins, and symlinks. + + * rlog.1: -r gets latest revision. + `:' now separates revisions in ranges, not `-'. + -rB. stands for the latest revision in branch B. + + * rcsclean.1: + Add -k, -r, -u, -V, RCSINIT. rcsclean now says what it does. + The exit status is now more conventional. + + * Makefile: Add MANDIR. + + * rcsfile.5: An RCS file must end in a newline character. + + * rcs.1: `:' now separates revisions in ranges, not `-'. + -rB. stands for the latest revision in branch B. + -mR:M replaces the log message for revision R with M. + + * rcsmerge.1: co -j -> merge + +Sun Apr 21 12:00:46 1991 Paul Eggert <eggert@twinsun.com> + + * ci.1, co.1, rcs.1, rcsclean.1, rcsdiff.1, rcsintro.1, rcsmerge.1, + rlog.1: Snapshot for DOS beta. + +Thu Feb 28 19:18:46 1991 Paul Eggert <eggert@twinsun.com> + + * ci.1: Describe setuid use better. + * merge.1, rcsclean.1: Fix white space. + +Mon Feb 25 07:12:31 1991 Paul Eggert <eggert@twinsun.com> + + * ci.1: Remove setgid support. + * co.1: Refer to merge(1) for how overlaps are reported. + * merge.1: On a few older hosts, overlaps are not reported. + +Tue Dec 4 05:18:34 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.1, co.1, ci.1: Use -I for prompts and -q for diagnostics. + +Thu Nov 1 05:03:17 1990 Paul Eggert <eggert@twinsun.com> + + * ci.1, rcs.1: Add -I and new -t behavior. + * ci.1: Describe setid behavior on old hosts. + * co.1: Add -I. + * rcsclean.1: Add rcsdiff options. + Clean working directory if no arguments are given. + * rcsfreeze.1: Add to BUGS. + * rcsclean, rcsfreeze.1: Fix troff glitches. + +Tue Sep 4 08:02:08 1990 Paul Eggert <eggert@twinsun.com> + + * co.1: Don't parse two-digit years, because it won't work after + 1999/12/31. + +Wed Aug 29 07:16:40 1990 Paul Eggert <eggert@twinsun.com> + + * co.1, rcsdiff.1: Add -kkvl. + * co.1: Document locker name insertion. + * merge.1, rcsmerge.1: Add -q. + * merge.1: Remove obsolete BUG section. + * rcsmerge.1: Document -p with rev. + * ci.1, co.1, rcs.1, rcsmerge.1: Fix troff glitches. + +Wed Aug 22 09:11:00 1990 Paul Eggert <eggert@twinsun.com> + + * Makefile: Don't put 'l' at the end of manual page names. + * ci.1: Add -V. + * co.1, rcs.1, rcsdiff.1, rcsmerge.1: Add -k, -V. + * ident.1: Look for all possible keywords, + not just those in the current list. + * merge.1: Add -L. + * rcsfile.5: Add description of version 5 syntax. + * rlog.1: Add -V. + * rcsintro.1, and all other man pages mentioned above: + Fix troff glitches. + +Thu May 24 22:28:46 1990 Paul Eggert <eggert@twinsun.com> + + * rcsfile.5: Document the fact that the branch keyword is + now optional, and that the character set is now ISO Latin 1. + +Wed May 23 06:33:58 1990 Paul Eggert <eggert@twinsun.com> + + * co.1, merge.1, rcsdiff.1: Document new -k, -K, -t options. + Reformat and remove lint. + +Thu Mar 22 06:48:26 1990 Paul Eggert <eggert@twinsun.com> + + * rcsdiff.1: Don't document all of diff's flags; just say that + RCS supports diff's flags. + * ci.1, co.1, rcs.1, rcsdiff.1, rlog.1, rcsmerge.1: + Add emulation of older RCS versions. + +Fri Jan 12 20:37:26 1990 Paul Eggert <eggert@twinsun.com> + + * merge.1: Document extra alias arguments. Fix layout problems. + +Thu Jan 11 16:35:48 1990 Paul Eggert <eggert@twinsun.com> + + * rlog.1, co.1: Use GMT, not local time, so people in + different timezones can collaborate. + +Wed Aug 2 14:26:46 1989 Paul Eggert <eggert@twinsun.com> + + * rlog.1, rcsdiff.1, rcs.1, co.1, ci.1: + Update DIAGNOSTICS to reflect RCS version 4. diff --git a/gnu/usr.bin/rcs/man/Makefile.in b/gnu/usr.bin/rcs/man/Makefile.in new file mode 100644 index 00000000000..f3512910f6f --- /dev/null +++ b/gnu/usr.bin/rcs/man/Makefile.in @@ -0,0 +1,80 @@ +# Make RCS man pages. + +# $Id: Makefile.in,v 1.1 1996/08/12 04:07:45 millert Exp $ + +# Copyright 1995 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, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + +srcdir = @srcdir@ +VPATH = @srcdir@ + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +PIC = @PIC@ + +prefix = @prefix@ +man1dir = $(prefix)/man/man1 +man5dir = $(prefix)/man/man5 +man1ext = .1 +man5ext = .5 + +SHELL = /bin/sh + +all default :: rcsfile.5 + +rcsfile.5 : rcsfile.5in + $(PIC) $(PICFLAGS) $(srcdir)/$@in >$@ + +check dvi info TAGS :: + +installdirs :: ../mkinstalldirs + -$(srcdir)/../mkinstalldirs $(man1dir) $(man5dir) + +man1pages = ci co ident merge rcs rcsclean rcsdiff rcsintro rcsmerge rlog + +install :: installdirs + -for m in $(man1pages); do \ + $(INSTALL_DATA) $(srcdir)/$$m.1 $(man1dir)/$$m$(man1ext); \ + done + -{ test -f rcsfile.5 || cd $(srcdir); } && \ + $(INSTALL_DATA) rcsfile.5 $(man5dir)/rcsfile$(man5ext) + +uninstall :: + for m in $(man1pages); do \ + rm -f $(man1dir)/$$m$(man1ext); \ + done + rm -f $(man5dir)/rcsfile$(man5ext) + +clean mostlyclean :: + rm -f core core.* *.core + +distclean :: clean + rm -f Makefile + +maintainer-clean :: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f rcsfile.5 + +installcheck installdebug :: diff --git a/gnu/usr.bin/rcs/man/ci.1 b/gnu/usr.bin/rcs/man/ci.1 new file mode 100644 index 00000000000..5cabba71b40 --- /dev/null +++ b/gnu/usr.bin/rcs/man/ci.1 @@ -0,0 +1,899 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: ci.1,v 1.1 1996/08/12 04:07:46 millert Exp $ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CI 1 \*(Dt GNU +.SH NAME +ci \- check in RCS revisions +.SH SYNOPSIS +.B ci +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B ci +stores new revisions into \*r files. +Each pathname matching an \*r suffix +is taken to be an \*r file. +All others +are assumed to be working files containing new revisions. +.B ci +deposits the contents of each working file +into the corresponding \*r file. +If only a working file is given, +.B ci +tries to find the corresponding \*r file in an \*r subdirectory +and then in the working file's directory. +For more details, see +.SM "FILE NAMING" +below. +.PP +For +.B ci +to work, the caller's login must be on the access list, +except if the access list is empty or the caller is the superuser or the +owner of the file. +To append a new revision to an existing branch, the tip revision on +that branch must be locked by the caller. Otherwise, only a +new branch can be created. This restriction is not enforced +for the owner of the file if non-strict locking is used +(see +.BR rcs (1)). +A lock held by someone else can be broken with the +.B rcs +command. +.PP +Unless the +.B \-f +option is given, +.B ci +checks whether the revision to be deposited differs from the preceding one. +If not, instead of creating a new revision +.B ci +reverts to the preceding one. +To revert, ordinary +.B ci +removes the working file and any lock; +.B "ci\ \-l" +keeps and +.B "ci\ \-u" +removes any lock, and then they both generate a new working file much as if +.B "co\ \-l" +or +.B "co\ \-u" +had been applied to the preceding revision. +When reverting, any +.B \-n +and +.B \-s +options apply to the preceding revision. +.PP +For each revision deposited, +.B ci +prompts for a log message. +The log message should summarize the change and must be terminated by +end-of-file or by a line containing +.BR \&. "\ by" +itself. +If several files are checked in +.B ci +asks whether to reuse the +previous log message. +If the standard input is not a terminal, +.B ci +suppresses the prompt +and uses the same log message for all files. +See also +.BR \-m . +.PP +If the \*r file does not exist, +.B ci +creates it and +deposits the contents of the working file as the initial revision +(default number: +.BR 1.1 ). +The access list is initialized to empty. +Instead of the log message, +.B ci +requests descriptive text (see +.B \-t +below). +.PP +The number +.I rev +of the deposited revision can be given by any of the options +.BR \-f , +.BR \-i , +.BR \-I , +.BR \-j , +.BR \-k , +.BR \-l , +.BR \-M , +.BR \-q , +.BR \-r , +or +.BR \-u . +.I rev +can be symbolic, numeric, or mixed. +Symbolic names in +.I rev +must already be defined; +see the +.B \-n +and +.B \-N +options for assigning names during checkin. +If +.I rev +is +.BR $ , +.B ci +determines the revision number from keyword values in the working file. +.PP +If +.I rev +begins with a period, +then the default branch (normally the trunk) is prepended to it. +If +.I rev +is a branch number followed by a period, +then the latest revision on that branch is used. +.PP +If +.I rev +is a revision number, it must be higher than the latest +one on the branch to which +.I rev +belongs, or must start a new branch. +.PP +If +.I rev +is a branch rather than a revision number, +the new revision is appended to that branch. The level number is obtained +by incrementing the tip revision number of that branch. +If +.I rev +indicates a non-existing branch, +that branch is created with the initial revision numbered +.IB rev .1\f1.\fP +.br +.ne 8 +.PP +If +.I rev +is omitted, +.B ci +tries to derive the new revision number from +the caller's last lock. If the caller has locked the tip revision of a branch, +the new revision is appended to that branch. +The new revision number is obtained +by incrementing the tip revision number. +If the caller locked a non-tip revision, a new branch is started at +that revision by incrementing the highest branch number at that revision. +The default initial branch and level numbers are +.BR 1 . +.PP +If +.I rev +is omitted and the caller has no lock, but owns +the file and locking +is not set to +.IR strict , +then the revision is appended to the +default branch (normally the trunk; see the +.B \-b +option of +.BR rcs (1)). +.PP +Exception: On the trunk, revisions can be appended to the end, but +not inserted. +.SH OPTIONS +.TP +.BI \-r rev +Check in revision +.IR rev . +.TP +.BR \-r +The bare +.B \-r +option (without any revision) has an unusual meaning in +.BR ci . +With other \*r commands, a bare +.B \-r +option specifies the most recent revision on the default branch, +but with +.BR ci , +a bare +.B \-r +option reestablishes the default behavior of releasing a lock and +removing the working file, and is used to override any default +.B \-l +or +.B \-u +options established by shell aliases or scripts. +.TP +.BR \-l [\f2rev\fP] +works like +.BR \-r , +except it performs an additional +.B "co\ \-l" +for the +deposited revision. Thus, the deposited revision is immediately +checked out again and locked. +This is useful for saving a revision although one wants to continue +editing it after the checkin. +.TP +.BR \-u [\f2rev\fP] +works like +.BR \-l , +except that the deposited revision is not locked. +This lets one read the working file +immediately after checkin. +.RS +.PP +The +.BR \-l , +bare +.BR \-r , +and +.B \-u +options are mutually exclusive and silently override each other. +For example, +.B "ci\ \-u\ \-r" +is equivalent to +.B "ci\ \-r" +because bare +.B \-r +overrides +.BR \-u . +.RE +.TP +.BR \-f [\f2rev\fP] +forces a deposit; the new revision is deposited even it is not different +from the preceding one. +.TP +.BR \-k [\f2rev\fP] +searches the working file for keyword values to determine its revision number, +creation date, state, and author (see +.BR co (1)), +and assigns these +values to the deposited revision, rather than computing them locally. +It also generates a default login message noting the login of the caller +and the actual checkin date. +This option is useful for software distribution. A revision that is sent to +several sites should be checked in with the +.B \-k +option at these sites to +preserve the original number, date, author, and state. +The extracted keyword values and the default log message can be overridden +with the options +.BR \-d , +.BR \-m , +.BR \-s , +.BR \-w , +and any option that carries a revision number. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostic output is not printed. +A revision that is not different from the preceding one is not deposited, +unless +.B \-f +is given. +.TP +.BR \-i [\f2rev\fP] +initial checkin; report an error if the \*r file already exists. +This avoids race conditions in certain applications. +.TP +.BR \-j [\f2rev\fP] +just checkin and do not initialize; +report an error if the \*r file does not already exist. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BR \-d "[\f2date\fP]" +uses +.I date +for the checkin date and time. +The +.I date +is specified in free format as explained in +.BR co (1). +This is useful for lying about the checkin date, and for +.B \-k +if no date is available. +If +.I date +is empty, the working file's time of last modification is used. +.TP +.BR \-M [\f2rev\fP] +Set the modification time on any new working file +to be the date of the retrieved revision. +For example, +.BI "ci\ \-d\ \-M\ \-u" "\ f" +does not alter +.IR f 's +modification time, even if +.IR f 's +contents change due to keyword substitution. +Use this option with care; it can confuse +.BR make (1). +.TP +.BI \-m "msg" +uses the string +.I msg +as the log message for all revisions checked in. +By convention, log messages that start with +.B # +are comments and are ignored by programs like GNU Emacs's +.B vc +package. +Also, log messages that start with +.BI { clumpname } +(followed by white space) are meant to be clumped together if possible, +even if they are associated with different files; the +.BI { clumpname } +label is used only for clumping, +and is not considered to be part of the log message itself. +.TP +.BI \-n "name" +assigns the symbolic name +.I name +to the number of the checked-in revision. +.B ci +prints an error message if +.I name +is already assigned to another +number. +.TP +.BI \-N "name" +same as +.BR \-n , +except that it overrides a previous assignment of +.IR name . +.TP +.BI \-s "state" +sets the state of the checked-in revision to the identifier +.IR state . +The default state is +.BR Exp . +.TP +.BI \-t file +writes descriptive text from the contents of the named +.I file +into the \*r file, +deleting the existing text. +The +.I file +cannot begin with +.BR \- . +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.RS +.PP +The +.B \-t +option, in both its forms, has effect only during an initial checkin; +it is silently ignored otherwise. +.PP +During the initial checkin, if +.B \-t +is not given, +.B ci +obtains the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +The user is prompted for the text if interaction is possible; see +.BR \-I . +.PP +For backward compatibility with older versions of \*r, a bare +.B \-t +option is ignored. +.RE +.TP +.B \-T +Set the \*r file's modification time to the new revision's time +if the former precedes the latter and there is a new revision; +preserve the \*r file's modification time otherwise. +If you have locked a revision, +.B ci +usually updates the \*r file's modification time to the current time, +because the lock is stored in the \*r file +and removing the lock requires changing the \*r file. +This can create an \*r file newer than the working file in one of two ways: +first, +.B "ci\ \-M" +can create a working file with a date before the current time; +second, when reverting to the previous revision +the \*r file can change while the working file remains unchanged. +These two cases can cause excessive recompilation caused by a +.BR make (1) +dependency of the working file on the \*r file. +The +.B \-T +option inhibits this recompilation by lying about the \*r file's date. +Use this option with care; it can suppress recompilation even when +a checkin of one working file should affect +another working file associated with the same \*r file. +For example, suppose the \*r file's time is 01:00, +the (changed) working file's time is 02:00, +some other copy of the working file has a time of 03:00, +and the current time is 04:00. +Then +.B "ci\ \-d\ \-T" +sets the \*r file's time to 02:00 instead of the usual 04:00; +this causes +.BR make (1) +to think (incorrectly) that the other copy is newer than the \*r file. +.TP +.BI \-w "login" +uses +.I login +for the author field of the deposited revision. +Useful for lying about the author, and for +.B \-k +if no author is available. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +specifies the suffixes for \*r files. +A nonempty suffix matches any pathname ending in the suffix. +An empty suffix matches any pathname of the form +.BI RCS/ path +or +.IB path1 /RCS/ path2. +The +.B \-x +option can specify a list of suffixes +separated by +.BR / . +For example, +.B \-x,v/ +specifies two suffixes: +.B ,v +and the empty suffix. +If two or more suffixes are specified, +they are tried in order when looking for an \*r file; +the first one that works is used for that file. +If no \*r file is found but an \*r file can be created, +the suffixes are tried in order +to determine the new \*r file's name. +The default for +.IR suffixes +is installation-dependent; normally it is +.B ,v/ +for hosts like Unix that permit commas in filenames, +and is empty (i.e. just the empty suffix) for other hosts. +.TP +.BI \-z zone +specifies the date output format in keyword substitution, +and specifies the default time zone for +.I date +in the +.BI \-d date +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +The +.B \-z +option does not affect dates stored in \*r files, +which are always \*u. +.SH "FILE NAMING" +Pairs of \*r files and working files can be specified in three ways +(see also the +example section). +.PP +1) Both the \*r file and the working file are given. The \*r pathname is of +the form +.IB path1 / workfileX +and the working pathname is of the form +.IB path2 / workfile +where +.IB path1 / +and +.IB path2 / +are (possibly different or empty) paths, +.I workfile +is a filename, and +.I X +is an \*r suffix. +If +.I X +is empty, +.IB path1 / +must start with +.B RCS/ +or must contain +.BR /RCS/ . +.PP +2) Only the \*r file is given. Then the working file is created in the current +directory and its name is derived from the name of the \*r file +by removing +.IB path1 / +and the suffix +.IR X . +.PP +3) Only the working file is given. +Then +.B ci +considers each \*r suffix +.I X +in turn, looking for an \*r file of the form +.IB path2 /RCS/ workfileX +or (if the former is not found and +.I X +is nonempty) +.IB path2 / workfileX. +.PP +If the \*r file is specified without a path in 1) and 2), +.B ci +looks for the \*r file first in the directory +.B ./RCS +and then in the current +directory. +.PP +.B ci +reports an error if an attempt to open an \*r file fails for an unusual reason, +even if the \*r file's pathname is just one of several possibilities. +For example, to suppress use of \*r commands in a directory +.IR d , +create a regular file named +.IB d /RCS +so that casual attempts to use \*r commands in +.I d +fail because +.IB d /RCS +is not a directory. +.SH EXAMPLES +Suppose +.B ,v +is an \*r suffix and the current directory contains a subdirectory +.B RCS +with an \*r file +.BR io.c,v . +Then each of the following commands check in a copy of +.B io.c +into +.B RCS/io.c,v +as the latest revision, removing +.BR io.c . +.LP +.RS +.nf +.ft 3 +ci io.c; ci RCS/io.c,v; ci io.c,v; +ci io.c RCS/io.c,v; ci io.c io.c,v; +ci RCS/io.c,v io.c; ci io.c,v io.c; +.ft +.fi +.RE +.PP +Suppose instead that the empty suffix +is an \*r suffix and the current directory contains a subdirectory +.B RCS +with an \*r file +.BR io.c . +The each of the following commands checks in a new revision. +.LP +.RS +.nf +.ft 3 +ci io.c; ci RCS/io.c; +ci io.c RCS/io.c; +ci RCS/io.c io.c; +.ft +.fi +.RE +.SH "FILE MODES" +An \*r file created by +.B ci +inherits the read and execute permissions +from the working file. If the \*r file exists already, +.B ci +preserves its read and execute permissions. +.B ci +always turns off all write permissions of \*r files. +.SH FILES +Temporary files are created in the directory containing +the working file, and also in the temporary directory (see +.B \s-1TMPDIR\s0 +under +.BR \s-1ENVIRONMENT\s0 ). +A semaphore file or files are created in the directory containing the \*r file. +With a nonempty suffix, the semaphore names begin with +the first character of the suffix; therefore, do not specify an suffix +whose first character could be that of a working filename. +With an empty suffix, the semaphore names end with +.B _ +so working filenames should not end in +.BR _ . +.PP +.B ci +never changes an \*r or working file. +Normally, +.B ci +unlinks the file and creates a new one; +but instead of breaking a chain of one or more symbolic links to an \*r file, +it unlinks the destination file instead. +Therefore, +.B ci +breaks any hard or symbolic links to any working file it changes; +and hard links to \*r files are ineffective, +but symbolic links to \*r files are preserved. +.PP +The effective user must be able to +search and write the directory containing the \*r file. +Normally, the real user must be able to +read the \*r and working files +and to search and write the directory containing the working file; +however, some older hosts +cannot easily switch between real and effective users, +so on these hosts the effective user is used for all accesses. +The effective user is the same as the real user +unless your copies of +.B ci +and +.B co +have setuid privileges. +As described in the next section, +these privileges yield extra security if +the effective user owns all \*r files and directories, +and if only the effective user can write \*r directories. +.PP +Users can control access to \*r files by setting the permissions +of the directory containing the files; only users with write access +to the directory can use \*r commands to change its \*r files. +For example, in hosts that allow a user to belong to several groups, +one can make a group's \*r directories writable to that group only. +This approach suffices for informal projects, +but it means that any group member can arbitrarily change the group's \*r files, +and can even remove them entirely. +Hence more formal projects sometimes distinguish between an \*r administrator, +who can change the \*r files at will, and other project members, +who can check in new revisions but cannot otherwise change the \*r files. +.SH "SETUID USE" +To prevent anybody but their \*r administrator from deleting revisions, +a set of users can employ setuid privileges as follows. +.nr n \w'\(bu'+2n-1/1n +.ds n \nn +.if \n(.g .if r an-tag-sep .ds n \w'\(bu'u+\n[an-tag-sep]u +.IP \(bu \*n +Check that the host supports \*r setuid use. +Consult a trustworthy expert if there are any doubts. +It is best if the +.B seteuid +system call works as described in Posix 1003.1a Draft 5, +because \*r can switch back and forth easily +between real and effective users, even if the real user is +.BR root . +If not, the second best is if the +.B setuid +system call supports saved setuid +(the {\s-1_POSIX_SAVED_IDS\s0} behavior of Posix 1003.1-1990); +this fails only if the real or effective user is +.BR root . +If \*r detects any failure in setuid, it quits immediately. +.IP \(bu \nn +Choose a user +.I A +to serve as \*r administrator for the set of users. +Only +.I A +can invoke the +.B rcs +command on the users' \*r files. +.I A +should not be +.B root +or any other user with special powers. +Mutually suspicious sets of users should use different administrators. +.IP \(bu \nn +Choose a pathname +.I B +to be a directory of files to be executed by the users. +.IP \(bu \nn +Have +.I A +set up +.I B +to contain copies of +.B ci +and +.B co +that are setuid to +.I A +by copying the commands from their standard installation directory +.I D +as follows: +.LP +.RS +.nf +.ne 3 +\f3mkdir\fP \f2B\fP +\f3cp\fP \f2D\fP\^\f3/c[io]\fP \f2B\fP +\f3chmod go\-w,u+s\fP \f2B\fP\f3/c[io]\fP +.fi +.RE +.IP \(bu \nn +Have each user prepend +.I B +to their path as follows: +.LP +.RS +.nf +.ne 2 +\f3PATH=\fP\f2B\fP\f3:$PATH; export PATH\fP # ordinary shell +\f3set path=(\fP\f2B\fP \f3$path)\fP # C shell +.fi +.RE +.IP \(bu \nn +Have +.I A +create each \*r directory +.I R +with write access only to +.I A +as follows: +.LP +.RS +.nf +.ne 2 +\f3mkdir\fP \f2R\fP +\f3chmod go\-w\fP \f2R\fP +.fi +.RE +.IP \(bu \nn +If you want to let only certain users read the \*r files, +put the users into a group +.IR G , +and have +.I A +further protect the \*r directory as follows: +.LP +.RS +.nf +.ne 2 +\f3chgrp\fP \f2G R\fP +\f3chmod g\-w,o\-rwx\fP \f2R\fP +.fi +.RE +.IP \(bu \nn +Have +.I A +copy old \*r files (if any) into +.IR R , +to ensure that +.I A +owns them. +.IP \(bu \nn +An \*r file's access list limits who can check in and lock revisions. +The default access list is empty, +which grants checkin access to anyone who can read the \*r file. +If you want limit checkin access, +have +.I A +invoke +.B "rcs\ \-a" +on the file; see +.BR rcs (1). +In particular, +.BI "rcs\ \-e\ \-a" A +limits access to just +.IR A . +.IP \(bu \nn +Have +.I A +initialize any new \*r files with +.B "rcs\ \-i" +before initial checkin, adding the +.B \-a +option if you want to limit checkin access. +.IP \(bu \nn +Give setuid privileges only to +.BR ci , +.BR co , +and +.BR rcsclean ; +do not give them to +.B rcs +or to any other command. +.IP \(bu \nn +Do not use other setuid commands to invoke \*r commands; +setuid is trickier than you think! +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +A backslash escapes spaces within an option. +The +.B \s-1RCSINIT\s0 +options are prepended to the argument lists of most \*r commands. +Useful +.B \s-1RCSINIT\s0 +options include +.BR \-q , +.BR \-V , +.BR \-x , +and +.BR \-z . +.TP +.B \s-1TMPDIR\s0 +Name of the temporary directory. +If not set, the environment variables +.B \s-1TMP\s0 +and +.B \s-1TEMP\s0 +are inspected instead and the first value found is taken; +if none of them are set, +a host-dependent default is used, typically +.BR /tmp . +.SH DIAGNOSTICS +For each revision, +.B ci +prints the \*r file, the working file, and the number +of both the deposited and the preceding revision. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +co(1), +emacs(1), +ident(1), make(1), rcs(1), rcsclean(1), rcsdiff(1), +rcsintro(1), rcsmerge(1), rlog(1), setuid(2), rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/man/co.1 b/gnu/usr.bin/rcs/man/co.1 new file mode 100644 index 00000000000..ad6559bf25f --- /dev/null +++ b/gnu/usr.bin/rcs/man/co.1 @@ -0,0 +1,736 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: co.1,v 1.1 1996/08/12 04:07:47 millert Exp $ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CO 1 \*(Dt GNU +.SH NAME +co \- check out RCS revisions +.SH SYNOPSIS +.B co +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B co +retrieves a revision from each \*r file and stores it into +the corresponding working file. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +Revisions of an \*r file can be checked out locked or unlocked. Locking a +revision prevents overlapping updates. A revision checked out for reading or +processing (e.g., compiling) need not be locked. A revision checked out +for editing and later checkin must normally be locked. Checkout with locking +fails if the revision to be checked out is currently locked by another user. +(A lock can be broken with +.BR rcs "(1).)\ \&" +Checkout with locking also requires the caller to be on the access list of +the \*r file, unless he is the owner of the +file or the superuser, or the access list is empty. +Checkout without locking is not subject to accesslist restrictions, and is +not affected by the presence of locks. +.PP +A revision is selected by options for revision or branch number, +checkin date/time, author, or state. +When the selection options +are applied in combination, +.B co +retrieves the latest revision +that satisfies all of them. +If none of the selection options +is specified, +.B co +retrieves the latest revision +on the default branch (normally the trunk, see the +.B \-b +option of +.BR rcs (1)). +A revision or branch number can be attached +to any of the options +.BR \-f , +.BR \-I , +.BR \-l , +.BR \-M , +.BR \-p , +.BR \-q , +.BR \-r , +or +.BR \-u . +The options +.B \-d +(date), +.B \-s +(state), and +.B \-w +(author) +retrieve from a single branch, the +.I selected +branch, +which is either specified by one of +.BR \-f , +\&.\|.\|., +.BR \-u , +or the default branch. +.PP +A +.B co +command applied to an \*r +file with no revisions creates a zero-length working file. +.B co +always performs keyword substitution (see below). +.SH OPTIONS +.TP +.BR \-r [\f2rev\fP] +retrieves the latest revision whose number is less than or equal to +.IR rev . +If +.I rev +indicates a branch rather than a revision, +the latest revision on that branch is retrieved. +If +.I rev +is omitted, the latest revision on the default branch +(see the +.B \-b +option of +.BR rcs (1)) +is retrieved. +If +.I rev +is +.BR $ , +.B co +determines the revision number from keyword values in the working file. +Otherwise, a revision is composed of one or more numeric or symbolic fields +separated by periods. +If +.I rev +begins with a period, +then the default branch (normally the trunk) is prepended to it. +If +.I rev +is a branch number followed by a period, +then the latest revision on that branch is used. +The numeric equivalent of a symbolic field +is specified with the +.B \-n +option of the commands +.BR ci (1) +and +.BR rcs (1). +.TP +.BR \-l [\f2rev\fP] +same as +.BR \-r , +except that it also locks the retrieved revision for +the caller. +.TP +.BR \-u [\f2rev\fP] +same as +.BR \-r , +except that it unlocks the retrieved revision if it was +locked by the caller. If +.I rev +is omitted, +.B \-u +retrieves the revision locked by the caller, if there is one; otherwise, +it retrieves the latest revision on the default branch. +.TP +.BR \-f [\f2rev\fP] +forces the overwriting of the working file; +useful in connection with +.BR \-q . +See also +.SM "FILE MODES" +below. +.TP +.B \-kkv +Generate keyword strings using the default form, e.g.\& +.B "$\&Revision: \*(Rv $" +for the +.B Revision +keyword. +A locker's name is inserted in the value of the +.BR Header , +.BR Id , +and +.B Locker +keyword strings +only as a file is being locked, +i.e. by +.B "ci\ \-l" +and +.BR "co\ \-l". +This is the default. +.TP +.B \-kkvl +Like +.BR \-kkv , +except that a locker's name is always inserted +if the given revision is currently locked. +.TP +.B \-kk +Generate only keyword names in keyword strings; omit their values. +See +.SM "KEYWORD SUBSTITUTION" +below. +For example, for the +.B Revision +keyword, generate the string +.B $\&Revision$ +instead of +.BR "$\&Revision: \*(Rv $" . +This option is useful to ignore differences due to keyword substitution +when comparing different revisions of a file. +Log messages are inserted after +.B $\&Log$ +keywords even if +.B \-kk +is specified, +since this tends to be more useful when merging changes. +.TP +.B \-ko +Generate the old keyword string, +present in the working file just before it was checked in. +For example, for the +.B Revision +keyword, generate the string +.B "$\&Revision: 1.1 $" +instead of +.B "$\&Revision: \*(Rv $" +if that is how the string appeared when the file was checked in. +This can be useful for file formats +that cannot tolerate any changes to substrings +that happen to take the form of keyword strings. +.TP +.B \-kb +Generate a binary image of the old keyword string. +This acts like +.BR \-ko , +except it performs all working file input and output in binary mode. +This makes little difference on Posix and Unix hosts, +but on DOS-like hosts one should use +.B "rcs\ \-i\ \-kb" +to initialize an \*r file intended to be used for binary files. +Also, on all hosts, +.BR rcsmerge (1) +normally refuses to merge files when +.B \-kb +is in effect. +.TP +.B \-kv +Generate only keyword values for keyword strings. +For example, for the +.B Revision +keyword, generate the string +.B \*(Rv +instead of +.BR "$\&Revision: \*(Rv $" . +This can help generate files in programming languages where it is hard to +strip keyword delimiters like +.B "$\&Revision:\ $" +from a string. +However, further keyword substitution cannot be performed once the +keyword names are removed, so this option should be used with care. +Because of this danger of losing keywords, +this option cannot be combined with +.BR \-l , +and the owner write permission of the working file is turned off; +to edit the file later, check it out again without +.BR \-kv . +.TP +.BR \-p [\f2rev\fP] +prints the retrieved revision on the standard output rather than storing it +in the working file. +This option is useful when +.B co +is part of a pipe. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostics are not printed. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BI \-d date +retrieves the latest revision on the selected branch whose checkin date/time is +less than or equal to +.IR date . +The date and time can be given in free format. +The time zone +.B LT +stands for local time; +other common time zone names are understood. +For example, the following +.IR date s +are equivalent +if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of Coordinated Universal Time (\*u): +.RS +.LP +.RS +.nf +.ta \w'\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP 'u +.ne 10 +\f38:00 pm lt\fP +\f34:00 AM, Jan. 12, 1990\fP default is \*u +\f31990-01-12 04:00:00+00\fP \*i 8601 (\*u) +\f31990-01-11 20:00:00\-08\fP \*i 8601 (local time) +\f31990/01/12 04:00:00\fP traditional \*r format +\f3Thu Jan 11 20:00:00 1990 LT\fP output of \f3ctime\fP(3) + \f3LT\fP +\f3Thu Jan 11 20:00:00 PST 1990\fP output of \f3date\fP(1) +\f3Fri Jan 12 04:00:00 GMT 1990\fP +\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP Internet RFC 822 +\f312-January-1990, 04:00 WET\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +Most fields in the date and time can be defaulted. +The default time zone is normally \*u, but this can be overridden by the +.B \-z +option. +The other defaults are determined in the order year, month, day, +hour, minute, and second (most to least significant). At least one of these +fields must be provided. For omitted fields that are of higher significance +than the highest provided field, the time zone's current values are assumed. +For all other omitted fields, +the lowest possible values are assumed. +For example, without +.BR \-z , +the date +.B "20, 10:30" +defaults to +10:30:00 \*u of the 20th of the \*u time zone's current month and year. +The date/time must be quoted if it contains spaces. +.RE +.TP +.BR \-M [\f2rev\fP] +Set the modification time on the new working file +to be the date of the retrieved revision. +Use this option with care; it can confuse +.BR make (1). +.TP +.BI \-s state +retrieves the latest revision on the selected branch whose state is set to +.IR state . +.TP +.B \-T +Preserve the modification time on the \*r file +even if the \*r file changes because a lock is added or removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some other copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when the change of lock +would mean a change to keyword strings in the other working file. +.TP +.BR \-w [\f2login\fP] +retrieves the latest revision on the selected branch which was checked in +by the user with login name +.IR login . +If the argument +.I login +is +omitted, the caller's login is assumed. +.TP +.BI \-j joinlist +generates a new revision which is the join of the revisions on +.IR joinlist . +This option is largely obsoleted by +.BR rcsmerge (1) +but is retained for backwards compatibility. +.RS +.PP +The +.I joinlist +is a comma-separated list of pairs of the form +.IB rev2 : rev3, +where +.I rev2 +and +.I rev3 +are (symbolic or numeric) +revision numbers. +For the initial such pair, +.I rev1 +denotes the revision selected +by the above options +.BR \-f , +\&.\|.\|., +.BR \-w . +For all other pairs, +.I rev1 +denotes the revision generated by the previous pair. +(Thus, the output +of one join becomes the input to the next.) +.PP +For each pair, +.B co +joins revisions +.I rev1 +and +.I rev3 +with respect to +.IR rev2 . +This means that all changes that transform +.I rev2 +into +.I rev1 +are applied to a copy of +.IR rev3 . +This is particularly useful if +.I rev1 +and +.I rev3 +are the ends of two branches that have +.I rev2 +as a common ancestor. If +.IR rev1 < rev2 < rev3 +on the same branch, +joining generates a new revision which is like +.I rev3, +but with all changes that lead from +.I rev1 +to +.I rev2 +undone. +If changes from +.I rev2 +to +.I rev1 +overlap with changes from +.I rev2 +to +.I rev3, +.B co +reports overlaps as described in +.BR merge (1). +.PP +For the initial pair, +.I rev2 +can be omitted. The default is the common +ancestor. +If any of the arguments indicate branches, the latest revisions +on those branches are assumed. +The options +.B \-l +and +.B \-u +lock or unlock +.IR rev1 . +.RE +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.I n, +where +.I n +can be +.BR 3 , +.BR 4 , +or +.BR 5 . +This can be useful when interchanging \*r files with others who are +running older versions of \*r. +To see which version of \*r your correspondents are running, have them invoke +.BR "rcs \-V" ; +this works with newer versions of \*r. +If it doesn't work, have them invoke +.B rlog +on an \*r file; +if none of the first few lines of output contain the string +.B branch: +it is version 3; +if the dates' years have just two digits, it is version 4; +otherwise, it is version 5. +An \*r file generated while emulating version 3 loses its default branch. +An \*r revision generated while emulating version 4 or earlier has +a time stamp that is off by up to 13 hours. +A revision extracted while emulating version 4 or earlier contains +abbreviated dates of the form +.IB yy / mm / dd +and can also contain different white space and line prefixes +in the substitution for +.BR $\&Log$ . +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +specifies the date output format in keyword substitution, +and specifies the default time zone for +.I date +in the +.BI \-d date +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +The +.B \-z +option does not affect dates stored in \*r files, +which are always \*u. +.RE +.SH "KEYWORD SUBSTITUTION" +Strings of the form +.BI $ keyword $ +and +.BI $ keyword : .\|.\|. $ +embedded in +the text are replaced +with strings of the form +.BI $ keyword : value $ +where +.I keyword +and +.I value +are pairs listed below. +Keywords can be embedded in literal strings +or comments to identify a revision. +.PP +Initially, the user enters strings of the form +.BI $ keyword $ . +On checkout, +.B co +replaces these strings with strings of the form +.BI $ keyword : value $ . +If a revision containing strings of the latter form +is checked back in, the value fields will be replaced during the next +checkout. +Thus, the keyword values are automatically updated on checkout. +This automatic substitution can be modified by the +.B \-k +options. +.PP +Keywords and their corresponding values: +.TP +.B $\&Author$ +The login name of the user who checked in the revision. +.TP +.B $\&Date$ +The date and time the revision was checked in. +With +.BI \-z zone +a numeric time zone offset is appended; otherwise, the date is \*u. +.TP +.B $\&Header$ +A standard header containing the full pathname of the \*r file, the +revision number, the date and time, the author, the state, +and the locker (if locked). +With +.BI \-z zone +a numeric time zone offset is appended to the date; otherwise, the date is \*u. +.TP +.B $\&Id$ +Same as +.BR $\&Header$ , +except that the \*r filename is without a path. +.TP +.B $\&Locker$ +The login name of the user who locked the revision (empty if not locked). +.TP +.B $\&Log$ +The log message supplied during checkin, preceded by a header +containing the \*r filename, the revision number, the author, and the date +and time. +With +.BI \-z zone +a numeric time zone offset is appended; otherwise, the date is \*u. +Existing log messages are +.I not +replaced. +Instead, the new log message is inserted after +.BR $\&Log: .\|.\|. $ . +This is useful for +accumulating a complete change log in a source file. +.RS +.LP +Each inserted line is prefixed by the string that prefixes the +.B $\&Log$ +line. For example, if the +.B $\&Log$ +line is +.RB \*(lq "//\ $\&Log: tan.cc\ $" \*(rq, +\*r prefixes each line of the log with +.RB \*(lq "//\ " \*(rq. +This is useful for languages with comments that go to the end of the line. +The convention for other languages is to use a +.RB \*(lq " \(** " \(rq +prefix inside a multiline comment. +For example, the initial log comment of a C program +conventionally is of the following form: +.RS +.LP +.nf +.ft 3 +.ne 3 +/\(** +.in +\w'/'u +\(** $\&Log$ +\(**/ +.in +.ft +.fi +.RE +.LP +For backwards compatibility with older versions of \*r, if the log prefix is +.B /\(** +or +.B (\(** +surrounded by optional white space, inserted log lines contain a space +instead of +.B / +or +.BR ( ; +however, this usage is obsolescent and should not be relied on. +.RE +.TP +.B $\&Name$ +The symbolic name used to check out the revision, if any. +For example, +.B "co\ \-rJoe" +generates +.BR "$\&Name:\ Joe\ $" . +Plain +.B co +generates just +.BR "$\&Name:\ \ $" . +.TP +.B $\&RCSfile$ +The name of the \*r file without a path. +.TP +.B $\&Revision$ +The revision number assigned to the revision. +.TP +.B $\&Source$ +The full pathname of the \*r file. +.TP +.B $\&State$ +The state assigned to the revision with the +.B \-s +option of +.BR rcs (1) +or +.BR ci (1). +.PP +The following characters in keyword values are represented by escape sequences +to keep keyword strings well-formed. +.LP +.RS +.nf +.ne 6 +.ta \w'newline 'u +\f2char escape sequence\fP +tab \f3\et\fP +newline \f3\en\fP +space \f3\e040 +$ \e044 +\e \e\e\fP +.fi +.RE +.SH "FILE MODES" +The working file inherits the read and execute permissions from the \*r +file. In addition, the owner write permission is turned on, unless +.B \-kv +is set or the file +is checked out unlocked and locking is set to strict (see +.BR rcs (1)). +.PP +If a file with the name of the working file exists already and has write +permission, +.B co +aborts the checkout, +asking beforehand if possible. +If the existing working file is +not writable or +.B \-f +is given, the working file is deleted without asking. +.SH FILES +.B co +accesses files much as +.BR ci (1) +does, except that it does not need to read the working file +unless a revision number of +.B $ +is specified. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The \*r pathname, the working pathname, +and the revision number retrieved are +written to the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +rcsintro(1), ci(1), ctime(3), date(1), ident(1), make(1), +rcs(1), rcsclean(1), rcsdiff(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH LIMITS +Links to the \*r and working files are not preserved. +.PP +There is no way to selectively suppress the expansion of keywords, except +by writing them differently. In nroff and troff, this is done by embedding the +null-character +.B \e& +into the keyword. +.br diff --git a/gnu/usr.bin/rcs/man/ident.1 b/gnu/usr.bin/rcs/man/ident.1 new file mode 100644 index 00000000000..2eb537351ad --- /dev/null +++ b/gnu/usr.bin/rcs/man/ident.1 @@ -0,0 +1,182 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: ident.1,v 1.1 1996/08/12 04:07:48 millert Exp $ +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH IDENT 1 \*(Dt GNU +.SH NAME +ident \- identify RCS keyword strings in files +.SH SYNOPSIS +.B ident +[ +.B \-q +] [ +.B \-V +] [ +.I file +\&.\|.\|. ] +.SH DESCRIPTION +.B ident +searches for all instances of the pattern +.BI $ keyword : "\ text\ " $ +in the named files or, if no files are named, the standard input. +.PP +These patterns are normally inserted automatically by the \*r command +.BR co (1), +but can also be inserted manually. +The option +.B \-q +suppresses +the warning given if there are no patterns in a file. +The option +.B \-V +prints +.BR ident 's +version number. +.PP +.B ident +works on text files as well as object files and dumps. +For example, if the C program in +.B f.c +contains +.IP +.ft 3 +#include <stdio.h> +.br +static char const rcsid[] = +.br + \&"$\&Id: f.c,v \*(iD $\&"; +.br +int main() { return printf(\&"%s\en\&", rcsid) == EOF; } +.ft P +.LP +and +.B f.c +is compiled into +.BR f.o , +then the command +.IP +.B "ident f.c f.o" +.LP +will output +.nf +.IP +.ft 3 +f.c: + $\&Id: f.c,v \*(iD $ +f.o: + $\&Id: f.c,v \*(iD $ +.ft +.fi +.PP +If a C program defines a string like +.B rcsid +above but does not use it, +.BR lint (1) +may complain, and some C compilers will optimize away the string. +The most reliable solution is to have the program use the +.B rcsid +string, as shown in the example above. +.PP +.B ident +finds all instances of the +.BI $ keyword : "\ text\ " $ +pattern, even if +.I keyword +is not actually an \*r-supported keyword. +This gives you information about nonstandard keywords like +.BR $\&XConsortium$ . +.SH KEYWORDS +Here is the list of keywords currently maintained by +.BR co (1). +All times are given in Coordinated Universal Time (\*u, +sometimes called \&\s-1GMT\s0) by default, but if the files +were checked out with +.BR co 's +.BI \-z zone +option, times are given with a numeric time zone indication appended. +.TP +.B $\&Author$ +The login name of the user who checked in the revision. +.TP +.B $\&Date$ +The date and time the revision was checked in. +.TP +.B $\&Header$ +A standard header containing the full pathname of the \*r file, the +revision number, the date and time, the author, the state, +and the locker (if locked). +.TP +.B $\&Id$ +Same as +.BR $\&Header$ , +except that the \*r filename is without a path. +.TP +.B $\&Locker$ +The login name of the user who locked the revision (empty if not locked). +.TP +.B $\&Log$ +The log message supplied during checkin. +For +.BR ident 's +purposes, this is equivalent to +.BR $\&RCSfile$ . +.TP +.B $\&Name$ +The symbolic name used to check out the revision, if any. +.TP +.B $\&RCSfile$ +The name of the \*r file without a path. +.TP +.B $\&Revision$ +The revision number assigned to the revision. +.TP +.B $\&Source$ +The full pathname of the \*r file. +.TP +.B $\&State$ +The state assigned to the revision with the +.B \-s +option of +.BR rcs (1) +or +.BR ci (1). +.PP +.BR co (1) +represents the following characters in keyword values by escape sequences +to keep keyword strings well-formed. +.LP +.RS +.nf +.ne 6 +.ta \w'newline 'u +\f2char escape sequence\fP +tab \f3\et\fP +newline \f3\en\fP +space \f3\e040 +$ \e044 +\e \e\e\fP +.fi +.RE +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/gnu/usr.bin/rcs/man/merge.1 b/gnu/usr.bin/rcs/man/merge.1 new file mode 100644 index 00000000000..19460d7a505 --- /dev/null +++ b/gnu/usr.bin/rcs/man/merge.1 @@ -0,0 +1,135 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: merge.1,v 1.1 1996/08/12 04:07:48 millert Exp $ +.TH MERGE 1 \*(Dt GNU +.SH NAME +merge \- three-way file merge +.SH SYNOPSIS +.B merge +[ +.I "options" +] +.I "file1 file2 file3" +.SH DESCRIPTION +.B merge +incorporates all changes that lead from +.I file2 +to +.I file3 +into +.IR file1 . +The result ordinarily goes into +.IR file1 . +.B merge +is useful for combining separate changes to an original. Suppose +.I file2 +is the original, and both +.I file1 +and +.I file3 +are modifications of +.IR file2 . +Then +.B merge +combines both changes. +.PP +A conflict occurs if both +.I file1 +and +.I file3 +have changes in a common segment of lines. +If a conflict is found, +.B merge +normally outputs a warning and brackets the conflict with +.B <<<<<<< +and +.B >>>>>>> +lines. +A typical conflict will look like this: +.LP +.RS +.nf +.BI <<<<<<< " file A" +.I "lines in file A" +.B "=======" +.I "lines in file B" +.BI >>>>>>> " file B" +.RE +.fi +.LP +If there are conflicts, the user should edit the result and delete one of the +alternatives. +.SH OPTIONS +.TP +.B \-A +Output conflicts using the +.B \-A +style of +.BR diff3 (1), +if supported by +.BR diff3 . +This merges all changes leading from +.I file2 +to +.I file3 +into +.IR file1 , +and generates the most verbose output. +.TP +\f3\-E\fP, \f3\-e\fP +These options specify conflict styles that generate less information +than +.BR \-A . +See +.BR diff3 (1) +for details. +The default is +.BR \-E . +With +.BR \-e , +.B merge +does not warn about conflicts. +.TP +.BI \-L " label" +This option may be given up to three times, and specifies labels +to be used in place of the corresponding file names in conflict reports. +That is, +.B "merge\ \-L\ x\ \-L\ y\ \-L\ z\ a\ b\ c" +generates output that looks like it came from files +.BR x , +.B y +and +.B z +instead of from files +.BR a , +.B b +and +.BR c . +.TP +.BI \-p +Send results to standard output instead of overwriting +.IR file1 . +.TP +.BI \-q +Quiet; do not warn about conflicts. +.BI \-V +Print \*r's version number. +.SH DIAGNOSTICS +Exit status is 0 for no conflicts, 1 for some conflicts, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH SEE ALSO +diff3(1), diff(1), rcsmerge(1), co(1). +.SH BUGS +It normally does not make sense to merge binary files as if they were text, but +.B merge +tries to do it anyway. +.br diff --git a/gnu/usr.bin/rcs/man/rcs.1 b/gnu/usr.bin/rcs/man/rcs.1 new file mode 100644 index 00000000000..bb70f9047be --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcs.1 @@ -0,0 +1,454 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcs.1,v 1.1 1996/08/12 04:07:49 millert Exp $ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.if !\n(.g \{\ +. if !\w|\*(lq| \{\ +. ds lq `` +. if \w'\(lq' .ds lq "\(lq +. \} +. if !\w|\*(rq| \{\ +. ds rq '' +. if \w'\(rq' .ds rq "\(rq +. \} +.\} +.TH RCS 1 \*(Dt GNU +.SH NAME +rcs \- change RCS file attributes +.SH SYNOPSIS +.B rcs +.IR "options file " .\|.\|. +.SH DESCRIPTION +.B rcs +creates new \*r files or changes attributes of existing ones. +An \*r file contains multiple revisions of text, +an access list, a change log, +descriptive text, +and some control attributes. +For +.B rcs +to work, the caller's login name must be on the access list, +except if the access list is empty, the caller is the owner of the file +or the superuser, or +the +.B \-i +option is present. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +Revision numbers use the syntax described in +.BR ci (1). +.SH OPTIONS +.TP +.B \-i +Create and initialize a new \*r file, but do not deposit any revision. +If the \*r file has no path prefix, try to place it +first into the subdirectory +.BR ./RCS , +and then into the current directory. +If the \*r file +already exists, print an error message. +.TP +.BI \-a "logins" +Append the login names appearing in the comma-separated list +.I logins +to the access list of the \*r file. +.TP +.BI \-A "oldfile" +Append the access list of +.I oldfile +to the access list of the \*r file. +.TP +.BR \-e [\f2logins\fP] +Erase the login names appearing in the comma-separated list +.I logins +from the access list of the \*r file. +If +.I logins +is omitted, erase the entire access list. +.TP +.BR \-b [\f2rev\fP] +Set the default branch to +.IR rev . +If +.I rev +is omitted, the default +branch is reset to the (dynamically) highest branch on the trunk. +.TP +.BI \-c string +Set the comment leader to +.IR string . +An initial +.BR ci , +or an +.B "rcs\ \-i" +without +.BR \-c , +guesses the comment leader from the suffix of the working filename. +.RS +.PP +This option is obsolescent, since \*r normally uses the preceding +.B $\&Log$ +line's prefix when inserting log lines during checkout (see +.BR co (1)). +However, older versions of \*r use the comment leader instead of the +.B $\&Log$ +line's prefix, so +if you plan to access a file with both old and new versions of \*r, +make sure its comment leader matches its +.B $\&Log$ +line prefix. +.RE +.TP +.BI \-k subst +Set the default keyword substitution to +.IR subst . +The effect of keyword substitution is described in +.BR co (1). +Giving an explicit +.B \-k +option to +.BR co , +.BR rcsdiff , +and +.B rcsmerge +overrides this default. +Beware +.BR "rcs\ \-kv", +because +.B \-kv +is incompatible with +.BR "co\ \-l". +Use +.B "rcs\ \-kkv" +to restore the normal default keyword substitution. +.TP +.BR \-l [\f2rev\fP] +Lock the revision with number +.IR rev . +If a branch is given, lock the latest revision on that branch. +If +.I rev +is omitted, lock the latest revision on the default branch. +Locking prevents overlapping changes. +If someone else already holds the lock, the lock is broken as with +.B "rcs\ \-u" +(see below). +.TP +.BR \-u [\f2rev\fP] +Unlock the revision with number +.IR rev . +If a branch is given, unlock the latest revision on that branch. +If +.I rev +is omitted, remove the latest lock held by the caller. +Normally, only the locker of a revision can unlock it. +Somebody else unlocking a revision breaks the lock. +This causes a mail message to be sent to the original locker. +The message contains a commentary solicited from the breaker. +The commentary is terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +.TP +.B \-L +Set locking to +.IR strict . +Strict locking means that the owner +of an \*r file is not exempt from locking for checkin. +This option should be used for files that are shared. +.TP +.B \-U +Set locking to non-strict. Non-strict locking means that the owner of +a file need not lock a revision for checkin. +This option should +.I not +be used for files that are shared. +Whether default locking is strict is determined by your system administrator, +but it is normally strict. +.TP +\f3\-m\fP\f2rev\fP\f3:\fP\f2msg\fP +Replace revision +.IR rev 's +log message with +.IR msg . +.TP +.B \-M +Do not send mail when breaking somebody else's lock. +This option is not meant for casual use; +it is meant for programs that warn users by other means, and invoke +.B "rcs\ \-u" +only as a low-level lock-breaking operation. +.TP +\f3\-n\fP\f2name\fP[\f3:\fP[\f2rev\fP]] +Associate the symbolic name +.I name +with the branch or +revision +.IR rev . +Delete the symbolic name if both +.B : +and +.I rev +are omitted; otherwise, print an error message if +.I name +is already associated with +another number. +If +.I rev +is symbolic, it is expanded before association. +A +.I rev +consisting of a branch number followed by a +.B .\& +stands for the current latest revision in the branch. +A +.B : +with an empty +.I rev +stands for the current latest revision on the default branch, +normally the trunk. +For example, +.BI "rcs\ \-n" name ":\ RCS/*" +associates +.I name +with the current latest revision of all the named \*r files; +this contrasts with +.BI "rcs\ \-n" name ":$\ RCS/*" +which associates +.I name +with the revision numbers extracted from keyword strings +in the corresponding working files. +.TP +\f3\-N\fP\f2name\fP[\f3:\fP[\f2rev\fP]] +Act like +.BR \-n , +except override any previous assignment of +.IR name . +.TP +.BI \-o range +deletes (\*(lqoutdates\*(rq) the revisions given by +.IR range . +A range consisting of a single revision number means that revision. +A range consisting of a branch number means the latest revision on that +branch. +A range of the form +.IB rev1 : rev2 +means +revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI : rev +means from the beginning of the branch containing +.I rev +up to and including +.IR rev , +and +.IB rev : +means +from revision +.I rev +to the end of the branch containing +.IR rev . +None of the outdated revisions can have branches or locks. +.TP +.B \-q +Run quietly; do not print diagnostics. +.TP +.B \-I +Run interactively, even if the standard input is not a terminal. +.TP +.B \-s\f2state\fP\f1[\fP:\f2rev\fP\f1]\fP +Set the state attribute of the revision +.I rev +to +.IR state . +If +.I rev +is a branch number, assume the latest revision on that branch. +If +.I rev +is omitted, assume the latest revision on the default branch. +Any identifier is acceptable for +.IR state . +A useful set of states +is +.B Exp +(for experimental), +.B Stab +(for stable), and +.B Rel +(for +released). +By default, +.BR ci (1) +sets the state of a revision to +.BR Exp . +.TP +.BR \-t [\f2file\fP] +Write descriptive text from the contents of the named +.I file +into the \*r file, deleting the existing text. +The +.IR file +pathname cannot begin with +.BR \- . +If +.I file +is omitted, obtain the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +Prompt for the text if interaction is possible; see +.BR \-I . +With +.BR \-i , +descriptive text is obtained +even if +.B \-t +is not given. +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.TP +.B \-T +Preserve the modification time on the \*r file +unless a revision is removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when a change to the \*r file +would mean a change to keyword strings in the working file. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the default time zone. +This option has no effect; +it is present for compatibility with other \*r commands. +.PP +At least one explicit option must be given, +to ensure compatibility with future planned extensions +to the +.B rcs +command. +.SH COMPATIBILITY +The +.BI \-b rev +option generates an \*r file that cannot be parsed by \*r version 3 or earlier. +.PP +The +.BI \-k subst +options (except +.BR \-kkv ) +generate an \*r file that cannot be parsed by \*r version 4 or earlier. +.PP +Use +.BI "rcs \-V" n +to make an \*r file acceptable to \*r version +.I n +by discarding information that would confuse version +.IR n . +.PP +\*r version 5.5 and earlier does not support the +.B \-x +option, and requires a +.B ,v +suffix on an \*r pathname. +.SH FILES +.B rcs +accesses files much as +.BR ci (1) +does, +except that it uses the effective user for all accesses, +it does not write the working file or its directory, +and it does not even read the working file unless a revision number of +.B $ +is specified. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The \*r pathname and the revisions outdated are written to +the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +rcsintro(1), co(1), ci(1), ident(1), rcsclean(1), rcsdiff(1), +rcsmerge(1), rlog(1), rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +A catastrophe (e.g. a system crash) can cause \*r to leave behind +a semaphore file that causes later invocations of \*r to claim +that the \*r file is in use. +To fix this, remove the semaphore file. +A semaphore file's name typically begins with +.B , +or ends with +.BR _ . +.PP +The separator for revision ranges in the +.B \-o +option used to be +.B \- +instead of +.BR : , +but this leads to confusion when symbolic names contain +.BR \- . +For backwards compatibility +.B "rcs \-o" +still supports the old +.B \- +separator, but it warns about this obsolete use. +.PP +Symbolic names need not refer to existing revisions or branches. +For example, the +.B \-o +option does not remove symbolic names for the outdated revisions; you must use +.B \-n +to remove the names. +.br diff --git a/gnu/usr.bin/rcs/man/rcsclean.1 b/gnu/usr.bin/rcs/man/rcsclean.1 new file mode 100644 index 00000000000..e1726fb866e --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsclean.1 @@ -0,0 +1,203 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsclean.1,v 1.1 1996/08/12 04:07:50 millert Exp $ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSCLEAN 1 \*(Dt GNU +.SH NAME +rcsclean \- clean up working files +.SH SYNOPSIS +.B rcsclean +.RI [ options "] [ " file " .\|.\|. ]" +.SH DESCRIPTION +.B rcsclean +removes files that are not being worked on. +.B "rcsclean \-u" +also unlocks and removes files that are being worked on +but have not changed. +.PP +For each +.I file +given, +.B rcsclean +compares the working file and a revision in the corresponding +\*r file. If it finds a difference, it does nothing. +Otherwise, it first unlocks the revision if the +.B \-u +option is given, +and then removes the working file +unless the working file is writable and the revision is locked. +It logs its actions by outputting the corresponding +.B "rcs \-u" +and +.B "rm \-f" +commands on the standard output. +.PP +Files are paired as explained in +.BR ci (1). +If no +.I file +is given, all working files in the current directory are cleaned. +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +.PP +The number of the revision to which the working file is compared +may be attached to any of the options +.BR \-n , +.BR \-q , +.BR \-r , +or +.BR \-u . +If no revision number is specified, then if the +.B \-u +option is given and the caller has one revision locked, +.B rcsclean +uses that revision; otherwise +.B rcsclean +uses the latest revision on the default branch, normally the root. +.PP +.B rcsclean +is useful for +.B clean +targets in makefiles. +See also +.BR rcsdiff (1), +which prints out the differences, +and +.BR ci (1), +which +normally reverts to the previous revision +if a file was not changed. +.SH OPTIONS +.TP +.BI \-k subst +Use +.I subst +style keyword substitution when retrieving the revision for comparison. +See +.BR co (1) +for details. +.TP +.BR \-n [\f2rev\fP] +Do not actually remove any files or unlock any revisions. +Using this option will tell you what +.B rcsclean +would do without actually doing it. +.TP +.BR \-q [\f2rev\fP] +Do not log the actions taken on standard output. +.TP +.BR \-r [\f2rev\fP] +This option has no effect other than specifying the revision for comparison. +.TP +.B \-T +Preserve the modification time on the \*r file +even if the \*r file changes because a lock is removed. +This option can suppress extensive recompilation caused by a +.BR make (1) +dependency of some other copy of the working file on the \*r file. +Use this option with care; it can suppress recompilation even when it is needed, +i.e. when the lock removal +would mean a change to keyword strings in the other working file. +.TP +.BR \-u [\f2rev\fP] +Unlock the revision if it is locked and no difference is found. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the time zone for keyword substitution; +see +.BR co (1) +for details. +.SH EXAMPLES +.LP +.RS +.ft 3 +rcsclean *.c *.h +.ft +.RE +.LP +removes all working files ending in +.B .c +or +.B .h +that were not changed +since their checkout. +.LP +.RS +.ft 3 +rcsclean +.ft +.RE +.LP +removes all working files in the current directory +that were not changed since their checkout. +.SH FILES +.B rcsclean +accesses files much as +.BR ci (1) +does. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +A backslash escapes spaces within an option. +The +.B \s-1RCSINIT\s0 +options are prepended to the argument lists of most \*r commands. +Useful +.B \s-1RCSINIT\s0 +options include +.BR \-q , +.BR \-V , +.BR \-x , +and +.BR \-z . +.SH DIAGNOSTICS +The exit status is zero if and only if all operations were successful. +Missing working files and \*r files are silently ignored. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +At least one +.I file +must be given in older Unix versions that +do not provide the needed directory scanning operations. +.br diff --git a/gnu/usr.bin/rcs/man/rcsdiff.1 b/gnu/usr.bin/rcs/man/rcsdiff.1 new file mode 100644 index 00000000000..c9dd4ed7c59 --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsdiff.1 @@ -0,0 +1,158 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsdiff.1,v 1.1 1996/08/12 04:07:51 millert Exp $ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSDIFF 1 \*(Dt GNU +.SH NAME +rcsdiff \- compare RCS revisions +.SH SYNOPSIS +.B rcsdiff +[ +.BI \-k subst +] [ +.B \-q +] [ +.BI \-r rev1 +[ +.BI \-r rev2 +] ] [ +.B \-T +] [ +.RI "\f3\-V\fP[" n ] +] [ +.BI \-x suffixes +] [ +.BI \-z zone +] [ +.I "diff options" +] +.I "file .\|.\|." +.SH DESCRIPTION +.B rcsdiff +runs +.BR diff (1) +to compare two revisions of each \*r file given. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +The option +.B \-q +suppresses diagnostic output. +Zero, one, or two revisions may be specified with +.BR \-r . +The option +.BI \-k subst +affects keyword substitution when extracting +revisions, as described in +.BR co (1); +for example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when comparing revisions +.B 1.1 +and +.BR 1.2 . +To avoid excess output from locker name substitution, +.B \-kkvl +is assumed if (1) at most one revision option is given, +(2) no +.B \-k +option is given, (3) +.B \-kkv +is the default keyword substitution, and +(4) the working file's mode would be produced by +.BR "co\ \-l". +See +.BR co (1) +for details +about +.BR \-T , +.BR \-V , +.B \-x +and +.BR \-z . +Otherwise, all options of +.BR diff (1) +that apply to regular files are accepted, with the same meaning as for +.BR diff . +.PP +If both +.I rev1 +and +.I rev2 +are omitted, +.B rcsdiff +compares the latest revision on the +default branch (by default the trunk) +with the contents of the corresponding working file. This is useful +for determining what you changed since the last checkin. +.PP +If +.I rev1 +is given, but +.I rev2 +is omitted, +.B rcsdiff +compares revision +.I rev1 +of the \*r file with +the contents of the corresponding working file. +.PP +If both +.I rev1 +and +.I rev2 +are given, +.B rcsdiff +compares revisions +.I rev1 +and +.I rev2 +of the \*r file. +.PP +Both +.I rev1 +and +.I rev2 +may be given numerically or symbolically. +.SH EXAMPLE +The command +.LP +.B " rcsdiff f.c" +.LP +compares the latest revision on the default branch of the \*r file +to the contents of the working file +.BR f.c . +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +Exit status is 0 for no differences during any comparison, +1 for some differences, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/man/rcsfile.5 b/gnu/usr.bin/rcs/man/rcsfile.5 new file mode 100644 index 00000000000..4499c64eacb --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsfile.5 @@ -0,0 +1,426 @@ +.lf 1 ./rcsfile.5in +.\" Set p to 1 if your formatter can handle pic output. +.if t .nr p 1 +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsfile.5,v 1.1 1996/08/12 04:07:52 millert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSFILE 5 \*(Dt GNU +.SH NAME +rcsfile \- format of RCS file +.SH DESCRIPTION +An \*r file's +contents are described by the grammar +below. +.PP +The text is free format: space, backspace, tab, newline, vertical +tab, form feed, and carriage return (collectively, +.IR "white space") +have no significance except in strings. +However, white space cannot appear within an id, num, or sym, +and an \*r file must end with a newline. +.PP +Strings are enclosed by +.BR @ . +If a string contains a +.BR @ , +it must be doubled; +otherwise, strings can contain arbitrary binary data. +.PP +The meta syntax uses the following conventions: `|' (bar) separates +alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose +phrases that can be repeated zero or more times; +`{' and '}+' enclose phrases that must appear at least once and can be +repeated; +Terminal symbols are in +.BR boldface ; +nonterminal symbols are in +.IR italics . +.LP +.nr w \w'\f3deltatext\fP ' +.nr y \w'\f3newphrase\fP ' +.if \nw<\ny .nr w \ny +.nr x \w'\f3branches\fP' +.nr y \w'{ \f3comment\fP' +.if \nx<\ny .nr x \ny +.nr y \w'\f3{ branch\fP' +.if \nx<\ny .nr x \ny +.ta \nwu +\w'::= 'u +\nxu+\w' 'u +.fc # +.nf +\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}* +.LP +\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP + { \f3branch\fP {\f2num\fP}\f3;\fP } + \f3access\fP {\f2id\fP}*\f3;\fP + \f3symbols\fP {\f2sym\fP \f3:\fP \f2num\fP}*\f3;\fP + \f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP} + { \f3comment\fP {\f2string\fP}\f3;\fP } + { \f3expand\fP {\f2string\fP}\f3;\fP } + { \f2newphrase\fP }* +.LP +\f2delta\fP ::= \f2num\fP + \f3date\fP \f2num\fP\f3;\fP + \f3author\fP \f2id\fP\f3;\fP + \f3state\fP {\f2id\fP}\f3;\fP + \f3branches\fP {\f2num\fP}*\f3;\fP + \f3next\fP {\f2num\fP}\f3;\fP + { \f2newphrase\fP }* +.LP +\f2desc\fP ::= \f3desc\fP \f2string\fP +.LP +\f2deltatext\fP ::= \f2num\fP + \f3log\fP \f2string\fP + { \f2newphrase\fP }* + \f3text\fP \f2string\fP +.LP +\f2num\fP ::= {\f2digit\fP | \f3.\fP}+ +.LP +\f2digit\fP ::= \f30\fP | \f31\fP | \f32\fP | \f33\fP | \f34\fP | \f35\fP | \f36\fP | \f37\fP | \f38\fP | \f39\fP +.LP +\f2id\fP ::= {\f2num\fP} \f2idchar\fP {\f2idchar\fP | \f2num\fP}* +.LP +\f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}* +.LP +\f2idchar\fP ::= any visible graphic character except \f2special\fP +.LP +\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP +.LP +\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP +.LP +\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP +.LP +\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP +.fi +.PP +Identifiers are case sensitive. Keywords are in lower case only. +The sets of keywords and identifiers can overlap. +In most environments \*r uses the \s-1ISO\s0 8859/1 encoding: +visible graphic characters are codes 041\-176 and 240\-377, +and white space characters are codes 010\-015 and 040. +.PP +Dates, which appear after the +.B date +keyword, are of the form +\f2Y\fP\f3.\fP\f2mm\fP\f3.\fP\f2dd\fP\f3.\fP\f2hh\fP\f3.\fP\f2mm\fP\f3.\fP\f2ss\fP, +where +.I Y +is the year, +.I mm +the month (01\-12), +.I dd +the day (01\-31), +.I hh +the hour (00\-23), +.I mm +the minute (00\-59), +and +.I ss +the second (00\-60). +.I Y +contains just the last two digits of the year +for years from 1900 through 1999, +and all the digits of years thereafter. +Dates use the Gregorian calendar; times use UTC. +.PP +The +.I newphrase +productions in the grammar are reserved for future extensions +to the format of \*r files. +No +.I newphrase +will begin with any keyword already in use. +.PP +The +.I delta +nodes form a tree. All nodes whose numbers +consist of a single pair +(e.g., 2.3, 2.1, 1.3, etc.) +are on the trunk, and are linked through the +.B next +field in order of decreasing numbers. +The +.B head +field in the +.I admin +node points to the head of that sequence (i.e., contains +the highest pair). +The +.B branch +node in the admin node indicates the default +branch (or revision) for most \*r operations. +If empty, the default +branch is the highest branch on the trunk. +.PP +All +.I delta +nodes whose numbers consist of +.RI 2 n +fields +.RI ( n \(>=2) +(e.g., 3.1.1.1, 2.1.2.2, etc.) +are linked as follows. +All nodes whose first +.RI 2 n \-1 +number fields are identical are linked through the +.B next +field in order of increasing numbers. +For each such sequence, +the +.I delta +node whose number is identical to the first +.RI 2 n \-2 +number fields of the deltas on that sequence is called the branchpoint. +The +.B branches +field of a node contains a list of the +numbers of the first nodes of all sequences for which it is a branchpoint. +This list is ordered in increasing numbers. +.LP +The following diagram shows an example of an \*r file's organization. +.if !\np \{\ +.nf +.vs 12 +.ne 36 +.cs 1 20 +.eo + + Head + | + | + v / \ + --------- / \ + / \ / \ | | / \ / \ + / \ / \ | 2.1 | / \ / \ + / \ / \ | | / \ / \ +/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\ +--------- --------- --------- --------- ------------- + ^ ^ | ^ ^ + | | | | | + | | v | | + / \ | --------- / \ | + / \ | \ 1.3 / / \ | + / \ ---------\ / / \----------- +/1.2.1.1\ \ / /1.2.2.1\ +--------- \ / --------- + ^ | ^ + | | | + | v | + | --------- | + | \ 1.2 / | + ----------------------\ /--------- + \ / + \ / + | + | + v + --------- + \ 1.1 / + \ / + \ / + \ / + +.ec +.cs 1 +.vs +.fi +.\} +.if \np \{\ +.lf 232 +.PS 4.250i 3.812i +.\" -2.0625 -4.25 1.75 0 +.\" 0.000i 4.250i 3.812i 0.000i +.nr 00 \n(.u +.nf +.nr 0x 1 +\h'3.812i' +.sp -1 +.lf 242 +\h'2.062i-(\w'Head'u/2u)'\v'0.125i-(0v/2u)+0v+0.22m'Head +.sp -1 +\h'2.062i'\v'0.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'0.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'0.750i'\D'l-0.025i -0.100i' +.sp -1 +\h'1.688i'\v'1.250i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'1.250i'\D'l0.000i -0.500i' +.sp -1 +\h'2.438i'\v'0.750i'\D'l-0.750i 0.000i' +.sp -1 +\h'1.688i'\v'0.750i'\D'l0.000i 0.500i' +.sp -1 +.lf 244 +\h'2.062i-(\w'2.1'u/2u)'\v'1.000i-(0v/2u)+0v+0.22m'2.1 +.sp -1 +\h'2.062i'\v'1.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'1.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'1.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 246 +\h'2.062i-(\w'1.3'u/2u)'\v'2.000i-(1v/2u)+0v+0.22m'1.3 +.sp -1 +\h'2.062i'\v'2.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'1.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'1.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.875i'\v'2.000i'\D'~-0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'1.350i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'1.375i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 249 +\h'1.375i-(\w'1.3.1.1'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.3.1.1 +.sp -1 +\h'1.375i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.000i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'1.750i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.062i'\v'2.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'2.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'2.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 252 +\h'2.062i-(\w'1.2'u/2u)'\v'3.000i-(1v/2u)+0v+0.22m'1.2 +.sp -1 +\h'2.062i'\v'3.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'2.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'2.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'1.875i'\v'3.000i'\D'~-0.500i 0.000i -0.500i 0.000i -0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'0.350i'\v'2.600i'\D'l0.025i -0.100i' +.sp -1 +\h'0.375i'\v'2.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 255 +\h'0.375i-(\w'1.2.1.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.1.1 +.sp -1 +\h'0.375i'\v'2.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'0.000i'\v'2.500i'\D'l0.750i 0.000i' +.sp -1 +\h'0.750i'\v'2.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'0.375i'\v'2.000i'\D'l0.000i -0.500i' +.sp -1 +\h'0.350i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'0.375i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 257 +\h'0.375i-(\w'1.2.1.3'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.1.3 +.sp -1 +\h'0.375i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'0.000i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'0.750i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.250i'\v'3.000i'\D'~0.500i 0.000i 0.000i -0.500i' +.sp -1 +\h'2.725i'\v'2.600i'\D'l0.025i -0.100i' +.sp -1 +\h'2.750i'\v'2.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 261 +\h'2.750i-(\w'1.2.2.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.2.1 +.sp -1 +\h'2.750i'\v'2.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'2.375i'\v'2.500i'\D'l0.750i 0.000i' +.sp -1 +\h'3.125i'\v'2.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.938i'\v'2.250i'\D'~0.500i 0.000i 0.000i -0.500i 0.000i -0.500i' +.sp -1 +\h'3.413i'\v'1.350i'\D'l0.025i -0.100i' +.sp -1 +\h'3.438i'\v'1.250i'\D'l0.025i 0.100i' +.sp -1 +.lf 264 +\h'3.438i-(\w'\s-21.2.2.1.1.1\s0'u/2u)'\v'1.000i-(1v/2u)+1v+0.22m'\s-21.2.2.1.1.1\s0 +.sp -1 +\h'3.438i'\v'0.750i'\D'l-0.375i 0.500i' +.sp -1 +\h'3.062i'\v'1.250i'\D'l0.750i 0.000i' +.sp -1 +\h'3.812i'\v'1.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.750i'\v'2.000i'\D'l0.000i -0.500i' +.sp -1 +\h'2.725i'\v'1.600i'\D'l0.025i -0.100i' +.sp -1 +\h'2.750i'\v'1.500i'\D'l0.025i 0.100i' +.sp -1 +.lf 267 +\h'2.750i-(\w'1.2.2.2'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.2.2 +.sp -1 +\h'2.750i'\v'1.000i'\D'l-0.375i 0.500i' +.sp -1 +\h'2.375i'\v'1.500i'\D'l0.750i 0.000i' +.sp -1 +\h'3.125i'\v'1.500i'\D'l-0.375i -0.500i' +.sp -1 +\h'2.062i'\v'3.250i'\D'l0.000i 0.500i' +.sp -1 +\h'2.087i'\v'3.650i'\D'l-0.025i 0.100i' +.sp -1 +\h'2.062i'\v'3.750i'\D'l-0.025i -0.100i' +.sp -1 +.lf 270 +\h'2.062i-(\w'1.1'u/2u)'\v'4.000i-(1v/2u)+0v+0.22m'1.1 +.sp -1 +\h'2.062i'\v'4.250i'\D'l-0.375i -0.500i' +.sp -1 +\h'1.688i'\v'3.750i'\D'l0.750i 0.000i' +.sp -1 +\h'2.438i'\v'3.750i'\D'l-0.375i 0.500i' +.sp -1 +.sp 4.250i+1 +.if \n(00 .fi +.br +.nr 0x 0 +.lf 271 +.PE +.lf 272 +.\} +.PP +.SH IDENTIFICATION +.de VL +\\$2 +.. +Author: Walter F. Tichy, +Purdue University, West Lafayette, IN, 47907. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH SEE ALSO +rcsintro(1), ci(1), co(1), ident(1), rcs(1), rcsclean(1), rcsdiff(1), +rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/gnu/usr.bin/rcs/man/rcsfile.5in b/gnu/usr.bin/rcs/man/rcsfile.5in new file mode 100644 index 00000000000..9fc1e5f6167 --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsfile.5in @@ -0,0 +1,294 @@ +.\" Set p to 1 if your formatter can handle pic output. +.if t .nr p 1 +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsfile.5in,v 1.1 1996/08/12 04:07:53 millert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSFILE 5 \*(Dt GNU +.SH NAME +rcsfile \- format of RCS file +.SH DESCRIPTION +An \*r file's +contents are described by the grammar +below. +.PP +The text is free format: space, backspace, tab, newline, vertical +tab, form feed, and carriage return (collectively, +.IR "white space") +have no significance except in strings. +However, white space cannot appear within an id, num, or sym, +and an \*r file must end with a newline. +.PP +Strings are enclosed by +.BR @ . +If a string contains a +.BR @ , +it must be doubled; +otherwise, strings can contain arbitrary binary data. +.PP +The meta syntax uses the following conventions: `|' (bar) separates +alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose +phrases that can be repeated zero or more times; +`{' and '}+' enclose phrases that must appear at least once and can be +repeated; +Terminal symbols are in +.BR boldface ; +nonterminal symbols are in +.IR italics . +.LP +.nr w \w'\f3deltatext\fP ' +.nr y \w'\f3newphrase\fP ' +.if \nw<\ny .nr w \ny +.nr x \w'\f3branches\fP' +.nr y \w'{ \f3comment\fP' +.if \nx<\ny .nr x \ny +.nr y \w'\f3{ branch\fP' +.if \nx<\ny .nr x \ny +.ta \nwu +\w'::= 'u +\nxu+\w' 'u +.fc # +.nf +\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}* +.LP +\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP + { \f3branch\fP {\f2num\fP}\f3;\fP } + \f3access\fP {\f2id\fP}*\f3;\fP + \f3symbols\fP {\f2sym\fP \f3:\fP \f2num\fP}*\f3;\fP + \f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP} + { \f3comment\fP {\f2string\fP}\f3;\fP } + { \f3expand\fP {\f2string\fP}\f3;\fP } + { \f2newphrase\fP }* +.LP +\f2delta\fP ::= \f2num\fP + \f3date\fP \f2num\fP\f3;\fP + \f3author\fP \f2id\fP\f3;\fP + \f3state\fP {\f2id\fP}\f3;\fP + \f3branches\fP {\f2num\fP}*\f3;\fP + \f3next\fP {\f2num\fP}\f3;\fP + { \f2newphrase\fP }* +.LP +\f2desc\fP ::= \f3desc\fP \f2string\fP +.LP +\f2deltatext\fP ::= \f2num\fP + \f3log\fP \f2string\fP + { \f2newphrase\fP }* + \f3text\fP \f2string\fP +.LP +\f2num\fP ::= {\f2digit\fP | \f3.\fP}+ +.LP +\f2digit\fP ::= \f30\fP | \f31\fP | \f32\fP | \f33\fP | \f34\fP | \f35\fP | \f36\fP | \f37\fP | \f38\fP | \f39\fP +.LP +\f2id\fP ::= {\f2num\fP} \f2idchar\fP {\f2idchar\fP | \f2num\fP}* +.LP +\f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}* +.LP +\f2idchar\fP ::= any visible graphic character except \f2special\fP +.LP +\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP +.LP +\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP +.LP +\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP +.LP +\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP +.fi +.PP +Identifiers are case sensitive. Keywords are in lower case only. +The sets of keywords and identifiers can overlap. +In most environments \*r uses the \s-1ISO\s0 8859/1 encoding: +visible graphic characters are codes 041\-176 and 240\-377, +and white space characters are codes 010\-015 and 040. +.PP +Dates, which appear after the +.B date +keyword, are of the form +\f2Y\fP\f3.\fP\f2mm\fP\f3.\fP\f2dd\fP\f3.\fP\f2hh\fP\f3.\fP\f2mm\fP\f3.\fP\f2ss\fP, +where +.I Y +is the year, +.I mm +the month (01\-12), +.I dd +the day (01\-31), +.I hh +the hour (00\-23), +.I mm +the minute (00\-59), +and +.I ss +the second (00\-60). +.I Y +contains just the last two digits of the year +for years from 1900 through 1999, +and all the digits of years thereafter. +Dates use the Gregorian calendar; times use UTC. +.PP +The +.I newphrase +productions in the grammar are reserved for future extensions +to the format of \*r files. +No +.I newphrase +will begin with any keyword already in use. +.PP +The +.I delta +nodes form a tree. All nodes whose numbers +consist of a single pair +(e.g., 2.3, 2.1, 1.3, etc.) +are on the trunk, and are linked through the +.B next +field in order of decreasing numbers. +The +.B head +field in the +.I admin +node points to the head of that sequence (i.e., contains +the highest pair). +The +.B branch +node in the admin node indicates the default +branch (or revision) for most \*r operations. +If empty, the default +branch is the highest branch on the trunk. +.PP +All +.I delta +nodes whose numbers consist of +.RI 2 n +fields +.RI ( n \(>=2) +(e.g., 3.1.1.1, 2.1.2.2, etc.) +are linked as follows. +All nodes whose first +.RI 2 n \-1 +number fields are identical are linked through the +.B next +field in order of increasing numbers. +For each such sequence, +the +.I delta +node whose number is identical to the first +.RI 2 n \-2 +number fields of the deltas on that sequence is called the branchpoint. +The +.B branches +field of a node contains a list of the +numbers of the first nodes of all sequences for which it is a branchpoint. +This list is ordered in increasing numbers. +.LP +The following diagram shows an example of an \*r file's organization. +.if !\np \{\ +.nf +.vs 12 +.ne 36 +.cs 1 20 +.eo + + Head + | + | + v / \ + --------- / \ + / \ / \ | | / \ / \ + / \ / \ | 2.1 | / \ / \ + / \ / \ | | / \ / \ +/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\ +--------- --------- --------- --------- ------------- + ^ ^ | ^ ^ + | | | | | + | | v | | + / \ | --------- / \ | + / \ | \ 1.3 / / \ | + / \ ---------\ / / \----------- +/1.2.1.1\ \ / /1.2.2.1\ +--------- \ / --------- + ^ | ^ + | | | + | v | + | --------- | + | \ 1.2 / | + ----------------------\ /--------- + \ / + \ / + | + | + v + --------- + \ 1.1 / + \ / + \ / + \ / + +.ec +.cs 1 +.vs +.fi +.\} +.if \np \{\ +.PS +define triangle_down ' + box invis $1 "" + { line from last box.s to last box.nw to last box.ne to last box.s } +' +define triangle_up ' + box invis "" $1 + { line from last box.n to last box.sw to last box.se to last box.n } +' + down + box invis "Head" height boxht/2 + arrow + box "2.1" + arrow + triangle_down("1.3") + { + spline -> from 1/2 between last box.nw and last box.s left then up + triangle_up("1.3.1.1") + } + arrow +B12: triangle_down("1.2") + { + spline -> from 1/2 between B12.nw and B12.s left then left then left then up + triangle_up("1.2.1.1") + arrow + triangle_up("1.2.1.3") + } + { + spline -> from 1/2 between B12.ne and B12.s right then up + triangle_up("1.2.2.1") + { + spline -> from 1/2 between last box.se and last box.n right then up then up + triangle_up("\s-21.2.2.1.1.1\s0") + } + arrow + triangle_up("1.2.2.2") + } + arrow + triangle_down("1.1") +.PE +.\} +.PP +.SH IDENTIFICATION +.de VL +\\$2 +.. +Author: Walter F. Tichy, +Purdue University, West Lafayette, IN, 47907. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH SEE ALSO +rcsintro(1), ci(1), co(1), ident(1), rcs(1), rcsclean(1), rcsdiff(1), +rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/gnu/usr.bin/rcs/man/rcsfreeze.1 b/gnu/usr.bin/rcs/man/rcsfreeze.1 new file mode 100644 index 00000000000..c8a44398617 --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsfreeze.1 @@ -0,0 +1,68 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsfreeze.1,v 1.1 1996/08/12 04:07:54 millert Exp $ +.ds r \s-1RCS\s0 +.TH RCSFREEZE 1 \*(Dt GNU +.SH NAME +rcsfreeze \- freeze a configuration of sources checked in under RCS +.SH SYNOPSIS +.B rcsfreeze +.RI [ "name" ] +.SH DESCRIPTION +.B rcsfreeze +assigns a symbolic revision +number to a set of \*r files that form a valid configuration. +.PP +The idea is to run +.B rcsfreeze +each time a new version is checked +in. A unique symbolic name (\c +.BI C_ number, +where +.I number +is increased each time +.B rcsfreeze +is run) is then assigned to the most +recent revision of each \*r file of the main trunk. +.PP +An optional +.I name +argument to +.B rcsfreeze +gives a symbolic name to the configuration. +The unique identifier is still generated +and is listed in the log file but it will not appear as +part of the symbolic revision name in the actual \*r files. +.PP +A log message is requested from the user for future reference. +.PP +The shell script works only on all \*r files at one time. +All changed files must be checked in already. +Run +.IR rcsclean (1) +first and see whether any sources remain in the current directory. +.SH FILES +.TP +.B RCS/.rcsfreeze.ver +version number +.TP +.B RCS/.rcsfreeze.log +log messages, most recent first +.SH AUTHOR +Stephan v. Bechtolsheim +.SH "SEE ALSO" +co(1), rcs(1), rcsclean(1), rlog(1) +.SH BUGS +.B rcsfreeze +does not check whether any sources are checked out and modified. +.PP +Although both source file names and RCS file names are accepted, +they are not paired as usual with RCS commands. +.PP +Error checking is rudimentary. +.PP +.B rcsfreeze +is just an optional example shell script, and should not be taken too seriously. +See \s-1CVS\s0 for a more complete solution. diff --git a/gnu/usr.bin/rcs/man/rcsintro.1 b/gnu/usr.bin/rcs/man/rcsintro.1 new file mode 100644 index 00000000000..3f140b3314a --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsintro.1 @@ -0,0 +1,302 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsintro.1,v 1.1 1996/08/12 04:07:55 millert Exp $ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.if !\n(.g \{\ +. if !\w|\*(lq| \{\ +. ds lq `` +. if \w'\(lq' .ds lq "\(lq +. \} +. if !\w|\*(rq| \{\ +. ds rq '' +. if \w'\(rq' .ds rq "\(rq +. \} +.\} +.am SS +.LP +.. +.TH RCSINTRO 1 \*(Dt GNU +.SH NAME +rcsintro \- introduction to RCS commands +.SH DESCRIPTION +The Revision Control System (\*r) manages multiple revisions of files. +\*r automates the storing, retrieval, logging, identification, and merging +of revisions. \*r is useful for text that is revised frequently, for example +programs, documentation, graphics, papers, and form letters. +.PP +The basic user interface is extremely simple. The novice only needs +to learn two commands: +.BR ci (1) +and +.BR co (1). +.BR ci , +short for \*(lqcheck in\*(rq, deposits the contents of a +file into an archival file called an \*r file. An \*r file +contains all revisions of a particular file. +.BR co , +short for \*(lqcheck out\*(rq, retrieves revisions from an \*r file. +.SS "Functions of \*r" +.IP \(bu +Store and retrieve multiple revisions of text. \*r saves all old +revisions in a space efficient way. +Changes no longer destroy the original, because the +previous revisions remain accessible. Revisions can be retrieved according to +ranges of revision numbers, symbolic names, dates, authors, and +states. +.IP \(bu +Maintain a complete history of changes. +\*r logs all changes automatically. +Besides the text of each revision, \*r stores the author, the date and time of +check-in, and a log message summarizing the change. +The logging makes it easy to find out +what happened to a module, without having to compare +source listings or having to track down colleagues. +.IP \(bu +Resolve access conflicts. When two or more programmers wish to +modify the same revision, \*r alerts the programmers and prevents one +modification from corrupting the other. +.IP \(bu +Maintain a tree of revisions. \*r can maintain separate lines of development +for each module. It stores a tree structure that represents the +ancestral relationships among revisions. +.IP \(bu +Merge revisions and resolve conflicts. +Two separate lines of development of a module can be coalesced by merging. +If the revisions to be merged affect the same sections of code, \*r alerts the +user about the overlapping changes. +.IP \(bu +Control releases and configurations. +Revisions can be assigned symbolic names +and marked as released, stable, experimental, etc. +With these facilities, configurations of modules can be +described simply and directly. +.IP \(bu +Automatically identify each revision with name, revision number, +creation time, author, etc. +The identification is like a stamp that can be embedded at an appropriate place +in the text of a revision. +The identification makes it simple to determine which +revisions of which modules make up a given configuration. +.IP \(bu +Minimize secondary storage. \*r needs little extra space for +the revisions (only the differences). If intermediate revisions are +deleted, the corresponding deltas are compressed accordingly. +.SS "Getting Started with \*r" +Suppose you have a file +.B f.c +that you wish to put under control of \*r. +If you have not already done so, make an \*r directory with the command +.IP +.B "mkdir RCS" +.LP +Then invoke the check-in command +.IP +.B "ci f.c" +.LP +This command creates an \*r file in the +.B RCS +directory, +stores +.B f.c +into it as revision 1.1, and +deletes +.BR f.c . +It also asks you for a description. The description +should be a synopsis of the contents of the file. All later check-in +commands will ask you for a log entry, which should summarize the +changes that you made. +.PP +Files in the \*r directory are called \*r files; +the others are called working files. +To get back the working file +.B f.c +in the previous example, use the check-out +command +.IP +.B "co f.c" +.LP +This command extracts the latest revision from the \*r file +and writes +it into +.BR f.c . +If you want to edit +.BR f.c , +you must lock it as you check it out with the command +.IP +.B "co \-l f.c" +.LP +You can now edit +.BR f.c . +.PP +Suppose after some editing you want to know what changes that you have made. +The command +.IP +.B "rcsdiff f.c" +.LP +tells you the difference between the most recently checked-in version +and the working file. +You can check the file back in by invoking +.IP +.B "ci f.c" +.LP +This increments the revision number properly. +.PP +If +.B ci +complains with the message +.IP +.BI "ci error: no lock set by " "your name" +.LP +then you have tried to check in a file even though you did not +lock it when you checked it out. +Of course, it is too late now to do the check-out with locking, because +another check-out would +overwrite your modifications. Instead, invoke +.IP +.B "rcs \-l f.c" +.LP +This command will lock the latest revision for you, unless somebody +else got ahead of you already. In this case, you'll have to negotiate with +that person. +.PP +Locking assures that you, and only you, can check in the next update, and +avoids nasty problems if several people work on the same file. +Even if a revision is locked, it can still be checked out for +reading, compiling, etc. All that locking +prevents is a +.I "check-in" +by anybody but the locker. +.PP +If your \*r file is private, i.e., if you are the only person who is going +to deposit revisions into it, strict locking is not needed and you +can turn it off. +If strict locking is turned off, +the owner of the \*r file need not have a lock for check-in; all others +still do. Turning strict locking off and on is done with the commands +.IP +.BR "rcs \-U f.c" " and " "rcs \-L f.c" +.LP +If you don't want to clutter your working directory with \*r files, create +a subdirectory called +.B RCS +in your working directory, and move all your \*r +files there. \*r commands will look first into that directory to find +needed files. All the commands discussed above will still work, without any +modification. +(Actually, pairs of \*r and working files can be specified in three ways: +(a) both are given, (b) only the working file is given, (c) only the +\*r file is given. Both \*r and working files may have arbitrary path prefixes; +\*r commands pair them up intelligently.) +.PP +To avoid the deletion of the working file during check-in (in case you want to +continue editing or compiling), invoke +.IP +.BR "ci \-l f.c" " or " "ci \-u f.c" +.LP +These commands check in +.B f.c +as usual, but perform an implicit +check-out. The first form also locks the checked in revision, the second one +doesn't. Thus, these options save you one check-out operation. +The first form is useful if you want to continue editing, +the second one if you just want to read the file. +Both update the identification markers in your working file (see below). +.PP +You can give +.B ci +the number you want assigned to a checked in +revision. Assume all your revisions were numbered 1.1, 1.2, 1.3, etc., +and you would like to start release 2. +The command +.IP +.BR "ci \-r2 f.c" " or " "ci \-r2.1 f.c" +.LP +assigns the number 2.1 to the new revision. +From then on, +.B ci +will number the subsequent revisions +with 2.2, 2.3, etc. The corresponding +.B co +commands +.IP +.BR "co \-r2 f.c" " and " "co \-r2.1 f.c" +.PP +retrieve the latest revision numbered +.RI 2. x +and the revision 2.1, +respectively. +.B co +without a revision number selects +the latest revision on the +.IR trunk , +i.e. the highest +revision with a number consisting of two fields. Numbers with more than two +fields are needed for branches. +For example, to start a branch at revision 1.3, invoke +.IP +.B "ci \-r1.3.1 f.c" +.LP +This command starts a branch numbered 1 at revision 1.3, and assigns +the number 1.3.1.1 to the new revision. For more information about +branches, see +.BR rcsfile (5). +.SS "Automatic Identification" +\*r can put special strings for identification into your source and object +code. To obtain such identification, place the marker +.IP +.B "$\&Id$" +.LP +into your text, for instance inside a comment. +\*r will replace this marker with a string of the form +.IP +.BI $\&Id: " filename revision date time author state " $ +.LP +With such a marker on the first page of each module, you can +always see with which revision you are working. +\*r keeps the markers up to date automatically. +To propagate the markers into your object code, simply put +them into literal character strings. In C, this is done as follows: +.IP +.ft 3 +static char rcsid[] = \&"$\&Id$\&"; +.ft +.LP +The command +.B ident +extracts such markers from any file, even object code +and dumps. +Thus, +.B ident +lets you find out +which revisions of which modules were used in a given program. +.PP +You may also find it useful to put the marker +.B $\&Log$ +into your text, inside a comment. This marker accumulates +the log messages that are requested during check-in. +Thus, you can maintain the complete history of your file directly inside it. +There are several additional identification markers; see +.BR co (1) +for +details. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/man/rcsmerge.1 b/gnu/usr.bin/rcs/man/rcsmerge.1 new file mode 100644 index 00000000000..a858aac31d8 --- /dev/null +++ b/gnu/usr.bin/rcs/man/rcsmerge.1 @@ -0,0 +1,189 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsmerge.1,v 1.1 1996/08/12 04:07:56 millert Exp $ +.ds r \&\s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSMERGE 1 \*(Dt GNU +.SH NAME +rcsmerge \- merge RCS revisions +.SH SYNOPSIS +.B rcsmerge +.RI [ options ] " file" +.SH DESCRIPTION +.B rcsmerge +incorporates the changes between two revisions +of an \*r file into the corresponding working file. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +At least one revision must be specified with one of the options +described below, usually +.BR \-r . +At most two revisions may be specified. +If only one revision is specified, the latest +revision on the default branch (normally the highest branch on the trunk) +is assumed for the second revision. +Revisions may be specified numerically or symbolically. +.PP +.B rcsmerge +prints a warning if there are overlaps, and delimits +the overlapping regions as explained in +.BR merge (1). +The command is useful for incorporating changes into a checked-out revision. +.SH OPTIONS +.TP +.B \-A +Output conflicts using the +.B \-A +style of +.BR diff3 (1), +if supported by +.BR diff3 . +This merges all changes leading from +.I file2 +to +.I file3 +into +.IR file1 , +and generates the most verbose output. +.TP +\f3\-E\fP, \f3\-e\fP +These options specify conflict styles that generate less information +than +.BR \-A . +See +.BR diff3 (1) +for details. +The default is +.BR \-E . +With +.BR \-e , +.B rcsmerge +does not warn about conflicts. +.TP +.BI \-k subst +Use +.I subst +style keyword substitution. +See +.BR co (1) +for details. +For example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when merging the changes from +.B 1.1 +to +.BR 1.2 . +It normally does not make sense to merge binary files as if they were text, so +.B rcsmerge +refuses to merge files if +.B \-kb +expansion is used. +.TP +.BR \-p [\f2rev\fP] +Send the result to standard output instead of overwriting the working file. +.TP +.BR \-q [\f2rev\fP] +Run quietly; do not print diagnostics. +.TP +.BR \-r [\f2rev\fP] +Merge with respect to revision +.IR rev . +Here an empty +.I rev +stands for the latest revision on the default branch, normally the head. +.TP +.B \-T +This option has no effect; +it is present for compatibility with other \*r commands. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.TP +.BI \-z zone +Use +.I zone +as the time zone for keyword substitution. +See +.BR co (1) +for details. +.SH EXAMPLES +Suppose you have released revision 2.8 of +.BR f.c . +Assume +furthermore that after you complete an unreleased revision 3.4, you receive +updates to release 2.8 from someone else. +To combine the updates to 2.8 and your changes between 2.8 and 3.4, +put the updates to 2.8 into file f.c and execute +.LP +.B " rcsmerge \-p \-r2.8 \-r3.4 f.c >f.merged.c" +.PP +Then examine +.BR f.merged.c . +Alternatively, if you want to save the updates to 2.8 in the \*r file, +check them in as revision 2.8.1.1 and execute +.BR "co \-j": +.LP +.B " ci \-r2.8.1.1 f.c" +.br +.B " co \-r3.4 \-j2.8:2.8.1.1 f.c" +.PP +As another example, the following command undoes the changes +between revision 2.4 and 2.8 in your currently checked out revision +in +.BR f.c . +.LP +.B " rcsmerge \-r2.8 \-r2.4 f.c" +.PP +Note the order of the arguments, and that +.B f.c +will be +overwritten. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsintro(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.br diff --git a/gnu/usr.bin/rcs/man/rlog.1 b/gnu/usr.bin/rcs/man/rlog.1 new file mode 100644 index 00000000000..fac2a0bd66d --- /dev/null +++ b/gnu/usr.bin/rcs/man/rlog.1 @@ -0,0 +1,314 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rlog.1,v 1.1 1996/08/12 04:07:56 millert Exp $ +.ds i \&\s-1ISO\s0 +.ds r \&\s-1RCS\s0 +.ds u \&\s-1UTC\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RLOG 1 \*(Dt GNU +.SH NAME +rlog \- print log messages and other information about RCS files +.SH SYNOPSIS +.B rlog +.RI [ " options " ] " file " .\|.\|. +.SH DESCRIPTION +.B rlog +prints information about \*r files. +.PP +Pathnames matching an \*r suffix denote \*r files; +all others denote working files. +Names are paired as explained in +.BR ci (1). +.PP +.B rlog +prints the following information for each +\*r file: \*r pathname, working pathname, head (i.e., the number +of the latest revision on the trunk), default branch, access list, locks, +symbolic names, suffix, total number of revisions, +number of revisions selected for printing, and +descriptive text. This is followed by entries for the selected revisions in +reverse chronological order for each branch. For each revision, +.B rlog +prints revision number, author, date/time, state, number of +lines added/deleted (with respect to the previous revision), +locker of the revision (if any), and log message. +All times are displayed in Coordinated Universal Time (\*u) by default; +this can be overridden with +.BR \-z . +Without options, +.B rlog +prints complete information. +The options below restrict this output. +.nr n \w'\f3\-V\fP\f2n\fP'+2n-1/1n +.ds n \nn +.if \n(.g .if r an-tag-sep .ds n \w'\f3\-V\fP\f2n\fP'u+\n[an-tag-sep]u +.TP \*n +.B \-L +Ignore \*r files that have no locks set. +This is convenient in combination with +.BR \-h , +.BR \-l , +and +.BR \-R . +.TP +.B \-R +Print only the name of the \*r file. +This is convenient for translating a +working pathname into an \*r pathname. +.TP +.B \-h +Print only the \*r pathname, working pathname, head, +default branch, access list, locks, +symbolic names, and suffix. +.TP +.B \-t +Print the same as +.BR \-h , +plus the descriptive text. +.TP +.B \-N +Do not print the symbolic names. +.TP +.B \-b +Print information about the revisions on the default branch, normally +the highest branch on the trunk. +.TP +.BI \-d "dates" +Print information about revisions with a checkin date/time in the ranges given by +the semicolon-separated list of +.IR dates . +A range of the form +.IB d1 < d2 +or +.IB d2 > d1 +selects the revisions that were deposited between +.I d1 +and +.I d2 +exclusive. +A range of the form +.BI < d +or +.IB d > +selects +all revisions earlier than +.IR d . +A range of the form +.IB d < +or +.BI > d +selects +all revisions dated later than +.IR d . +If +.B < +or +.B > +is followed by +.B = +then the ranges are inclusive, not exclusive. +A range of the form +.I d +selects the single, latest revision dated +.I d +or earlier. +The date/time strings +.IR d , +.IR d1 , +and +.I d2 +are in the free format explained in +.BR co (1). +Quoting is normally necessary, especially for +.B < +and +.BR > . +Note that the separator is +a semicolon. +.TP +.BR \-l [\f2lockers\fP] +Print information about locked revisions only. +In addition, if the comma-separated list +.I lockers +of login names is given, +ignore all locks other than those held by the +.IR lockers . +For example, +.B "rlog\ \-L\ \-R\ \-lwft\ RCS/*" +prints the name of \*r files locked by the user +.BR wft . +.TP +.BR \-r [\f2revisions\fP] +prints information about revisions given in the comma-separated list +.I revisions +of revisions and ranges. +A range +.IB rev1 : rev2 +means revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI : rev +means revisions from the beginning of the branch up to and including +.IR rev , +and +.IB rev : +means revisions starting with +.I rev +to the end of the branch containing +.IR rev . +An argument that is a branch means all +revisions on that branch. +A range of branches means all revisions +on the branches in that range. +A branch followed by a +.B .\& +means the latest revision in that branch. +A bare +.B \-r +with no +.I revisions +means the latest revision on the default branch, normally the trunk. +.TP +.BI \-s states +prints information about revisions whose state attributes match one of the +states given in the comma-separated list +.IR states . +.TP +.BR \-w [\f2logins\fP] +prints information about revisions checked in by users with +login names appearing in the comma-separated list +.IR logins . +If +.I logins +is omitted, the user's login is assumed. +.TP +.B \-T +This option has no effect; +it is present for compatibility with other \*r commands. +.TP +.BI \-V +Print \*r's version number. +.TP +.BI \-V n +Emulate \*r version +.I n +when generating logs. +See +.BR co (1) +for more. +.TP +.BI \-x "suffixes" +Use +.I suffixes +to characterize \*r files. +See +.BR ci (1) +for details. +.PP +.B rlog +prints the intersection of the revisions selected with +the options +.BR \-d , +.BR \-l , +.BR \-s , +and +.BR \-w , +intersected +with the union of the revisions selected by +.B \-b +and +.BR \-r . +.TP +.BI \-z zone +specifies the date output format, +and specifies the default time zone for +.I date +in the +.BI \-d dates +option. +The +.I zone +should be empty, a numeric \*u offset, or the special string +.B LT +for local time. +The default is an empty +.IR zone , +which uses the traditional \*r format of \*u without any time zone indication +and with slashes separating the parts of the date; +otherwise, times are output in \*i 8601 format with time zone indication. +For example, if local time is January 11, 1990, 8pm Pacific Standard Time, +eight hours west of \*u, +then the time is output as follows: +.RS +.LP +.RS +.nf +.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u +.ne 4 +\f2option\fP \f2time output\fP +\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP +\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP +\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP +.ta 4n +4n +4n +4n +.fi +.RE +.SH EXAMPLES +.LP +.nf +.B " rlog \-L \-R RCS/*" +.B " rlog \-L \-h RCS/*" +.B " rlog \-L \-l RCS/*" +.B " rlog RCS/*" +.fi +.LP +The first command prints the names of all \*r files in the subdirectory +.B RCS +that have locks. The second command prints the headers of those files, +and the third prints the headers plus the log messages of the locked revisions. +The last command prints complete information. +.SH ENVIRONMENT +.TP +.B \s-1RCSINIT\s0 +options prepended to the argument list, separated by spaces. +See +.BR ci (1) +for details. +.SH DIAGNOSTICS +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Manual Page Revision: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. +.br +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +The separator for revision ranges in the +.B \-r +option used to be +.B \- +instead of +.BR : , +but this leads to confusion when symbolic names contain +.BR \- . +For backwards compatibility +.B "rlog \-r" +still supports the old +.B \- +separator, but it warns about this obsolete use. +.br diff --git a/gnu/usr.bin/rcs/mkinstalldirs b/gnu/usr.bin/rcs/mkinstalldirs new file mode 100644 index 00000000000..0801ec2c966 --- /dev/null +++ b/gnu/usr.bin/rcs/mkinstalldirs @@ -0,0 +1,32 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Last modified: 1994-03-25 +# Public domain + +errstatus=0 + +for file in ${1+"$@"} ; do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d in ${1+"$@"} ; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" || errstatus=$? + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/gnu/usr.bin/rcs/rcs.ms b/gnu/usr.bin/rcs/rcs.ms new file mode 100644 index 00000000000..861219f3f5c --- /dev/null +++ b/gnu/usr.bin/rcs/rcs.ms @@ -0,0 +1,1518 @@ +.\" Format this file with: +.\" pic file | tbl | troff -ms +.\" +.\" \*s stands for $, and avoids problems when this file is checked in. +.ds s $ +.de D( +.DS +.nr VS 12p +.vs 12p +.I +.. +.de D) +.DE +.nr VS 18p +.vs 18p +.R +.. +.de Id +.ND \\$4 +.. +.Id $Id: rcs.ms,v 1.1 1996/08/12 04:07:40 millert Exp $ +.RP +.TL +RCS\*-A System for Version Control +.sp +.AU +Walter F. Tichy +.AI +Department of Computer Sciences +Purdue University +West Lafayette, Indiana 47907 +.sp +.AB +An important problem in program development and maintenance is version control, +i.e., the task of keeping a software system consisting of many versions and +configurations well organized. +The Revision Control System (RCS) +is a software tool that assists with that task. +RCS manages revisions of text documents, in particular source programs, +documentation, and test data. +It automates the storing, retrieval, logging and identification of revisions, +and it provides selection mechanisms for composing configurations. +This paper introduces basic version control concepts and +discusses the practice of version control +using RCS. +For conserving space, RCS stores deltas, i.e., differences between +successive revisions. Several delta storage methods are discussed. +Usage statistics show that RCS's delta storage method is +space and time efficient. +The paper concludes with a detailed survey of version control tools. +.sp +\fBKeywords\fR: configuration management, history management, +version control, revisions, deltas. +.AE +.FS +An earlier version of this paper was published in +.I "Software\*-Practice & Experience" +.B 15 , +7 (July 1985), 637-654. +.FE +.nr VS 18p +.LP +.NH +Introduction +.PP +Version control is the task of keeping software +systems consisting of many versions and configurations well organized. +The Revision Control System (RCS) is a set of UNIX +commands that assist with that task. +.PP +RCS' primary function is to manage \fIrevision groups\fR. +A revision group is a set of text documents, called \fIrevisions\fR, +that evolved from each other. A new revision is +created by manually editing an existing one. +RCS organizes the revisions into an ancestral tree. The initial revision +is the root of the tree, and the tree edges indicate +from which revision a given one evolved. +Besides managing individual revision groups, RCS provides +flexible selection functions for composing configurations. +RCS may be combined with MAKE\u1\d, +resulting in a powerful package for version control. +.PP +RCS also offers facilities for +merging updates with customer modifications, +for distributed software development, and +for automatic identification. +Identification is the `stamping' +of revisions and configurations with unique markers. +These markers are akin to serial numbers, +telling software maintainers unambiguously which configuration +is before them. +.PP +RCS is designed for both production and experimental +environments. +In production environments, +access controls detect update conflicts and prevent overlapping changes. +In experimental environments, where strong controls are +counterproductive, it is possible to loosen the controls. +.PP +Although RCS was originally intended for programs, it is useful for any +text that is revised frequently and whose previous revisions must be +preserved. RCS has been applied successfully to store the source +text for drawings, VLSI layouts, documentation, specifications, +test data, form letters and articles. +.PP +This paper discusses the practice of +version control using RCS. +It also introduces basic version control concepts, +useful for clarifying current practice and designing similar systems. +Revision groups of individual components are treated in the next three sections, +and the extensions to configurations follow. +Because of its size, a survey of version control tools +appears at the end of the paper. +.NH +Getting started with RCS +.PP +Suppose a text file \fIf.c\fR is to be placed under control of RCS. +Invoking the check-in command +.D( +ci f.c +.D) +creates a new revision group with the contents of +\fIf.c\fR as the initial +revision (numbered 1.1) +and stores the group into the file \fIf.c,v\fR. +Unless told otherwise, the command deletes \fIf.c\fR. +It also asks for a description of the group. +The description should state the common purpose of all revisions in the group, +and becomes part of the group's documentation. +All later check-in commands will ask for a log entry, +which should summarize the changes made. +(The first revision is assigned a default log message, +which just records the fact that it is the initial revision.) +.PP +Files ending in \fI,v\fR +are called \fIRCS files\fR (\fIv\fR stands for \fIv\fRersions); +the others are called working files. +To get back the working file \fIf.c\fR in the previous example, +execute the check-out command: +.D( +co f.c +.D) +.R +This command extracts the latest revision from +the revision group \fIf.c,v\fR and writes +it into \fIf.c\fR. +The file \fIf.c\fR can now be edited and, when finished, +checked back in with \fIci\fR: +.D( +ci f.c +.D) +\fICi\fR assigns number 1.2 to +the new revision. +If \fIci\fR complains with the message +.D( +ci error: no lock set by <login> +.D) +then the system administrator has decided to configure RCS for a +production environment by enabling the `strict locking feature'. +If this feature is enabled, all RCS files are initialized +such that check-in operations require a lock on the previous revision +(the one from which the current one evolved). +Locking prevents overlapping modifications if several people work on the same file. +If locking is required, the revision should +have been locked during the check-out by using +the option \fI\-l\fR: +.D( +co \-l f.c +.D) +Of course it is too late now for the check-out with locking, because +\fIf.c\fR has already been changed; checking out the file again +would overwrite the modifications. +(To prevent accidental overwrites, \fIco\fR senses the presence +of a working file and asks whether the user really intended to overwrite it. +The overwriting check-out is sometimes useful for +backing up to the previous revision.) +To be able to proceed with the check-in in the present case, first execute +.D( +rcs \-l f.c +.D) +This command retroactively locks the latest revision, unless someone +else locked it in the meantime. In this case, the two programmers +involved have to negotiate whose +modifications should take precedence. +.PP +If an RCS file is private, i.e., if only the owner of the file is expected +to deposit revisions into it, the strict locking feature is unnecessary and +may be disabled. +If strict locking is disabled, +the owner of the RCS file need not have a lock for check-in. +For safety reasons, all others +still do. Turning strict locking off and on is done with the commands: +.D( +rcs \-U f.c \fRand\fP rcs \-L f.c +.D) +These commands enable or disable the strict locking feature for each RCS file +individually. +The system administrator only decides whether strict locking is +enabled initially. +.PP +To reduce the clutter in a working directory, all RCS files can be moved +to a subdirectory with the name \fIRCS\fR. +RCS commands look first into that directory for RCS files. +All the commands presented above work +with the \fIRCS\fR subdirectory without change.\(dg +.FS \(dg +Pairs of RCS and working files can actually be specified in 3 ways: +a) both are given, b) only the working file is given, c) only the +RCS file is given. +If a pair is given, both files may have arbitrary path prefixes; +RCS commands pair them up intelligently. +.FE +.PP +It may be undesirable that \fIci\fR deletes the working file. +For instance, sometimes one would like to save the current revision, +but continue editing. +Invoking +.D( +ci \-l f.c +.D) +checks in \fIf.c\fR as usual, but performs an additional +check-out with locking afterwards. Thus, the working file does +not disappear after the check-in. +Similarly, the option +\fI\-u\fR does a check-in followed by a check-out without +locking. This option is useful if the file is needed for compilation after the check-in. +Both options update the identification markers in the working file +(see below). +.PP +Besides the operations \fIci\fR and \fIco\fR, RCS provides the following +commands: +.sp 0 +.nr VS 12p +.vs 12p +.TS +tab(%); +li l. +ident%extract identification markers +rcs%change RCS file attributes +rcsclean%remove unchanged working files (optional) +rcsdiff%compare revisions +rcsfreeze%record a configuration (optional) +rcsmerge%merge revisions +rlog%read log messages and other information in RCS files +.TE +A synopsis of these commands appears in the Appendix. +.NH 2 +Automatic Identification +.PP +RCS can stamp source and object code with special identification strings, +similar to product and serial numbers. +To obtain such identification, place the marker +.D( +\*sId\*s +.D) +into the text of a revision, for instance inside a comment. +The check-out operation will replace this marker with a string of the form +.D( +\*sId: filename revisionnumber date time author state locker \*s +.D) +This string need never be touched, because \fIco\fR keeps it +up to date automatically. +To propagate the marker into object code, simply put +it into a literal character string. In C, this is done as follows: +.D( +static char rcsid[] = \&"\*sId\*s\&"; +.D) +The command \fIident\fR extracts such markers from any file, in particular from +object code. +\fIIdent\fR helps to find out +which revisions of which modules were used in a given program. +It returns a complete and unambiguous component list, +from which a copy of the program can be reconstructed. +This facility is invaluable for program maintenance. +.PP +There are several additional identification markers, one for each component +of \*sId\*s. +The marker +.D( +\*sLog\*s +.D) +has a similar function. It accumulates +the log messages that are requested during check-in. +Thus, one can maintain the complete history of a revision directly inside it, +by enclosing it in a comment. +Figure 1 is an edited version of a log contained in revision 4.1 of +the file \fIci.c\fR. The log appears at the beginning of the file, +and makes it easy to determine what the recent modifications were. +.sp +.nr VS 12p +.vs 12p +.ne 18 +.nf +.in +0.5i +/* +.in +\w'/'u +* \*sLog: ci.c,v \*s +* Revision 4.1 1983/05/10 17:03:06 wft +* Added option \-d and \-w, and updated assignment of date, etc. to new delta. +* Added handling of default branches. +* +* Revision 3.9 1983/02/15 15:25:44 wft +* Added call to fastcopy() to copy remainder of RCS file. +* +* Revision 3.8 1983/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 1982/12/10 16:09:20 wft +* Corrected checking of return code from diff. +* An RCS file now inherits its mode during the first ci from the working file, +* except that write permission is removed. +*/ +.in 0 +.ce 1 +Figure 1. Log entries produced by the marker \*sLog\*s. +.fi +.nr VS 18p +.vs 18p +.sp 0 +.LP +Since revisions are stored in the form of differences, +each log message is +physically stored once, +independent of the number of revisions present. +Thus, the \*sLog\*s marker incurs negligible space overhead. +.NH +The RCS Revision Tree +.PP +RCS arranges revisions in an ancestral tree. +The \fIci\fR command builds this tree; the auxiliary command \fIrcs\fR +prunes it. +The tree has a root revision, normally numbered 1.1, and successive revisions +are numbered 1.2, 1.3, etc. The first field of a revision number +is called the \fIrelease number\fR and the second one +the \fIlevel number\fR. Unless given explicitly, +the \fIci\fR command assigns a new revision number +by incrementing the level number of the previous revision. +The release number must be incremented explicitly, using the +\fI\-r\fR option of \fIci\fR. +Assuming there are revisions 1.1, 1.2, and 1.3 in the RCS file f.c,v, the command +.D( +ci \-r2.1 f.c \fRor\fP ci \-r2 f.c +.D) +assigns the number 2.1 to the new revision. +Later check-ins without the \fI\-r\fR option will assign the numbers 2.2, 2.3, +and so on. +The release number should be incremented only at major transition points +in the development, for instance when a new release of a software product has +been completed. +.NH 2 +When are branches needed? +.PP +A young revision tree is slender: +It consists of only one branch, called the trunk. +As the tree ages, side branches may form. +Branches are needed in the following 4 situations. +.IP "\fITemporary fixes\fR" +.sp 0 +Suppose a tree has 5 revisions grouped in 2 releases, +as illustrated in Figure 2. +Revision 1.3, the last one of release 1, is in operation at customer sites, +while release 2 is in active development. +.ne 4 +.PS 4i +.ps -2 +box "1.1" +arrow +box "1.2" +arrow +box "1.3" +arrow +box "2.1" +arrow +box "2.2" +arrow dashed +.ps +2 +.PE +.ce 1 +Figure 2. A slender revision tree. +.sp 0 +Now imagine a customer requesting a fix of +a problem in revision 1.3, although actual development has moved on +to release 2. RCS does not permit an extra +revision to be spliced in between 1.3 and 2.1, since that would not reflect +the actual development history. Instead, create a branch +at revision 1.3, and check in the fix on that branch. +The first branch starting at 1.3 has number 1.3.1, and +the revisions on that branch are numbered 1.3.1.1, 1.3.1.2, etc. +The double numbering is needed to allow for another +branch at 1.3, say 1.3.2. +Revisions on the second branch would be numbered +1.3.2.1, 1.3.2.2, and so on. +The following steps create +branch 1.3.1 and add revision 1.3.1.1: +.sp 0 +.I +.nr VS 12p +.vs 12p +.TS +tab(%); +l l l. + %co \-r1.3 f.c% \*- check out revision 1.3 + %edit f.c% \*- change it + %ci \-r1.3.1 f.c% \*- check it in on branch 1.3.1 +.TE +.nr VS 18p +.vs 18p +.R +This sequence of commands transforms the tree of Figure 2 into +the one in Figure 3. +Note that it may be necessary to incorporate the differences +between 1.3 and 1.3.1.1 +into a revision at level 2. The operation \fIrcsmerge\fR automates this +process (see the Appendix). +.ne 7 +.PS 4i +.ps -2 + box "1.1" + arrow + box "1.2" + arrow +R13: box "1.3" + arrow +R21: box "2.1" + arrow +R22: box "2.2" + arrow dashed + line invis down from R21.s +RB1: box "1.3.1.1" + arrow dashed right from RB1.e + arrow from R13.s to RB1.w +.ps +2 +.PE +.ce 1 +Figure 3. A revision tree with one side branch +.sp +.IP "\fIDistributed development and customer modifications\fR" +.sp 0 +Assume a situation as in Figure 2, where revision 1.3 is in operation +at several customer sites, +while release 2 is in development. +Customer sites should use RCS to store the distributed software. +However, customer modifications should not be placed on the same branch +as the distributed source; instead, they should be placed on a side branch. +When the next software distribution arrives, +it should be appended to the trunk of +the customer's RCS file, and the customer +can then merge the local modifications back into the new release. +In the above example, a +customer's RCS file would contain the following tree, assuming +that the customer has received revision 1.3, added his local modifications +as revision 1.3.1.1, then received revision 2.4, and merged +2.4 and 1.3.1.1, resulting in 2.4.1.1. +.ne 7 +.PS 4i +.ps -2 +R13: box "1.3" + line invis +R21: box invis + line invis +R22: box invis + line invis +R24: box "2.4" + line invis +R25: box invis + line invis + arrow from R13.e to R24.w + line invis down from R21.s +RB1: box "1.3.1.1" + arrow from R13.s to RB1.w + right + line invis down from R25.s +RB2: box "2.4.1.1" + arrow from R24.s to RB2.w +.ps +2 +.PE +.ce 1 +Figure 4. A customer's revision tree with local modifications. +.sp 1 +This approach is actually practiced in the CSNET project, +where several universities and a company cooperate +in developing a national computer network. +.IP "\fIParallel development\fR" +.sp 0 +Sometimes it is desirable to explore an alternate design or +a different implementation technique in parallel with the +main line development. Such development +should be carried out on a side branch. +The experimental changes may later be moved into the main line, or abandoned. +.IP "\fIConflicting updates\fR" +.sp 0 +A common occurrence is that one programmer +has checked out a revision, but cannot complete the assignment +for some reason. In the meantime, another person +must perform another modification +immediately. In that case, the second person should check-out the same revision, +modify it, and check it in on a side branch, for later merging. +.PP +Every node in a revision tree consists of the following attributes: +a revision number, a check-in date and time, the author's identification, +a log entry, a state and the actual text. All these attributes +are determined at the time the revision is checked in. +The state attribute indicates the status of a revision. +It is set automatically to `experimental' during check-in. +A revision can later be promoted to a higher status, for example +`stable' or `released'. The set of states is user-defined. +.NH 2 +Revisions are represented as deltas +.PP +For conserving space, RCS stores revisions in the form +of deltas, i.e., as differences between revisions. +The user interface completely hides this fact. +.PP +A delta is a sequence of edit commands that transforms one string +into another. The deltas employed by RCS are line-based, which means +that the only edit commands allowed are insertion and deletion of lines. +If a single character in a line is changed, the +edit scripts consider the entire line changed. +The program \fIdiff\fR\u2\d +produces a small, line-based delta between pairs of text files. +A character-based edit script would take much longer to compute, +and would not be significantly shorter. +.PP +Using deltas is a classical space-time tradeoff: deltas reduce the +space consumed, but increase access time. +However, a version control tool should impose as little delay +as possible on programmers. +Excessive delays discourage the use of version controls, +or induce programmers to take shortcuts that compromise system integrity. +To gain reasonably fast access time for both editing and compiling, +RCS arranges deltas in the following way. +The most recent revision on the trunk is stored intact. +All other revisions on the trunk are stored as reverse deltas. +A reverse delta describes how to go backward in the development history: +it produces the desired revision if applied to the successor of that revision. +This implementation has the advantage +that extraction of the latest revision is a simple and fast copy +operation. +Adding a new revision to the trunk is also fast: \fIci\fR simply +adds the new revision intact, replaces the previous +revision with a reverse delta, and keeps the rest of the old deltas. +Thus, \fIci\fR requires the computation +of only one new delta. +.PP +Branches need special treatment. The naive solution would be to +store complete copies for the tips of all branches. +Clearly, this approach would cost too much space. Instead, +RCS uses \fIforward\fR deltas for branches. Regenerating a revision +on a side branch proceeds as follows. First, extract the latest revision +on the trunk; secondly, apply reverse deltas until the fork revision for +the branch is obtained; thirdly, apply forward deltas until the desired +branch revision is reached. Figure 5 illustrates a tree with +one side branch. Triangles pointing to the left and right represent +reverse and forward deltas, respectively. +.ne 8 +.PS 4i +.ps -2 +define BD X [line invis $1 right .5; +line up .3 then left .5 down .3 then right .5 down .3 then up .3] X + +define FD X [line invis $1 right .5; +line left .5 down .3 then up .6 then right .5 down .3;] X + +right +D11: BD(" 1.1") + arrow right from D11.e +D12: BD(" 1.2") + arrow right from D12.e +D13: BD(" 1.3") + arrow right from D13.e +D21: BD(" 2.1") + arrow right from D21.e +D22: box "2.2" + line invis down from D21.s +F1: FD("1.3.1.1 ") + arrow from D13.se to F1.w + arrow from F1.e right + right +F2: FD("1.3.1.2 ") +.ps +2 +.PE +.ce 1 +Figure 5. A revision tree with reverse and forward deltas. +.sp 0 +.PP +Although implementing fast check-out for the latest trunk revision, +this arrangement has the disadvantage that generation of other revisions +takes time proportional to the number of deltas applied. For example, +regenerating the branch tip in Figure 5 requires application of five +deltas (including the initial one). Since usage statistics show that +the latest trunk revision is the one that is retrieved in 95 per cent +of all cases (see the section on usage statistics), biasing check-out time +in favor of that revision results in significant savings. +However, careful implementation of the delta application process is +necessary to provide low retrieval overhead for other revisions, in +particular for branch tips. +.PP +There are several techniques for delta application. +The naive one is to pass each delta to a general-purpose text editor. +A prototype of RCS invoked the UNIX editor \fIed\fR both +for applying deltas and for expanding the identification markers. +Although easy to implement, performance was poor, owing to the +high start-up costs and excess generality of \fIed\fR. An intermediate +version of RCS used a special-purpose, stream-oriented editor. +This technique reduced the cost of applying a delta to the cost of +checking out the latest trunk revision. The reason for this behavior +is that each delta application involves a complete pass over +the preceding revision. +.PP +However, there is a much better algorithm. Note that the deltas are +line oriented and that most of the work of a stream editor involves +copying unchanged lines from one revision to the next. A faster +algorithm avoids unnecessary copying of character strings by using +a \fIpiece table\fR. +A piece table is a one-dimensional array, specifying how a given +revision is `pieced together' from lines in the RCS file. +Suppose piece table \fIPT\dr\u\fR represents revision \fIr\fR. +Then \fIPT\dr\u[i]\fR contains the starting position of line \fIi\fR +of revision \fIr\fR. +Application of the next delta transforms piece table \fIPT\dr\u\fR +into \fIPT\dr+1\u\fR. For instance, a delete command removes a +series of entries from the piece table. An insertion command inserts +new entries, moving the entries following the insertion point further down the +array. The inserted entries point to the text lines in the delta. +Thus, no I/O is involved except for reading the delta itself. When all +deltas have been applied to the piece table, a sequential pass +through the table looks up each line in the RCS file and copies it to +the output file, updating identification markers at the same time. +Of course, the RCS file must permit random access, since the copied +lines are scattered throughout that file. Figure 6 illustrates an +RCS file with two revisions and the corresponding piece tables. +.ne 13 +.sp 6 +.ce 1 +\fIFigure 6 is not available.\fP +.sp 5 +.ce 1 +Figure 6. An RCS file and its piece tables +.sp 0 +.PP +The piece table approach has the property that the time for applying a single +delta is roughly determined by the size of the delta, and not by the +size of the revision. For example, if a delta is +10 per cent of the size of a revision, then applying it takes only +10 per cent of the time to generate the latest trunk revision. (The stream +editor would take 100 per cent.) +.PP +There is an important alternative for representing deltas that affects +performance. SCCS\u3\d, +a precursor of RCS, uses \fIinterleaved\fR deltas. +A file containing interleaved deltas is partitioned into blocks of lines. +Each block has a header that specifies to which revision(s) the block +belongs. The blocks are sorted out in such a way that a single +pass over the file can pick up all the lines belonging to a given +revision. Thus, the regeneration time for all revisions is the same: +all headers must be inspected, and the associated blocks either copied +or skipped. As the number of revisions increases, the cost of retrieving +any revision is much higher than the cost of checking out the +latest trunk revision with reverse deltas. A detailed comparison +of SCCS's interleaved deltas and RCS's reverse deltas can be found +in Reference 4. +This reference considers the version of RCS with the +stream editor only. The piece table method improves performance +further, so that RCS is always faster than SCCS, except if 10 +or more deltas are applied. +.PP +Additional speed-up for both delta methods can be obtained by caching +the most recently generated revision, as has been implemented in DSEE.\u5\d +With caching, access time to frequently used revisions can approach normal file +access time, at the cost of some additional space. +.NH +Locking: A Controversial Issue +.PP +The locking mechanism for RCS was difficult to design. +The problem and its solution are first presented in their `pure' form, +followed by a discussion of the complications +caused by `real-world' considerations. +.PP +RCS must prevent two or more persons from depositing competing changes of the +same revision. +Suppose two programmers check out revision 2.4 and +modify it. Programmer A checks in a revision before programmer B\&. +Unfortunately, programmer B has not seen A's +changes, so the effect is that A's changes are covered up by B's deposit. +A's changes are not lost since all revisions +are saved, but they are confined to a single revision.\(dd +.FS \(dd +Note that this problem is entirely different from the atomicity problem. +Atomicity means that +concurrent update operations on the same RCS file cannot be permitted, +because that may result in inconsistent data. +Atomic updates are essential (and implemented in RCS), +but do not solve the conflict discussed here. +.FE +.PP +This conflict is prevented in RCS by locking. +Whenever someone intends to edit a revision (as opposed +to reading or compiling it), the revision should be checked out +and locked, +using the \fI\-l\fR option on \fIco\fR. On subsequent check-in, +\fIci\fR tests the lock and then removes it. +At most one programmer at a time may +lock a particular revision, and only this programmer may check in +the succeeding revision. +Thus, while a revision is locked, it is the exclusive responsibility +of the locker. +.PP +An important maxim for software tools like RCS is that they must +not stand in the way of making progress with a project. +This consideration leads to several weakenings of the locking mechanism. +First of all, even if a revision is locked, it can +still be checked out. This is necessary if other people +wish to compile or inspect the locked revision +while the next one is in preparation. The only operations they +cannot do are to lock the revision or to check in the succeeding one. Secondly, +check-in operations on other branches in the RCS file are still possible; the +locking of one revision does not affect any other revision. +Thirdly, revisions are occasionally locked for a long period of time +because a programmer is absent or otherwise unable to complete +the assignment. If another programmer has to make a pressing change, +there are the following three alternatives for making progress: +a) find out who is holding the lock and ask that person to release it; +b) check out the locked revision, modify it, check it +in on a branch, and merge the changes later; +c) break the lock. Breaking a lock leaves a highly visible +trace, namely an electronic mail message that is sent automatically to the +holder of the lock, recording the breaker and a commentary requested from him. +Thus, breaking locks is tolerated under certain circumstances, +but will not go unnoticed. +Experience has shown that the automatic mail message attaches a high enough +stigma to lock breaking, +such that programmers break locks only in real emergencies, +or when a co-worker resigns and leaves locked revisions behind. +.PP +If an RCS file is private, i.e., when a programmer owns an RCS file +and does not expect anyone else to perform check-in operations, +locking is an unnecessary nuisance. +In this case, +the `strict locking feature' discussed earlier may be disabled, +provided that file protection +is set such that only the owner may write the RCS file. +This has the effect that only the owner can check-in revisions, +and that no lock is needed for doing so. +.PP +As added protection, +each RCS file contains an access list that specifies the users +who may execute update operations. If an access list is empty, +only normal UNIX file protection applies. Thus, the access list is +useful for restricting the set of people who would otherwise have update +permission. Just as with locking, the access list +has no effect on read-only operations such as \fIco\fR. This approach +is consistent with the UNIX philosophy of openness, which contributes +to a productive software development environment. +.NH +Configuration Management +.PP +The preceding sections described how RCS deals with revisions of individual +components; this section discusses how to handle configurations. +A configuration is a set of revisions, where each revision comes +from a different revision group, and the revisions are selected +according to a certain criterion. +For example, +in order to build a functioning compiler, the `right' +revisions from the scanner, the parser, the optimizer +and the code generator must be combined. +RCS, in conjunction with MAKE, +provides a number of facilities to effect a smooth selection. +.NH 2 +RCS Selection Functions +.PP +.IP "\fIDefault selection\fR" +.sp 0 +During development, the usual selection criterion is to choose +the latest revision of all components. The \fIco\fR command +makes this selection by default. For example, the command +.D( +co *,v +.D) +retrieves the latest revision on the default branch of each RCS file +in the current directory. +The default branch is usually the trunk, but may be +set to be a side branch. +Side branches as defaults are needed in distributed software development, +as discussed in the section on the RCS revision tree. +.sp +.IP "\fIRelease based selection\fR" +.sp 0 +Specifying a release or branch number selects the latest revision in +that release or branch. +For instance, +.D( +co \-r2 *,v +.D) +retrieves the latest revision with release number 2 from each RCS file. +This selection is convenient if a release has been completed and +development has moved on to the next release. +.sp +.IP "\fIState and author based selection\fR" +.sp 0 +If the highest level number within a given release number +is not the desired one, +the state attribute can help. For example, +.D( +co \-r2 \-sReleased *,v +.D) +retrieves the latest revision with release number 2 whose state attribute +is `Released'. +Of course, the state attribute has to be set appropriately, using the +\fIci\fR or \fIrcs\fR commands. +Another alternative is to select a revision by its author, +using the \fI\-w\fR option. +.sp +.IP "\fIDate based selection\fR" +.sp 0 +Revisions may also be selected by date. +Suppose a release of an entire system was +completed and current on March 4, at 1:00 p.m. local time. Then the command +.D( +co \-d'March 4, 1:00 pm LT' *,v +.D) +checks out all the components of that release, independent of the numbering. +The \fI\-d\fR option specifies a `cutoff date', i.e., +the revision selected has a check-in date that +is closest to, but not after the date given. +.IP "\fIName based selection\fR" +.sp 0 +The most powerful selection function is based on assigning symbolic +names to revisions and branches. +In large systems, a single release number or date is not sufficient +to collect the appropriate revisions from all groups. +For example, suppose one wishes to combine release 2 +of one subsystem and release 15 of another. +Most likely, the creation dates of those releases differ also. +Thus, a single revision number or date passed to the \fIco\fR command +will not suffice to select the right revisions. +Symbolic revision numbers solve this problem. +Each RCS file may contain a set of symbolic names that are mapped +to numeric revision numbers. For example, assume +the symbol \fIV3\fR is bound to release number 2 in file \fIs,v\fR, and to +revision number 15.9 in \fIt,v\fR. +Then the single command +.D( +co \-rV3 s,v t,v +.D) +retrieves the latest revision of release 2 from \fIs,v\fR, +and revision 15.9 from \fIt,v\fR. +In a large system with many modules, checking out all +revisions with one command greatly simplifies configuration management. +.PP +Judicious use of symbolic revision numbers helps with organizing +large configurations. +A special command, \fIrcsfreeze\fR, +assigns a symbolic revision number to a selected revision +in every RCS file. +\fIRcsfreeze\fR effectively freezes a configuration. +The assigned symbolic revision number selects all components +of the configuration. +If necessary, symbolic numbers +may even be intermixed with numeric ones. Thus, \fIV3.5\fR in the +above example +would select revision 2.5 in \fIs,v\fR and branch 15.9.5 in \fIt,v\fR. +.PP +The options \fI\-r\fR, \fI\-s\fR, \fI\-w\fR and \fI\-d\fR +may be combined. If a branch is given, the latest revision +on that branch satisfying all conditions is retrieved; +otherwise, the default branch is used. +.NH 2 +Combining MAKE and RCS +.PP +MAKE\u1\d +is a program that processes configurations. +It is driven by configuration specifications +recorded in a special file, called a `Makefile'. +MAKE avoids redundant processing steps +by comparing creation dates of source and processed objects. +For example, when instructed to compile all +modules of a given system, it only recompiles +those source modules that were changed +since they were processed last. +.PP +MAKE has been extended with an auto-checkout feature for RCS.* +.FS * +This auto-checkout extension is available only in some versions of MAKE, +e.g. GNU MAKE. +.FE +When a certain file to be processed is not present, +MAKE attempts a check-out operation. +If successful, MAKE performs the required processing, and then deletes +the checked out file to conserve space. +The selection parameters discussed above can be passed to MAKE +either as parameters, or directly embedded in the Makefile. +MAKE has also been extended to search the subdirectory named \fIRCS\fR +for needed files, rather than just the current working directory. +However, if a working file is present, MAKE totally ignores the corresponding +RCS file and uses the working file. +(In newer versions of MAKE distributed by AT&T and others, +auto-checkout can be +achieved with the rule DEFAULT, instead of a special extension of MAKE. +However, a file checked out by the rule DEFAULT +will not be deleted after processing. \fIRcsclean\fR can be +used for that purpose.) +.PP +With auto-checkout, RCS/MAKE can effect a selection rule +especially tuned for multi-person software development and maintenance. +In these situations, +programmers should obtain configurations that consist of +the revisions they have personally checked out plus the latest +checked in revision of all other revision groups. +This schema can be set up as follows. +.PP +Each programmer chooses a working directory +and places into it a symbolic link, named \fIRCS\fR, +to the directory containing the relevant RCS files. +The symbolic link makes sure that \fIco\fR and \fIci\fR +operations need only specify the working files, and that +the Makefile need not be changed. +The programmer then checks out the needed files and modifies them. +If MAKE is invoked, +it composes configurations by selecting those +revisions that are checked out, and the rest from the +subdirectory \fIRCS\fR. +The latter selection may be controlled by a symbolic +revision number or any of the other selection criteria. +If there are several programmers editing in separate working directories, +they are insulated from each other's changes until checking in their +modifications. +.PP +Similarly, a maintainer can recreate an older configuration +by starting to work in an empty working directory. +During the initial MAKE invocation, all revisions are selected from RCS files. +As the maintainer checks out files and modifies them, +a new configuration is gradually built up. +Every time MAKE is invoked, it substitutes the modified revisions +into the configuration being manipulated. +.PP +A final application of RCS is to use it for storing Makefiles. +Revision groups of Makefiles represent +multiple versions of configurations. +Whenever a configuration is baselined or distributed, +the best approach is to unambiguously fix +the configuration with a symbolic revision number by calling +\fIrcsfreeze\fR, +to embed that symbol into the Makefile, and to +check in the Makefile (using the same symbolic revision number). +With this approach, old configurations +can be regenerated easily and reliably. +.NH +Usage Statistics +.PP +The following usage statistics were collected on two DEC VAX-11/780 +computers of the Purdue Computer Science Department. Both machines +are mainly used for research purposes. Thus, the data +reflect an environment in which the majority of projects +involve prototyping and advanced software development, +but relatively little long-term maintenance. +.PP +For the first experiment, +the \fIci\fR and \fIco\fR operations were instrumented +to log the number of backward and forward deltas applied. +The data were collected during a 13 month period +from Dec. 1982 to Dec. 1983. +Table I summarizes the results. +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c|c|c|c|c s|c s +c|c|c|c|c s|c s +l|n|n|n|n n|n n. +Operation#Total#Total deltas#Mean deltas#Operations#Branch + #operations #applied#applied#with >1 delta#operations +_ +co # 7867# 9320#1.18#509#(6%)#203#(3%) +ci # 3468# 2207#0.64# 85#(2%)# 75#(2%) +ci & co#11335#11527#1.02#594#(5%)#278#(2%) +.TE +.ce 1 +Table I. Statistics for \fIco\fR and \fIci\fR operations. +.nr VS 18p +.vs 18p +.PP +The first two lines show statistics for check-out and check-in; +the third line shows the combination. +Recall that \fIci\fR performs an implicit check-out to obtain +a revision for computing the delta. +In all measures presented, the most recent revision (stored intact) +counts as one delta. The number of deltas applied represents +the number of passes necessary, where the first `pass' is a copying step. +.PP +Note that the check-out operation is executed more than +twice as frequently as the check-in operation. +The fourth column gives the mean number of deltas +applied in all three cases. +For \fIci\fR, the mean number of deltas applied is less +than one. +The reasons are that the initial check-in requires no delta at all, and that +the only time \fIci\fR requires more than one delta is for branches. +Column 5 shows the actual number of operations that applied more than one +delta. +The last column indicates that branches were not used often. +.PP +The last three columns demonstrate that the most recent trunk revision +is by far the most frequently accessed. +For RCS, check-out of +this revision is a simple copy operation, which is the absolute minimum +given the copy-semantics of \fIco\fR. +Access to older revisions and branches +is more common in non-academic environments, +yet even if access to older deltas were an order +of magnitude more frequent, +the combined average number of deltas applied would still be below 1.2. +Since RCS is faster than SCCS until up to 10 delta applications, +reverse deltas are clearly the method of choice. +.PP +The second experiment, conducted in March of 1984, +involved surveying the existing RCS files +on our two machines. The goal was to determine the mean number of +revisions per RCS file, as well as the space consumed by them. +Table II shows the results. (Tables I and II were produced at different +times and are unrelated.) +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c | c | c | c | c | c | c +c | c | c | c | c | c | c +l | n | n | n | n | n | n. + #Total RCS#Total#Mean#Mean size of#Mean size of#Overhead + #files#revisions#revisions#RCS files#revisions +_ +All files #8033#11133#1.39#6156#5585#1.10 +Files with#1477# 4578#3.10#8074#6041#1.34 +\(>= 2 deltas +.TE +.ce 1 +Table II. Statistics for RCS files. +.nr VS 18p +.vs 18p +.PP +The mean number of revisions per RCS file is 1.39. +Columns 5 and 6 show the mean sizes (in bytes) of an RCS file +and of the latest revision of each RCS file, respectively. +The `overhead' column contains the ratio of the mean sizes. +Assuming that all revisions in an RCS file are approximately the same size, +this ratio gives a measure of the space consumed by the extra revisions. +.PP +In our sample, over 80 per cent of the RCS files contained only a single revision. +The reason is that our +systems programmers routinely check in all source files +on the distribution tapes, even though they may never touch them again. +To get a better indication of how much space savings are possible +with deltas, all measures with those files +that contained 2 or more revisions were recomputed. Only for those files +is RCS necessary. +As shown in the second line, the average number of revisions for those files is +3.10, with an overhead of 1.34. This means that the extra 2.10 deltas +require 34 per cent extra space, or +16 per cent per extra revision. +Rochkind\u3\d +measured the space consumed by SCCS, and +reported an average of 5 revisions per group +and an overhead of 1.37 (or about 9 per cent per extra revision). +In a later paper, Glasser\u6\d +observed an average of 7 revisions per group in a single, large project, +but provided no overhead figure. +In his paper on DSEE\u5\d, +Leblang reported that delta storage combined with blank compression +results in an overhead of a mere 1\-2 per cent per revision. +Since leading blanks accounted for about 20 per cent of the surveyed Pascal +programs, a revision group with 5\-10 members was smaller +than a single cleartext copy. +.PP +The above observations demonstrate clearly that the space needed +for extra revisions is small. With delta storage, the luxury of +keeping multiple revisions online is certainly affordable. +In fact, introducing a system with delta storage may reduce +storage requirements, because programmers often save back-up copies +anyway. Since back-up copies are stored much more efficiently with deltas, +introducing a system such as RCS may +actually free a considerable amount of space. +.NH +Survey of Version Control Tools +.PP +The need to keep back-up copies of software arose when +programs and data were no longer stored on paper media, but were entered +from terminals and stored on disk. +Back-up copies are desirable for reliability, and many modern editors +automatically save a back-up copy for every file touched. +This strategy +is valuable for short-term back-ups, but not suitable for long-term +version control, since an existing back-up copy is overwritten whenever the +corresponding file is edited. +.PP +Tape archives are suitable for long-term, offline storage. +If all changed files are dumped on a back-up tape once per day, old revisions +remain accessible. However, tape archives are unsatisfactory +for version control in several ways. First, backing up the file +system every 24 hours does not capture intermediate revisions. +Secondly, the old revisions are not online, +and accessing them is tedious and time-consuming. +In particular, it is impractical to +compare several old revisions of a group, +because that may require mounting and searching several tapes. +Tape archives are important fail-safe tools in the +event of catastrophic disk failures or accidental deletions, +but they are ill-suited for version control. +Conversely, version control tools do not obviate the +need for tape archives. +.PP +A natural technique for keeping several old revisions online is +to never delete a file. +Editing a file +simply creates a new file with the same +name, but with a different sequence number. +This technique, available as an option in DEC's VMS operating system, +turns out to be inadequate for version control. +First, it is prohibitively expensive in terms of storage costs, +especially since no data compression techniques are employed. +Secondly, indiscriminately storing every change produces too many +revisions, and programmers have difficulties distinguishing them. +The proliferation of revisions forces programmers to spend much time on +finding and deleting useless files. +Thirdly, most of the support functions like locking, logging, +revision selection, +and identification described in this paper are not available. +.PP +An alternative approach is to separate editing from revision control. +The user may repeatedly edit a given revision, +until freezing it with an explicit command. +Once a revision is frozen, it is stored permanently and can no longer be modified. +(In RCS, freezing a revisions is done with \fIci\fR.) +Editing a frozen revision implicitly creates a new one, which +can again be changed repeatedly until it is frozen itself. +This approach saves exactly those revisions that the user +considers important, and keeps the number of revisions manageable. +IBM's CLEAR/CASTER\u7\d, +AT&T's SCCS\u3\d, +CMU's SDC\u8\d +and DEC's CMS\u9\d, +are examples of version control systems using this approach. +CLEAR/CASTER maintains a data base of programs, specifications, +documentation and messages, using deltas. +Its goal is to provide control over the development process from a +management viewpoint. +SCCS stores multiple revisions of source text in an ancestral tree, +records a log entry for each revision, +provides access control, and has facilities +for uniquely identifying each revision. +An efficient delta technique +reduces the space consumed by each revision group. +SDC is much simpler than SCCS because it stores not more than +two revisions. However, it maintains a complete log for all old +revisions, some of which may be on back-up tape. +CMS, like SCCS, manages tree-structured revision groups, +but offers no identification mechanism. +.PP +Tools for dealing with configurations are still in a state of flux. +SCCS, SDC and CMS can be combined with MAKE or MAKE-like programs. +Since flexible selection rules are missing from all these tools, +it is sometimes difficult +to specify precisely which revision of each group +should be passed to MAKE for building a desired configuration. +The Xerox Cedar system\u10\d +provides a `System Modeller' that can rebuild +a configuration from an arbitrary set of module revisions. +The revisions of a module are only distinguished by creation time, +and there is no tool for managing groups. +Since the selection rules are primitive, +the System Modeller appears to be somewhat tedious to use. +Apollo's DSEE\u5\d +is a sophisticated software engineering environment. +It manages revision groups in a way similar to SCCS and CMS. Configurations +are built using `configuration threads'. +A configuration thread states which revision of each group +named in a configuration should be chosen. +A configuration thread may contain dynamic specifiers +(e.g., `choose the revisions I am currently working on, +and the most recent revisions otherwise'), which are bound +automatically at build time. +It also provides a notification mechanism for alerting +maintainers about the need to rebuild a system after a change. +.PP +RCS is based on a general model for describing +multi-version/multi-configuration systems\u11\d. +The model describes systems using AND/OR graphs, where AND nodes represent +configurations, and OR nodes represent version groups. +The model gives rise to a suit of selection rules for +composing configurations, almost all of which are implemented in RCS. +The revisions selected by RCS are passed to MAKE for configuration building. +Revision group management is modelled after SCCS. +RCS retains SCCS's best features, +but offers a significantly simpler user interface, +flexible selection rules, adequate integration with MAKE +and improved identification. +A detailed comparison of RCS and SCCS appears in Reference 4. +.PP +An important component of all revision control systems +is a program for computing deltas. +SCCS and RCS use the program \fIdiff\fR\u2\d, +which first computes the longest common substring of two +revisions, and then produces the delta from that substring. +The delta is simply an edit script consisting of deletion and +insertion commands that generate one revision from the other. +.PP +A delta based on a longest common substring is not necessarily minimal, +because it does not take advantage of crossing block moves. +Crossing block moves arise if two or more blocks of lines +(e.g., procedures) +appear in a different order in two revisions. +An edit script derived from a longest common substring +first deletes the shorter of the two blocks, and then reinserts it. +Heckel\u12\d +proposed an algorithm for detecting block moves, but +since the algorithm is based on heuristics, +there are conditions +under which the generated delta is far from minimal. +DSEE uses this algorithm combined with blank compression, +apparently with satisfactory overall results. +A new algorithm that is guaranteed to produce a minimal delta based on +block moves appears in Reference 13. +A future release of RCS will use this algorithm. +.PP +\fIAcknowledgements\fR: +Many people have helped make RCS a success by contributed criticisms, suggestions, +corrections, and even whole new commands (including manual pages). +The list of people is too long to be +reproduced here, but my sincere thanks for their help and +goodwill goes to all of them. +.sp +.nr VS 12p +.vs 12p +.SH +Appendix: Synopsis of RCS Operations +.LP +.IP "\fIci\fP \fB\- check in revisions\fP" +.sp 0 +\fICi\fR stores the contents of a working file into the +corresponding RCS file as a new revision. +If the RCS file doesn't exist, \fIci\fR creates it. +\fICi\fR removes the working file, unless one of the options +\fI\-u\fR or \fI\-l\fR is present. +For each check-in, \fIci\fR asks for a commentary +describing the changes relative to the previous revision. +.sp 1 +\fICi\fR assigns the revision number given by the \fI\-r\fR option; +if that option is missing, it derives the number from the +lock held by the user; if there is no lock and locking is not strict, +\fIci\fR increments the number of the latest revision on the trunk. +A side branch can only be started by explicitly specifying its +number with the \fI\-r\fR option during check-in. +.sp 1 +\fICi\fR also determines +whether the revision to be checked in is different from the +previous one, and asks whether to proceed if not. +This facility simplifies check-in operations for large systems, +because one need not remember which files were changed. +.sp 1 +The option \fI\-k\fR searches the checked in file for identification +markers containing +the attributes +revision number, check-in date, author and state, and assigns these +to the new revision rather than computing them. This option is +useful for software distribution: Recipients of distributed software +using RCS should check in updates with the \fI\-k\fR option. +This convention guarantees that revision numbers, check-in dates, +etc., are the same at all sites. +.IP "\fIco\fP \fB\- check out revisions\fP" +.sp 0 +\fICo\fR retrieves revisions according to revision number, +date, author and state attributes. It either places the revision +into the working file, or prints it on the standard output. +\fICo\fR always expands the identification markers. +.IP "\fIident\fP \fB\- extract identification markers\fP" +.sp 0 +\fIIdent\fR extracts the identification markers expanded by \fIco\fR +from any file and prints them. +.IP "\fIrcs\fP \fB\- change RCS file attributes\fP" +.sp 0 +\fIRcs\fR is an administrative operation that changes access lists, +locks, unlocks, breaks locks, toggles the strict-locking feature, +sets state attributes and symbolic revision numbers, changes the +description, and deletes revisions. A revision can +only be deleted if it is not the fork of a side branch. +.br +.ne 10 +.IP "\fIrcsclean\fP \fB\- clean working directory\fP" +.sp 0 +\fIRcsclean\fR removes working files that were checked out but never changed.* +.FS * +The \fIrcsclean\fP and \fIrcsfreeze\fP commands +are optional and are not always installed. +.FE +.IP "\fIrcsdiff\fP \fB\- compare revisions\fP" +.sp 0 +\fIRcsdiff\fR compares two revisions and prints their +difference, using the UNIX tool \fIdiff\fR. +One of the revisions compared may be checked out. +This command is useful for finding out about changes. +.IP "\fIrcsfreeze\fP \fB\- freeze a configuration\fP" +.sp 0 +\fIRcsfreeze\fR assigns the same symbolic revision number +to a given revision in all RCS files. +This command is useful for accurately recording a configuration.* +.IP "\fIrcsmerge\fP \fB\- merge revisions\fP" +.sp 0 +\fIRcsmerge\fR merges two revisions, \fIrev1\fR and \fIrev2\fR, +with respect to a common ancestor. +A 3-way file comparison determines the segments of lines that +are (a) the same in all three revisions, or (b) the same in 2 revisions, +or (c) different in all three. For all segments of type (b) where +\fIrev1\fR is the differing revision, +the segment in \fIrev1\fR replaces the corresponding segment of \fIrev2\fR. +Type (c) indicates an overlapping change, is flagged as an error, and requires user +intervention to select the correct alternative. +.IP "\fIrlog\fP \fB\- read log messages\fP" +.sp 0 +\fIRlog\fR prints the log messages and other information in an RCS file. +.bp +.LP +.nr VS 12p +.vs 12p +.]< +.ds [F 1 +.]- +.ds [K FELD02 +.ds [K MakeArticle +.ds [A Feldman, Stuart I. +.ds [D March 1979 +.ds [T Make\*-A Program for Maintaining Computer Programs +.ds [J Software\*-Practice & Experience +.ds [V 9 +.ds [N 3 +.ds [P 255-265 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 2 +.]- +.ds [K HUNT01 +.ds [T An Algorithm for Differential File Comparison +.ds [A Hunt, James W. +.as [A " and McIlroy, M. D. +.ds [I Computing Science Technical Report, Bell Laboratories +.ds [R 41 +.ds [D June 1976 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 4 tech-report +.ds [F 3 +.]- +.ds [K SCCS +.ds [A Rochkind, Marc J. +.ds [D Dec. 1975 +.ds [T The Source Code Control System +.ds [J IEEE Transactions on Software Engineering +.ds [V SE-1 +.ds [N 4 +.ds [P 364-370 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 4 +.]- +.ds [K TICH08 +.ds [T Design, Implementation, and Evaluation of a Revision Control System +.ds [A Tichy, Walter F. +.ds [B Proceedings of the 6th International Conference on Software Engineering +.ds [I ACM, IEEE, IPS, NBS +.ds [D September 1982 +.ds [P 58-67 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.]- +.ds [K LEBL01 +.ds [A Leblang, David B. +.as [A " and Chase, Robert P. +.ds [T Computer-Aided Software Engineering in a Distributed Workstation Environment +.ds [O Proceedings of the ACM SIGSOFT/SIGPLAN Software Engineering Symposium +.as [O " on Practical Software Development Environments. +.ds [J SIGPLAN Notices +.ds [V 19 +.ds [N 5 +.ds [D May 1984 +.ds [P 104-112 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 1 +.ds [F 3 +.ds [F 6 +.]- +.ds [K SCCSEval +.ds [A Glasser, Alan L. +.ds [D Nov. 1978 +.ds [T The Evolution of a Source Code Control System +.ds [J Software Engineering Notes +.ds [V 3 +.ds [N 5 +.ds [P 122-125 +.nr [P 1 +.ds [O Proceedings of the Software Quality and Assurance Workshop. +.nr [T 0 +.nr [A 1 +.nr [O 1 +.][ 1 journal-article +.ds [F 5 +.ds [F 7 +.]- +.ds [K IBMClearCaster +.ds [A Brown, H.B. +.ds [D 1970 +.ds [T The Clear/Caster System +.ds [J Nato Conference on Software Engineering, Rome +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 3 +.ds [F 8 +.]- +.ds [K HabermannSDC +.ds [A Habermann, A. Nico +.ds [D Jan. 1979 +.ds [T A Software Development Control System +.ds [I Technical Report, Carnegie-Mellon University, Department of Computer Science +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 9 +.]- +.ds [K CMS +.ds [A DEC +.ds [T Code Management System +.ds [I Digital Equipment Corporation +.ds [O Document No.\ EA-23134-82 +.ds [D 1982 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 10 +.]- +.ds [K LAMP01 +.ds [A Lampson, Butler W. +.as [A " and Schmidt, Eric E. +.ds [T Practical Use of a Polymorphic Applicative Language +.ds [B Proceedings of the 10th Symposium on Principles of Programming Languages +.ds [I ACM +.ds [P 237-255 +.nr [P 1 +.ds [D January 1983 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.ds [F 11 +.]- +.ds [K TICH07 +.ds [T A Data Model for Programming Support Environments and its Application +.ds [A Tichy, Walter F. +.ds [B Automated Tools for Information System Design and Development +.ds [E Hans-Jochen Schneider and Anthony I. Wasserman +.ds [C Amsterdam +.ds [I North-Holland Publishing Company +.ds [D 1982 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 4 +.ds [F 2 +.ds [F 12 +.]- +.ds [K HECK01 +.ds [T A Technique for Isolating Differences Between Files +.ds [A Heckel, Paul +.ds [J Communications of the ACM +.ds [D April 1978 +.ds [V 21 +.ds [N 4 +.ds [P 264-268 +.nr [P 1 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 1 journal-article +.ds [F 13 +.]- +.ds [K TICH11 +.ds [T The String-to-String Correction Problem with Block Moves +.ds [A Tichy, Walter F. +.ds [D Nov. 1984 +.ds [J ACM Transactions on Computer Systems +.ds [V 2 +.ds [N 4 +.ds [P 309-321 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.]> diff --git a/gnu/usr.bin/rcs/rcs_func.ms b/gnu/usr.bin/rcs/rcs_func.ms new file mode 100644 index 00000000000..9818086c3de --- /dev/null +++ b/gnu/usr.bin/rcs/rcs_func.ms @@ -0,0 +1,95 @@ +.SH +Functions of RCS (Revision Control System) +.PP +RCS manages software libraries. It greatly increases programmer productivity +by providing the following functions. +.IP 1. +RCS stores and retrieves multiple revisions of program and other text. +Thus, one can maintain one or more releases while developing the next +release, with a minimum of space overhead. Changes no longer destroy the +original -- previous revisions remain accessible. +.RS +.IP a. +Maintains each module as a tree of revisions. +.IP b. +Project libraries can +be organized centrally, decentralized, or any way you like. +.IP c. +RCS works for any type of text: programs, documentation, memos, papers, +graphics, VLSI layouts, form letters, etc. +.RE +.IP 2. +RCS maintains a complete history of changes. +Thus, one can find out what happened to a module easily +and quickly, without having to compare source listings or +having to track down colleagues. +.RS +.IP a. +RCS performs automatic record keeping. +.IP b. +RCS logs all changes automatically. +.IP c. +RCS guarantees project continuity. +.RE +.IP 3. +RCS manages multiple lines of development. +.IP 4. +RCS can merge multiple lines of development. +Thus, when several parallel lines of development must be consolidated +into one line, the merging of changes is automatic. +.IP 5. +RCS flags coding conflicts. +If two or more lines of development modify the same section of code, +RCS can alert programmers about overlapping changes. +.IP 6. +RCS resolves access conflicts. +When two or more programmers wish to modify the same revision, +RCS alerts the programmers and makes sure that one modification won't wipe +out the other one. +.IP 7. +RCS provides high-level retrieval functions. +Revisions can be retrieved according to ranges of revision numbers, +symbolic names, dates, authors, and states. +.IP 8. +RCS provides release and configuration control. +Revisions can be marked as released, stable, experimental, etc. +Configurations of modules can be described simply and directly. +.IP 9. +RCS performs automatic identification of modules with name, revision +number, creation time, author, etc. +Thus, it is always possible to determine which revisions of which +modules make up a given configuration. +.IP 10. +Provides high-level management visibility. +Thus, it is easy to track the status of a software project. +.RS +.IP a. +RCS provides a complete change history. +.IP b. +RCS records who did what when to which revision of which module. +.RE +.IP 11. +RCS is fully compatible with existing software development tools. +RCS is unobtrusive -- its interface to the file system is such that +all your existing software tools can be used as before. +.IP 12. +RCS' basic user interface is extremely simple. The novice need to learn +only two commands. Its more sophisticated features have been +tuned towards advanced software development environments and the +experienced software professional. +.IP 13. +RCS simplifies software distribution if customers +maintain sources with RCS also. This technique assures proper +identification of versions and configurations, and tracking of customer +modifications. Customer modifications can be merged into distributed +versions locally or by the development group. +.IP 14. +RCS needs little extra space for the revisions (only the differences). +If intermediate revisions are deleted, the corresponding +differences are compressed into the shortest possible form. +.IP 15. +RCS is implemented with reverse deltas. This means that +the latest revision, which is the one that is accessed most often, +is stored intact. All others are regenerated from the latest one +by applying reverse deltas (backward differences). This +results in fast access time for the revision needed most often. diff --git a/gnu/usr.bin/rcs/src/ChangeLog b/gnu/usr.bin/rcs/src/ChangeLog new file mode 100644 index 00000000000..c0c23a7c3ba --- /dev/null +++ b/gnu/usr.bin/rcs/src/ChangeLog @@ -0,0 +1,1799 @@ +Fri Jun 16 06:19:24 1995 Paul Eggert <eggert@twinsun.com> + + * version.c: Version 5.7 released. + * Makefile.in (TESTPREFIX): Remove; replaced with $(srcdir)/. + * rcsutil.c (catchsig): Remove `return'. + * Makefile.in, ci.c, co.c, conf.sh, ident.c, maketime.c, maketime.h, + merge.c, merger.c, partime.c, partime.h, rcs.c, rcsbase.h, rcsclean.c, + rcsdiff.c, rcsedit.c, rcsfcmp.c, rcsfnms.c, rcsgen.c, rcskeep.c, + rcskeys.c, rcslex.c, rcsmap.c, rcsmerge.c, rcsrev.c, rcssyn.c, rcstest, + rcstime.c, rcsutil.c, rlog.c: Update FSF address. + +Mon Jun 5 08:30:02 1995 Paul Eggert <eggert@twinsun.com> + + * version.c (RCS_version_string): Update to 5.6.8.1 (beta). + +Fri Jun 2 18:19:00 1995 Paul Eggert <eggert@twinsun.com> + + * rcsutil.c (catchsigaction): + New name for `catchsig', for sa_sigaction signature. + Use nRCS even if !has_psiginfo, to remove unused variable warning. + (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. + Use ENOTSUP only if defined. + * conf.sh, conf.heg (has_sa_sigaction): New configuration variable. + +Thu Jun 1 16:23:43 1995 Paul Eggert <eggert@twinsun.com> + + * version.c (RCS_version_string): Update to 5.6.8 (beta). + + * Makefile.in: + Renamed from Makefile; autoconf now preprocesses this file. + Rewrite to follow GNU makefile standards. + (clean): `rm core*' -> `rm core core.* *.core', for BSD/OS 2.0. + + * ci.c (main): Add -kb. Use `cmpdate', not `cmpnum', to compare dates. + Don't worry about errno after ftruncate fails. + Fix input file rewinding bug when large_memory && !maps_memory + and checking in a branch tip. + + (fixwork): Fall back on chmod if fchmod fails; it might be ENOSYS. + + * co.c (main, preparejoin): + Pass argument instead of using `join' static variable. + (main): Add -kb. + + * ident.c (exiterr, reportError): + New functions, needed for DOS and OS/2 ports. + (scanfile): Use them. + + * rcsdiff.c (main): + Pass "--binary" if -kb and if --binary makes a difference. + Don't treat + options specially. + + * rcsmerge.c (main): + Report an error if -kb, so don't worry about binary stdout. + Punctuate messages properly. Rewrite to avoid `goto end'. + + * rcs.c (main): + Warn if no options were given. Punctuate messages properly. + + (sendmail): Rewind mailmess before flushing it. + Output another warning if mail should work but fails. + + (buildeltatext): Pass "--binary" if -kb and if --binary is needed. + + * merger.c (normalize_arg): Don't treat + options specially. + (merge): Don't make -A the default; always use -E. + Always open working file in text mode. + + * rlog.c (struct rcslockers): Renamed from `struct lockers'. + (getnumericrev): Return error indication instead of ignoring errors. + (main): Check it. Don't use dateform. + (recentdate, extdate): cmpnum -> cmpdate + + * rcsclean.c (unlock): struct lock -> struct rcslock + + * rcsedit.c (dirtpname): No longer external. + (do_link): Simplify logic. + (finisheditline, finishedit): Replace Iseek/Itell with definiens. + (fopen_update_truncate): Replace `#if' with `if'. + (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. + + (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was + output at the end of incomplete lines. + + (keyreplace): Do not assume that seeking backwards + at the start of a file will fail; on some systems it succeeds. + Convert C- and Pascal-style comment starts to ` *' in comment leader. + + (rcswriteopen): Use fdSafer to get safer file descriptor. + Open RCS file with FOPEN_RB. + + (chnamemod): Work around bad_NFS_rename; don't ignore un_link result. + Fall back on chmod if fchmod fails, since it might be ENOSYS. + + (aflush): Move to rcslex.c. + + * rcsfcmp.c (rcsfcmp): Add -kb support. + + * rcsfnms.c (basefilename): Renamed from basename to avoid collisions. + (dirlen): Remove (for similar reasons). + (rcsreadopen): Open with FOPEN_RB. + (SLASHSLASH_is_SLASH): Default is 0. + (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug. + + * rcsgen.c (putadmin): Open RCS file with FOPEN_WB. + + * rcskeep.c (getoldkeys): Don't panic if a Name: is empty. + + * rcslex.c: + (map_fd_deallocate, mmap_deallocate, read_deallocate, + nothing_to_deallocate): New functions. + (Iclose): If large_memory and maps_memory, use them to deallocate. + (fd2RILE): Use map_fd if available. + If one mapping method fails, try the next instead of giving up; + if they all fail, fall back on ordinary read. + Work around bug: root mmap over NFS succeeds, but accessing dumps core. + Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. + (advise_access): Use madvise only if this instance used mmap. + (Iopen): Use fdSafer to get safer file descriptor. + (aflush): Moved here from rcsedit.c. + + * rcsrev.c (cmpdate, normalizeyear): + New functions work around MKS incompatibility. + (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug. + (genrevs, genbranch): cmpnum -> cmpdate + + * rcssyn.c (expand_names): Add "b" for -kb. + (getdelta): Don't strip leading "19" from MKS dates; see cmpdate. + + * rcsutil.c (catchsig, restoreints, setup_catchsig): + Use SA_SIGINFO, not has_psiginfo, + to determine whether to use SA_SIGINFO feature, + but also check at runtime whether the feature works. + (catchsig): If an mmap_signal occurs, report the affected file name. + (unsupported_SA_SIGINFO, accessName): New variables. + (setup_catchsig): If using SA_SIGINFO, use sa_sigaction not sa_handler. + If SA_SIGINFO fails, fall back on sa_handler method. + + (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New fns. + (concatenate): Remove. + + (runv): Work around bad_wait_if_SIGCHLD_ignored bug. + Remove reference to OPEN_O_WORK. + + * maketime.h (adjzone, str2time): + Time zones are now (long) seconds, not (int) minutes. + (const): Remove. + + * maketime.c (difftm): + Use smaller expressions, to avoid confusing buggy compilers. + (adjzone, str2time): Time zones are (long) seconds, not (int) minutes. + + * partime.c (zone_names): + Add "jdt", since Japan might use it. "nrt" -> "nrft". + Use special markers 1 and -1, not TM_LOCAL_ZONE and TM_UNDEFINED_ZONE, + to avoid integer overflow now that time zones are seconds, not minutes. + (parse_decimal): Round to even. + (parzone): Time zones are now (long) seconds, not (int) minutes, + as allowed by ISO 8601. + + * partime.h (TM_UNDEFINED_ZONE, zone): + Now (long) seconds, not (int) minutes. + (const): Remove. + + * rcstime.c (dateform): Remove. + (zone_offset, str2time_checked, zone_set, date2str): + Time zones are now (long) seconds, not (int) minutes. + (date2str): Use `-08' style time zone, not `-0800', for ISO 8601. + + * conf.sh: `#!/bin/sh' -> `#! /bin/sh'; some picky hosts require this. + Ensure we don't run as root; it would yield the wrong answers. + Ensure the C compiler works on a trivial program first. + Rename incoming environment variables to follow GNU makefile standards. + Use $PREPARE_CC consistently. + + (_POSIX_C_SOURCE, <mach/mach.h>, <net/errno.h>, has_map_fd, + bad_NFS_rename, has_si_errno, bad_wait_if_SIGCHLD_ignored): + New configuration options. + + (has_sys_dir_h, text_equals_binary_stdio, text_work_stdio, + FOPEN_R, FOPEN_W, FOPEN_WPLUS, has_caddr_t, DIFF3_A): Remove. + + (O_BINARY, OPEN_O_WRONLY, FOPEN_RB, FOPEN_R_WORK, FOPEN_WB, + FOPEN_W_WORK, FOPEN_WPLUS_WORK): New macros, for -kb support. + (bad_fopen_wplus): Use "w+", not FOPEN_WPLUS. + + (has_const, has_volatile): Rename test functions to avoid collisions. + (has_fchmod, has_mmap, has_vfork): fileno(stdio) -> STDIN_FILENO. + (has_fflush_input): Don't trust fflush's return value when testing. + (has_mmap): Use MAP_FAILED if available. Look at 0th byte, not last. + Define to 0 if HP-UX 8.07 or 9.*, or SunOS 5.4 before patch 10. + (has_psiginfo): Handler's 3rd arg is void *, as per Posix 1003.1b-1993. + (has_varargs): Don't set if has_stdarg. + (large_memory): Also define to 1 if has_map_fd. + (mmap_signal): Don't try to deduce it if creat yields ETXTBSY. + + * conf.heg: Adjust to latest conf.sh. + + * rcsbase.h (SIZEABLE_PATH): + Don't depend on PATH_MAX: it's not worth configuring. + (Ioffset_type, BINARY_EXPAND, MIN_UNEXPAND, MIN_UNCHANGED_EXPAND): + New macros. + (maps_memory): New macro; replaces many instances of `has_mmap'. + (cacheptr): Renamed from cachetell. + (struct RILE): New alternate name for RILE; the type is now recursive. + (deallocate): New member for RILE, used for generic deallocation. + (cacheunget_): No longer take a failure arg; just call Ierror. + (struct rcslock): Renamed from struct lock, to avoid collisions with + system headers on some hosts. All users changed. + (basefilename): Renamed from basename, likewise. + (dirtpname): Remove; no longer external. + (dirlen, dateform): Remove; no longer used. + (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New fns. + (zonelenmax): Increase to 9 for full ISO 8601 format. + (catchmmapints): Depend on has_NFS. + + * rcstest: Add -kb test. + `#!/bin/sh' -> `#! /bin/sh'; some picky hosts require this. + (ALL_CFLAGS): Renamed from CFLAGS. + (LIBS): Renamed from LDLIBS. + (CL): Add explicit `-o a.out'. + +Thu Apr 14 06:55:24 1994 Paul Eggert <eggert@twinsun.com> + + * conf.sh: Relax test for puts. Improve test for mmap; don't + rely on getpagesize, output better messages when it fails, + don't assume you can mmap something larger than the file, + output "SIGSEGV" if appropriate, add commentary. + +Wed Apr 13 16:31:38 1994 Paul Eggert <eggert@twinsun.com> + + * version.c: now 5.6.7.4 (beta); intermediate versions were PC-related. + * rcstime.c: Don't dump core if there's no zone. + * rlog.c: Fix bug; `rlog -lxxx' inverted the sense of -l. + +Sun Mar 20 04:52:58 1994 Paul Eggert <eggert@twinsun.com> + + * version.c: now 5.6.7 (beta) + + * ci.c: Do not remove the lock when `ci -l' reverts. + Do not generate a corrupted RCS file if the user modifies + the working file while `ci' is running. + * ci.c, co.c: + Move buffer-flushes out of critical sections, as they aren't critical. + * ci.c, co.c, merger.c, rcs.c, rcsbase.h, rcsdiff.c, rcsmerge.c, + rcsutil.c: Specify subprocess input via file descriptor, not file name. + * ci.c, co.c, rcs.c, rcsclean.c, rcsedit.c: + Use ORCSerror to clean up after a fatal error. + + * ident.c: Remove `exiting' from identExit. + + * rcs.c, rcsgen.c: Flush stderr after prompt. + + * rcsclean.c: Add void_closedir support. + + * rlog.c: Emulate -V4's white space generation more precisely. + -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it. + * rlog.c, rcsgen.c: Work around SVR4 stdio performance bug. + + * rcsbase.h: Add primitives for reading backwards from a RILE; + this is needed to go back and find the $Log prefix. + + * partime.c: Don't include rcsbase.h; you only need conf.h. + Fix `am/pm' parsing bug. Update time zones. + + * rcsedit.c, rcsfcmp.c: + Normally calculate the $Log prefix from context, not from RCS file. + + * rcsfcmp.c: + Calculate line numbers correctly even if the $Log prefix contains \n. + + * rcsfnms.c: Strip trailing SLASHes from TMPDIR; some systems need it. + + * rcslex.c: Don't worry if madvise fails. Add Orewind. + + * rcsutil.c: Move setmtime to rcsedit.c. + Avoid messing with I/O buffers in the child process. + Define dup in terms of F_DUPFD if it exists. + + * Makefile: Make sure $(DESTBINDIR) exists before installing. + Remove core* (for BSDI), RCS/a.*. + + * conf.sh: Do errno checking before readlink checking. + Check for GCC __attribute__ infelicities. + Output a better error message for wait problems. + Check for AIX 3.2.0 mmap bug. Update HP-UX test. + Fix SLASHes typo. Add SEEK_CUR. Configure off_t, has_fflush_input. + Improve test for __attribute__((noreturn)). + * conf.heg: Adjust to latest conf.sh. + + * rcstest: Port to BSD/386 1.1's /bin/sh brain damage. + Test for new $Log behavior. + + * co.c, merge.c, merger.c, rcs.c, rcsbase.h, rcsdiff.c, rcsedit.c, + rcsfcmp.c, rcsfnms.c, rcsgen.c, rcskeep.c, rcslex.c, rcsmerge.c, + rcsrev.c, rcssyn.c, rcsutil.c, rlog.c: Remove lint. + +Tue Nov 9 17:55:29 1993 Paul Eggert <eggert@twinsun.com> + + * version.c: now 5.6.6 (beta) + * merge.c, rcsclean.c, rcsutil.c, rlog.c: + -V now prints version on stdout and exits. + * ci.c, co.c, rcs.c, rcsdiff.c, rcsmerge.c: + Likewise. Don't print usage twice. + * Makefile: Add -Dhas_conf_h, maketime.h, partime.h, rcstime.c. + ident wants version.o. + * conf.heg, conf.sh: gcc doesn't do constant expressions in + __attribute__((format(...))). + * conf.sh: Quote $dots. + * ident.c: Add -V. + * maketime.c: Move RCS-specific stuff into rcstime.c. + * maketime.h, partime.h, rcstime.c: New files. + * partime.c: Complete rewrite from scratch. + * rcsbase.h: Move RCS-specific time handling into rcstime.c. + printf_string now takes two arguments, alas. + * rcskeep.c: Use simpler time zone parsing strategy + now that we're using ISO 8601 format. + * rcslex.c: Fix `label: }' typo. + * rcsrev.c: Fix format string typos. + +Wed Nov 3 18:25:07 1993 Paul Eggert <eggert@twinsun.com> + + * version.c: Initial revision; now 5.6.5 (beta) + + * ci.c, co.c, rcs.c, rcsdiff.c, rcsclean.c, rcsedit.c, rcsmerge.c, + rcsrev.c, rlog.c: Add -z. + + * ci.c: Don't subtract from RCS file timestamp even if -T. + Scan for and use Name keyword if -k. + * ci.c, rcs.c, rcsbase.h, rcsgen.c, rcslex.c, rcssyn.c: + Don't discard ignored phrases. + * ci.c, co.c, rcs.c, rcsbase.h, rcsclean.c, rcsdiff.c, rcsfnms.c, + rcsgen.c, rcskeep.c, rcslex.c, rcsrev.c, rcssyn.c: + Improve quality of diagnostics. + + * co.c: Generate a value for the Name keyword. + * co.c, rcsbase.h: Don't arbitrarily limit the number of joins. + + * ident.c: Test for char == EOF, not char < 0. + + * rcs.c: Don't lose track of -m or -t when there are no other changes. + + * rcsdiff.c: Put revision numbers in -c output. + * rcsdiff.c, rcsbase.h, rcsedit.c, rcskeys.c: Add Name keyword. + * rcsdiff.c, rcsmerge.c: Pass -Vn to `co'. + * rcsdiff.c, rcsmerge.c, rlog.c: Ignore -T. + + * rcsmerge.c: + Pass unexpanded revision name to `co', so that $Name works. + * rcsmerge.c, rcsbase.h, merge.c, merger.c: + Add -A, -E, -e. Allow up to three labels. + + * rlog.c: Add -N. + + * rcsbase.h: Remove `nil'. Improve quality of prototypes. + + * partime.c: Simplify time zone table. + + * rcsedit.c: If bad_unlink, ignore errno when unlink fails. + Escape white space, $, and \ in keyword string file names. + Don't output 2 spaces between date and time after Log. + + * rcskeep.c: Scan for Name keyword. + + * rcsfnms.c: Determine whether a file name is too long indirectly, + by examining inode numbers, instead of trying to use operating system + primitives like pathconf, which are not trustworthy in general. + File names may now hold white space or $. + Do not flatten ../X in pathnames; that may be wrong for symlinks. + Add getabsname hook. + + * rcsfcmp.c: + Fix yet another off-by-one error when comparing Log string expansions. + + * rcsrev.c: Add `namedrev' for Name support. + Revision `.N' now stands for `D.N', where D is the default branch. + + * rcssyn.c: Parse MKS dates; ignore \r in diff control lines. + + * rcsutil.c: Use psiginfo and setreuid if available. + Move date2str to maketime.c. + + * Makefile: Fix DIFF3_A default. + + * conf.sh: Update for RCS 5.6.5 (much better configuration messages). + * conf.heg: Adjust to latest conf.sh. + + * rcstest: Compile our own program instead of using `tr'. + Use rlog to get UTC instead of relying on `date -u'. + Test `Name' keyword. + + * rcsfreeze.sh: Don't ignore RCS/.*,v files. + +Tue Jun 29 17:03:43 1993 Paul Eggert <eggert@twinsun.com> + + * maketime.c: Add -z support. New function zone_set, date2str. + str2time now has default_zone arg. Parenthesize to keep GCC happy. + +Tue Jul 28 16:12:44 1992 Paul Eggert <eggert@twinsun.com> + + Version 5.6.4 (beta) + + * ci.c: Add -i, -j. Check that working and RCS files are distinct. + * ci.c, co.c, rcs.c, rcsclean.c, rcsdiff.c, rcsmerge.c, rcsutil.c, + rlog.c: Add -V. + + * co.c, rcsclean.c: Check that working and RCS files are distinct. + + * rcs.c: rcs -l now asks whether you want to break the lock. + Set RCS file's mode and time at right moment. + + * rcsclean.c: Fix -n bugs. + + * rcsdiff.c: Use co -M for better dates with traditional diff -c. + + * rlog.c: Don't miss B.0 when handling branch B. + Diagnose missing `,' in -r. + * rlog.c, rcsedit.c, rcslex.c, rcsrev.c, rcssyn.c: + Avoid `unsigned'; this avoids the ULONG_MAX/10 configuration problem. + * rlog.c, rcsbase.h, rcsedit.c, rcsgen.c rcsfcmp.c, rcskeep.c, rcslex.c, + rcssyn.c: Statement macro names now end in _. + + * partime.c: Fix time zones again. + + * rcsedit.c: Some hosts have readlink but not ELOOP. + Preserve dates more systematically. + + * rcsgen.c, rcsfnms.c: Be consistent about pathnames vs filenames. + + * rcsfnms.c: Add .sty. .pl now implies Perl, not Prolog. + Fix fdlock initialization bug. Check that $PWD is really ".". + + * rcslex.c, rcsrev.c: Identifiers may now start with a digit and + (unless they are symbolic names) may contain `.'. + + * rcsutil.c: has_sigaction overrides sig_zaps_handler. Fix -M bug. + Add mmap_signal, which minimizes signal handling for non-mmap hosts. + + * Makefile: Add BINDIR. Update for GCC 2. Redo configuration scheme. + Add rcsvers.c. + + * conf.sh: Output more chatter to keep builders happy. + Fix several portability bugs: fileno, readlink, readlink errno, + HP-UX brain damage, execv, mmap, waitpid, same_file. Remove ULONG_MAX. + * conf.heg: Adjust to latest conf.sh. + + * rcstest: Test rcsclean. rm -f a.c more often. + *'! 1.1.1.1') -> *!\ 1.1.1.1) to avoid some shell bugs. + Set PWD unconditionally. Don't assume RCS knows date's time zone. + Avoid `tr' if possible; it's not standardized well. + +Sun May 31 08:29:18 1992 Paul Eggert <eggert@twinsun.com> + + * maketime.c: + Complete rewrite from scratch, because we need to work even if + the underlying implementation has leap seconds. + + * partime.c: The special time zone LOCAL_TIME+1 now tells maketime + that no zone was given. + +Mon Feb 17 23:02:28 1992 Paul Eggert <eggert@twinsun.com> + + Version 5.6.3 (beta) + + * ci.c, co.c, rcs.c, rcsbase.h, rcsclean.c, rcsedit.c, rcsutil.c: + Add -T. + * ci.c: `-rREV' now just specifies a revision REV; + only bare `-r' reverts to default. + + * rcsdiff.c: Output more readable context diff headers. + Suppress needless checkout and comparison of identical revisions. + + * maketime.c: + When setting a file's modification time, set its access time to now. + + * rcsbase.h, rcslex.c, rcsutil.c: Work around NFS mmap SIGBUS problem. + + * rcsfnms.c: `a/RCS/b/c' is now an RCS file with an empty extension, + not just `a/b/RCS/c'. + + * conf.sh: Add has_utimbuf to avoid echo line length limit. + Some sigactions don't allow 2nd arg of type struct sigaction const *. + * conf.heg: Adjust to latest conf.sh. + + * rcsfreeze.sh: Use `rcs -nN:' to simplify the code. + +Mon Jan 27 16:42:53 1992 Paul Eggert <eggert@twinsun.com> + + Version 5.6.2 (beta) + + * ci.c: Always unlock branchpoint if caller has a lock. + * ci.c, conf.sh, rcsedit.c: Support bad_chmod_close. + * ci.c, co.c, conf.sh, rcs.c, rcsbase.h, rcsclean.c, rcsedit.c, + rcsfnms.c, rcsgen.c: Support bad_creat0. + * ci.c, co.c, ident.c, merge.c, rcs.c, rcsbase.h, rcsclean.c, + rcsdiff.c, rcsmerge.c, rlog.c, rcsutil.c: lint -> RCS_lint + + * rcs.c: Add -M. + Avoid invoking umask(); it's one less thing to configure. + + * rcsdiff.c: Add GNU diff 1.15.2's new options. + + * rlog.c: Don't duplicate unexpected_EOF's function. + + * maketime.c: Fix day of week bug with dates after February 28, 2100. + + * rcsfnms.c: Fix bug: Expand and Ignored weren't reinitialized. + Avoid `char const c=ch;' compiler bug. + + * rcsgen.c: Move put routines here from rcssyn.c. + * rcssyn.c: Move put routines to rcsgen.c. + + * rcsutil.c: Work around NFS mmap bug that leads to SIGBUS core dumps. + + * Makefile: Add DESTRCSDIR. lint -> RCS_lint + + * conf.sh: Add workaround for `volatile sig_atomic_t' screwups, + and for Ultrix 4.2 prototype brain damage. + * conf.heg: Adjust to latest conf.sh. + +Thu Jan 9 19:02:09 1992 Paul Eggert <eggert@twinsun.com> + + * Makefile: $(LDLIBS) -o $@ -> -o $@ $(LDLIBS) to keep MS-DOS happy. + +Mon Jan 6 03:04:24 1992 Paul Eggert <eggert@twinsun.com> + + Version 5.6.1 (beta) + + * ci.c: Invoke utime() before chmod() to keep some buggy systems happy. + + * co.c, merge.c, rcsdiff.c, rcsmerge.c, rlog.c: Update usage string. + + * rcs.c: + Avoid changing RCS file in common cases where no change can occur. + + * rcsbase.h, rcsedit.c, rcsfnms.c, rcsrev.c, rcssyn.c, rcsutil.c, + rlog.c: while (E) ; -> while (E) continue; + + * rcsedit.c: Add setmode parameter to chnamemod. + addsymbol now reports changes. + + * rcsfnms.c: Shorten long (>31 chars) name. + + * rcslex.c: Use OPEN_O_BINARY if mode contains 'b'. + * rcsutil.c: O_BINARY -> OPEN_O_WORK + + * rcssyn.c: ULONG_MAX/10 -> ULONG_MAX_OVER_10 + + * Makefile: Remove obsolete AIX comment. sh -> $(SHELL). + + * conf.sh: Simplify MS-DOS port; work around several porting anomalies. + * conf.heg: Adjust to latest conf.sh. + + * rcstest: mkdir RCS if it doesn't already exist. + +Wed Nov 20 18:21:10 1991 Paul Eggert <eggert@twinsun.com> + + Version 5.6 released. + * ci.c, rcs.c: Don't read the delta tree from a nonexistent RCS file. + * rcsmerge.c: Don't Iopen(f, "r+"); it's not portable. + * conf.sh, conf.heg: Remove FOPEN_RPLUS. + * rcstest: Test for rcs -i -A bug. + +Sun Nov 3 03:30:44 1991 Paul Eggert <eggert@twinsun.com> + + * rcsclean.c: + Don't remove working files that look like they are checked out. + + * rcsedit.c: Move the warning about link breaking + to where they're actually being broken. + + * rcslex.c: Fix porting bug to ancient hosts lacking vfprintf. + + * conf.sh: Detect yet another const compiler bug. + Don't declare args to mmap, strchr, strrchr, in case they disagree. + +Mon Oct 7 17:32:46 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c, rcs.c, rcsdiff.c: Remove lint. + * ci.c, rcsgen.c: Fix log bugs, e.g. ci -t/dev/null when has_mmap. + * co.c: -k affects just working file, not RCS file. + * rcsfcmp.c: Count log lines correctly. + * conf.sh: Add declare_signal, large_memory, O_EXCL. Remove creat. + * conf.heg: Adjust to latest conf.sh. + * rcsbase.h, rcsedit.c, rcslex.c, rcsutil.c: + Support piece tables even if !has_mmap. + * rcsedit.c: Fix rare NFS bugs. + * Makefile: Fix Minix description. + +Thu Sep 26 23:10:30 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c: Plug file descriptor leak. + +Tue Sep 24 00:49:12 1991 Paul Eggert <eggert@twinsun.com> + + * rcsfnms.c: Don't export bindex(). + * rcslex.c: Don't export Ierror(), errsay(). + * rcsbase.h: Remove unexported functions. + * Makefile: Put MS-DOS support into a separate distribution. + +Wed Sep 18 07:29:10 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c: Work around a common ftruncate() bug. + +Tue Sep 17 19:07:40 1991 Paul Eggert <eggert@twinsun.com> + + * rlog.c: Getscript() didn't uncache partial lines. + * rcsedit.c: SGI readlink() yields ENXIO, not EINVAL, for nonlinks. + +Tue Sep 10 22:36:26 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c, rcsgen.c: Fix test for redirected stdin. + + * ident.c: Open files with FOPEN_R, not FOPEN_R_WORK, + because they might be executables, not working files. + + * conf.heg: Adjust to latest conf.sh. + + * Makefile: Prepend $(DIFFPREFIX). to PATH. + Adjust `make clean' for DOS. + +Wed Aug 21 00:29:52 1991 Paul Eggert <eggert@twinsun.com> + + * rcstest: Fix test for non-text diffs; it missed some botched diffs. + +Tue Aug 20 23:05:00 1991 Paul Eggert <eggert@twinsun.com> + + * merger.c: Fix !DIFF3_BIN porting bug. + * conf.sh: Fix porting bugs for #include, SENDMAIL, pwd, + cc with wrong exit status. + +Mon Aug 19 23:17:54 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c: When there are no changes, + revert to previous revision instead of aborting. + * ci.c, co.c, rcs.c: Add -M. + * ci.c, rcs.c, rcsbase.h, rcsedit.c, rcsgen.c: Add piece tables. + * ci.c, rcs.c, rcsdiff.c, rcsmerge.c, rcsrev.c: Add -r$. + * ci.c, co.c, merge.c, rcs.c, rcsbase.h, rcsdiff.c, rcsedit.c, + rcsfcmp.c, rcsfnms.c, rcsgen.c, rcskeep.c, rcslex.c, rcsmerge.c, + rcsrev.c, rcssyn.c, rcsutil.c, rlog.c: Tune. + + * co.c: Warn before removing somebody else's file. + Fix co -j bugs. + + * ident.c: Report read errors immediately. + + * merge.c: Remove lint. + + * rcs.c, rlog.c: Revision separator is `:', not `-'. + + * rcsclean.c: Rewrite from scratch. + + * rcsdiff.c: Add RCSINIT. + + * rlog.c: Check for missing and duplicate logs. + Permit log messages that do not end in newline (including empty logs). + + * rcskeys.c: Say `T const' instead of `const T'; + it's less confusing for pointer types. + (This change was made in other source files too.) + + * rcslex.c: Add eoflex(), mmap support. + + * rcsrev.c: Add `-rB.'. Remove botches like `<now>' from messages. + + * conf.sh: Add ALL_ABSOLUTE, FOPEN_RPLUS, ftell, getlogin_is_secure, + RCS_SHELL, sigmask, strchr, TZ_must_be_set. + Add checks for dirent, ftruncate, link, madvise, memcmp, memcpy, + memmove, mmap, spawn, wait, utimbuf. + Improve checks for const/volatile, va_start. + Remove atoi, has_utime, strncmp, strncpy. + Make NEW_LINK_BEHAVIOR the default. + sgi has seteuid. + Don't try to guess the bad line number by parsing the error message. + * conf.heg: Adjust to latest conf.sh. + + * maketime.c: Add setfiledate, str2time, TZ_must_be_set. + + * merger.c: Plug memory leak. + + * partime.c: Update time zones. + + * rcsbase.h, rcsedit.c: Add NFS bug workarounds. + * rcsedit.c: Catch odd filenames. + + * rcsfnms.c: Fix messages when rcswriteopen fails. + Look in $TMP and $TEMP if $TMPDIR isn't set. + + * rcsmap.c: Don't assume EOF == -1. + + * rcsutil.c: Add spawn() support. + Explicate assumptions about getting invoker's name. + Standardize user-visible dates. + + * Makefile: Improve hooks to make DOS makefile from Unix makefile. + Switch to GNU diff as the default. ${} -> $(). + + * rcstest: Add test for reverting checkin. Ease DOS ports. + Add PATH warning. + +Sat Jun 22 22:43:23 1991 Paul Eggert <eggert@twinsun.com> + + * rcsclean.sh: Unlock the correct version. + +Sun Apr 21 11:59:14 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c, co.c, rcsbase.h, rcs.c, rcsdiff.c, rcsedit.c, + rcsfnms.c, rcsmerge.c, rlog.c: Add -x, RCSINIT. + * ci.c, co.c, conf.sh, Makefile, rcsbase.h, rcs.c, rcsdiff.c, + rcsedit.c, rcsfcmp.c, rcsfnms.c, rcsgen.c, rcslex.c, rcsmerge.c, + rcssyn.c, rlog.c: Add MS-DOS support. + * ci.c, co.c: Ensure that working file is newer than RCS file + after co -[lu]. + + * merge.c, merger.c: Initial revision + * partime.c: Don't put , just before } in initializer. + * rcsedit.c, rcsfcmp.c, rcsfnms.c, rcssyn.c: Fix errno bugs. + * rcskeep.c, rcssyn.c: Disambiguate names on shortname hosts. + * rcskeys.c, rcsmap.c: Don't put , just before } in initializer. + * rcsrev.c: Add tiprev(). + * rcsutil.c: Plug setuid security hole. + * conf.sh, conf.heg: Fix several reported bugs. + * rcstest: Fix porting bugs. + * rcsfreeze.sh: Port to broken shells. + +Sun Mar 17 05:42:57 1991 Paul Eggert <eggert@twinsun.com> + + * rcsclean.sh: Support RCS file names. Add -n. + +Thu Feb 28 19:18:53 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c: 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. + + * rcsbase.h, conf.sh, conf.heg: Try setuid() if seteuid() doesn't work. + + * rcsfcmp.c: Open work file at most once. + + * rcssyn.c: Fix null termination bug in reporting keyword expansion. + + * rcsutil.c: Try setuid() if seteuid() doesn't work. + Check the results more carefully. + + * Makefile: Remove setuid installation -- it's now up to the users. + +Tue Feb 26 17:48:39 1991 Paul Eggert <eggert@twinsun.com> + + * ci.c: getdate -> getcurdate (SVR4 name clash) + + * ident.c: Don't report empty keywords. Check for I/O errors. + + * rcs.c: 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability + * rcs.c, rlog.c, rcslex.c, rcsutil.c: + strsave -> str_save (DG/UX name clash) + + * rcsmerge.c: Merging a revision to itself is no longer an error. + + * rlog.c: Survive RCS files with missing logs. + + * rcsbase.h: Move ANSI C / Posix declarations into conf.sh. + * rcsbase.h, rcsedit.c, rcsfnms.c: Support new link behavior. + + * rcsedit.c: Work around broken "w+" fopen. + * rcsedit.c, rcsfnms.c, rcsutil.c: Fix setuid bug. + + * rcsfnms.c: Support tempnam. Define more portable getcwd(). + + * rcslex.c: Work around fputs bug. + + * rcsrev.c: Avoid overflow when comparing revision numbers. + + * rcssyn.c: Check diff output more carefully; avoid overflow. + + * rcsutil.c: Use fread, fwrite more portably. + Support waitpid. Don't assume -1 is acceptable to W* macros. + + * Makefile: Remove setgid support. Add NEW_LINK_BEHAVIOR. + + * conf.sh: Revamp extensively for portability. + * conf.heg: Adjust to latest conf.sh. + + * rcstest: Work even if diff -c and diff3 -E don't. + Improve handling of binary files and bad locks. + +Mon Dec 31 01:00:12 1990 Paul Eggert <eggert@twinsun.com> + + Version 5.5 released. + * ci.c: Don't use uninitialized storage when handling -{N,n}. + +Sun Dec 30 05:07:35 1990 Paul Eggert <eggert@twinsun.com> + + * rcsedit.c: + Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). + +Thu Dec 27 19:54:26 1990 Paul Eggert <eggert@twinsun.com> + + * rcsgen.c: Fix bug: rcs -t inserted \n, making RCS file grow. + +Tue Dec 18 17:19:21 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.c: Fix bug with multiple -n and -N options. + +Thu Dec 13 06:54:07 1990 Paul Eggert <eggert@twinsun.com> + + * rcsdiff.c, Makefile: GNU diff 1.15 has -u. + +Tue Dec 4 05:18:49 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c, co.c, rcs.c, rcsbase.h, rcsgen.c, rcslex.c, rcsutil.c: + Use -I for prompts and -q for diagnostics. + + * co.c: Don't checkaccesslist() unless necessary. + + * rcsutil.c: Don't output a blank line after a signal diagnostic. + +Tue Nov 27 09:26:05 1990 Paul Eggert <eggert@twinsun.com> + + * rcsfcmp.c: Fix comment leader bug. + +Sun Nov 11 00:06:35 1990 Paul Eggert <eggert@twinsun.com> + + * rcs.c: Fix `rcs -e' core dump. + +Mon Nov 5 20:30:10 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: Don't remove working file when aborting due to no changes. + +Thu Nov 1 05:28:48 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c, rcs.c: Add -I and new -t behavior. + * ci.c, rcs.c, rcsbase.h, rcsedit.c, rcsgen.c, rcsfcmp.c, rcsfnms.c, + rcssyn.c, rlog.c: Permit arbitrary data in logs and comment leaders. + + * co.c: Fix -j. Add -I. + + * merge.sh: Yield exit status 2 on error like the man page says. + + * rcsdiff.c, rcsmerge.c, rcsutil.c: Remove unneeded setid check. + + * maketime.c: Remove lint. + + * rcsbase.h: Don't assume that builtins are functions; + they may be macros. + + * rcsgen.c: Add -I and new -t behavior. + + * rcslex.c, rcssyn.c: + When ignoring unknown phrases, copy them to the output RCS file. + + * rcssyn.c: Don't check for nontext on initial checkin. + + * rcsutil.c: Add awrite(), fremember(). + + * Makefile: Add test and debug instructions. + Remove unwieldy "make depend" stuff. + + * conf.sh: Use better guess for Posix 1003.1-1990 seteuid() behavior. + (A real test would requiring running conf.sh setid. Ugh!) + Use test for vfork() that detects GCC 1.37.1+sparc and IRIX bugs. + Use better test for const and volatile. + User better test for include files; just running cpp isn't enough, + because the include files may be buggy. + Don't assume that builtins are functions; they may be macros. + Remove DIFF_ARBITRARY. + * conf.heg: Initial revision + + * rcsclean.sh: Basically a complete rewrite. Add rcsdiff options. + Clean working directory if no arguments are given. + + * rcsfreeze.sh: Check for errors better. + Remove the need for one of the temporary files. + +Sat Oct 6 00:16:45 1990 Paul Eggert <eggert@twinsun.com> + + * rcsutil.c: Don't fread F if feof(F). + +Thu Oct 4 06:30:22 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c, co.c, rcs.c, rcsdiff.c, rlog.c: + Accumulate exit status across files. + + * maketime.c: Don't assume time_t is 32 bits. + Calculate the GMT offset of 'xxx LT' as of xxx, not as of now. + Fix bugs near epoch and near end of time. + + * partime.c: Check for overflow when lexing an integer. + Remove date vs time heuristics that fail between 2000 and 2400. + Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'. + + * rcskeep.c: Parse time zone offsets; + future RCS versions may output them. + +Thu Sep 27 01:31:43 1990 Paul Eggert <eggert@twinsun.com> + + * rcsdiff.c: Yield 1, not EXIT_FAILURE, when diffs are found. + * rcsbase.h, conf.sh: Port wait() to non-Posix ANSI C hosts. + * conf.sh: Test for vfork() file descriptor screwup. + +Tue Sep 25 20:11:46 1990 Adam Hammer <hammer@cs.purdue.edu> + + * ci.c: fixed another small typo + +Mon Sep 24 18:56:31 1990 Paul Eggert <eggert@twinsun.com> + + * partime.c: Update time zones. + +Fri Sep 21 06:16:38 1990 Adam Hammer <hammer@cs.purdue.edu> + + * ci.c: made it handle multiple -{N,n}'s. + Also, made it treat re-directed stdin the same as the terminal + +Thu Sep 20 07:58:32 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: ci -k now checks dates more thoroughly. + + * merge.sh: Fix bug in passing arguments to old diff3 programs. + Fix calculation of #!/bin/sh header. + + * rcskeep.c: ci -k now checks dates more thoroughly. + + * rcssyn.c: + Remove the test for non-text bytes; it caused more pain than it cured. + + * conf.sh: Calculate signal_type more reliably on older systems. + + * rcstest: Adjust to old and brain damaged diffs. + +Sat Sep 15 01:29:51 1990 Paul Eggert <eggert@twinsun.com> + + * conf.sh: Output header warning that it's generated automatically. + +Tue Sep 11 02:41:16 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: Fix revision bug with `ci -k file1 file2'. + * co.c: co -kv yields a readonly working file. + * rcsdiff.c: Simplify -kkvl test. + * rlog.c: Plug memory leak. + * rcsedit.c: Tune expandline(). + * rcsfcmp.c: Don't ignore differences in keyword strings if -ko is set. + * Makefile: Don't make -R the default. + +Tue Sep 4 17:07:19 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: Permit adjacent revisions with identical time stamps + (possible on fast hosts). + * ci.c, rcsbase.h, rcsedit.c: Improve incomplete line handling. + * ci.c, co.c, rcs.c, rcsbase.h, rcsgen.c: + Standardize yes-or-no procedure. + + * rcsdiff.c: Diff's argv was too small by 1. + + * rlog.c, rcsedit.c, rcslex.c, rcssyn.c: Count RCS lines better. + + * rcsmerge.c: Check for I/O error when reading working file. + + * partime.c: Don't permit 'Aug Aug'. + Don't parse two-digit years, because it won't work after 1999/12/31. + + * rcsbase.h: Don't redefine NAME_MAX, PATH_MAX. + + * rcsfnms.c: Fix typo when !RCSSEP. + + * rcsgen.c: Fix `co -p1.1 -ko' bug. + + * rcssyn.c: Parse RCS files with no revisions. + Don't strip leading white space from diff commands. + + * rcsutil.c: Store fread()'s result in an fread_type object. + + * conf.sh: Don't include <stdarg.h> unless has_prototypes is set. + + * rcstest: Test -kkv, etc. + Work even when today's day of the month is less than 10. + +Wed Aug 29 07:14:52 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: Expand locker value like co. + * ci.c, rcs.c, rcsbase.h, rcsfcmp.c, rcsgen.c, rcssyn.c: + Clean old log messages too. + + * merge.sh: Add -q. + + * rcs.c: Remove unused setuid support. + + * rcsdiff.c, rcsbase.h rcsedit.c, rcssyn.c: Add -kkvl. + + * rcsmerge.c: Add -q. Pass -L options to merge. + + * partime.c: Be able to parse our own date format. + Don't assume year<10000. + + * rcsbase.h: Fix type typos exposed by porting. + + * rcsedit.c: + Fix bug when getting revisions to files ending in incomplete lines. + Fix bug in comment leader expansion. + + * rcsfnms.c, rcslex.c: + Work around buggy compilers with defective argument promotion. + + * rcsutil.c: Declare getpwuid() more carefully. + + * Makefile: Remove -g from default CFLAGS. + + * conf.sh: Fix porting bugs. Add sanity checks and better doc strings. + + * rcstest: Initial revision + +Wed Aug 22 08:49:14 1990 Paul Eggert <eggert@twinsun.com> + + * ci.c: Don't pass +args to diff. + * ci.c, co.c, rcsbase.h, rcsdiff.c, rcsedit.c, rcskeys.c, rcssyn.c: + Add -k. + * ci.c, co.c, rcs.c, rcsbase.h, rcsdiff.c, rcsedit.c, rcsmerge.c, + rcsutil.c, rlog.c: Add -V. + * ci.c, co.c, ident.c, rcs.c, rcsbase.h, rcsdiff.c, rcsedit.c, + rcsfnms.c, rcsgen.c, rcskeep.c, rcslex.c, rcsmerge.c, rcsrev.c, + rcssyn.c, rcsutil.c, rlog.c: + Remove compile-time limits; use malloc instead. + * ci.c, co.c, rcs.c, rcsbase.h, rcsdiff.c, rcsutil.c, rlog.c: + Add setuid support. + * ci.c, rcsedit.c, rcskeep.c, rcssyn.c: Don't require a final newline. + * ci.c, co.c, rcs.c, rcsdiff.c, rcsedit.c, rcsfnms.c, rcsmerge.c: + Make lock and temp files faster and safer. + * ci.c, co.c, maketime.c, rcs.c, rcsedit.c, rcsfnms.c, rcskeep.c, + rcsutil.c: Permit dates past 1999/12/31. + * ci.c, co.c, rlog.c, maketime.c, partime.c, rcsutil.c, Makefile: + Switch to GMT and fixed the bugs exposed thereby. + * ci.c, rcs.c, rcsedit.c, rcssyn.c: Check diff's output. + * ci.c, co.c, rcsbase.h, rcsutil.c: Remove snooping. + * ci.c, co.c, rcs.c, rcsfnms.c, rcskeep.c, rcsmerge.c, rcsrev.c, + rcssyn.c, rcsutil.c: Tune. + * ci.c, co.c, ident.c, maketime.c, partime.c, rcs.c, rcsbase.h, + rcsdiff.c, rcsedit.c, rcsfcmp.c, rcsfnms.c, rcsgen.c, rcskeep.c, + rcskeys.c, rcslex.c, rcsmerge.c, rcsrev.c, rcssyn.c, rcsutil.c, rlog.c: + Ansify and Posixate. + + * co.c, rcsutil.c: Permit multiple locks by same user. + + * ident.c: Don't limit output to known keywords. + * ident.c, rcs.c, rcsfcmp.c, rcsrev.c, rcssyn.c: Remove lint. + + * merge.sh: Become a shell file that outputs the needed shell file. + Support GNU diff3. Add -L. + + * rcs.c: Don't lose names when applying -a option to multiple files. + Fix umask bug. Yield proper exit status. + + * rcsdiff.c: Don't pass arguments with leading '+' to diff; + GNU DIFF treats them as options. Add GNU diff's flags. + * rcsdiff.c, rcsbase.h, rcsmerge.c, rcsfnms.c: Don't use access(). + + * rcsmerge.c: Propagate merge's exit status. + + * rlog.c: + Report dates in long form, to warn about dates past 1999/12/31. + Change "added/del" message to make room for the longer dates. + * rlog.c, rcsedit.c: Don't generate trailing white space. + + * partime.c: Update time zones. Fix peekahead and int-size bugs. + + * rcsbase.h: Adjust ANSI C / Posix support. Remove v2 support. + * rcsbase.h, rcslex.c, rcssyn.c: Add support for ISO 8859. + + * rcsedit.c, rcsfcmp.c: Don't append "checked in with -k by " log to + logs, so that checking in a program with -k doesn't change it. + + * rcsfnms.c: Ignore signals when manipulating the semaphore file. + Modernize list of file name extensions. + Beware file names beginning with "-". + Fix test for non-regular files. + + * rcskeep.c: Retrieve old log message if there is one. + + * rcslex.c: Report errno-related errors with perror(). + Use better hash function. + + * rcsrev.c: Remove possibility of an internal error. + + * rcssyn.c: Try to parse future RCS formats without barfing. + Don't output branch keyword if there's no default branch, + because RCS version 3 doesn't understand it. + Check that a newly checked-in file is acceptable as input to 'diff'. + + * rcsutil.c: 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. + + * Makefile: Configure more automatically. Upgrade for RCS 5.0. + + * conf.sh, rcsmap.c: Initial revision + +Wed May 23 06:54:32 1990 Paul Eggert <eggert@twinsun.com> + + * Makefile: Make Posix the default; it's the wave of the future. + Add advice for SunOS. Remove references to rcsclean, rcsfreeze. + +Thu Mar 22 07:06:14 1990 Paul Eggert <eggert@twinsun.com> + + * merge.sh: Add support for GNU DIFF 1.15. + + * Makefile: Permit dates past 1999/12/31. Ansify and Posixate. + Remove snoop and v2 support. Add <vfork.h> flag. + +Sat Nov 18 13:34:16 1989 Adam Hammer <hammer@cs.purdue.edu> + + Version 4.3 released. + +Mon Oct 30 12:29:00 1989 Daniel Trinkle <trinkle@cs.purdue.edu> + + * rcsclean.c: + Added -q option to agree with man page, added code to actually unlock + the RCS file if there were no differences, picked a bit of lint. + +Wed Jun 28 12:56:23 1989 Paul Eggert <eggert@twinsun.com> + + * rcsfnms.c: + Prevent checkin of all non-regular files, not just directories. + +Wed Jun 28 09:02:14 1989 Thomas Narten <narten@cs.purdue.edu> + + Version 4.2 released. + +Mon May 1 15:17:14 1989 Thomas Narten <narten@cs.purdue.edu> + + changed copyright header to reflect current distribution rules + + * rcsbase.h: changed #include <strings.h> -> string.h for USG systems. + * partime.c: fixed #ifdef DEBUG construct + * rcsfnms.c: changed getwd to not stat empty directories. + +Thu Dec 8 14:02:20 1988 Paul Eggert <eggert@sm.unisys.com> + + * rcsfnms.c: + Fix bug: "co -p .../RCS/dir,v" failed if ./dir is a directory. + +Tue Nov 8 16:01:02 1988 Thomas Narten <narten@cs.purdue.edu> + + * ci.c: changes from root@seismo.CSS.GOV (Super User) + -d with no arguments uses the mod time of the file it is checking in + + * rcs.c: removed include <sysexits.h> (not needed) + minor fix for -A option + + * rcsbase.h: removed defs for functions loaded from libraries + * maketime.c: allow negative time zones (-24h <= x <= 24h) + * rcsedit.c: misplaced semicolon caused infinite loop + * rcsutil.c: corrected use of varargs routines + +Sun Aug 28 15:09:38 1988 Paul Eggert <eggert@sm.unisys.com> + + * Makefile: 'make depend' now discards /usr/include/* + (#ifdefs may comment around them). + Use sed rather than ex for portability. Remove obsolete comments. + Separate LINTFLAGS. Use LDFLAGS consistently. + Edit DIFF in merge.sh. Use execv(), not system(); allow cc -R. + Permit signal handlers to yield either 'void' or 'int'. + + * rcslex.c: + 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. + + * rcsgen.c: Shrink stdio code size; allow cc -R; remove lint; + isatty() -> ttystdin() + + * partime.c: Remove unportable "#endif XXX"s. + + * maketime.c: Allow cc -R. Remove unportable "#endif XXX"s. + +Tue Aug 9 19:13:28 1988 Paul Eggert <eggert@sm.unisys.com> + + * ci.c: Remove lint. + isatty(fileno(stdin)) -> ttystdin() + Make sure workfile is a regular file; + use its mode if RCSfile doesn't have one. + * ci.c, rcsdiff.c, rcsedit.c: Allow cc -R. + + * co.c: Fix "co -d" core dump; rawdate wasn't always initialized. + fix putchar('\0') and diagnose() botches. + * co.c, rcs.c, rcsbase.h, rcskeep.c, rcssyn.c, rlog.c: Remove lint. + + * merge.sh: + Check and yield proper exit status; keep temporary files private; + economize to avoid a temporary file; fix "1 overlaps"; + keep '.' out of PATH. + + * rcsdiff.c, rcsmerge.c: Yield exit status like diff(1)'s. + + * rcsmerge.c: Beware merging into a readonly file. + Beware merging a revision to itself (no change). + + * rcs.c: Don't access freed storage. + Yield proper exit status. + + * rlog.c: Check for memory exhaustion; don't access freed storage. + Shrink stdio code size. + * rlog.c, rcsutil.c: Check for memory exhaustion. + + * rcsbase.h: Permit -Dhshsize=nn. + * rcsbase.h, rcsedit.c, rcsfcmp.c: Shrink stdio code size. + + * rcskeep.c: Speed up by making FILE *fp local, not global. + + * rcsutil.c: Permit signal handlers to yield either 'void' or + 'int'; fix oldSIGINT botch. Yield exit status like diff(1)'s. + +Wed Jul 27 00:31:11 1988 Paul Eggert <eggert@sm.unisys.com> + + * merge.sh: RCS version 4 + +Thu Jun 23 16:06:55 1988 Paul Eggert <eggert@sm.unisys.com> + + * sig_t.sh: Initial revision + +Fri Dec 18 17:06:41 1987 Thomas Narten <narten@cs.purdue.edu> + + * ci.c, co.c, rcs.c, rcsfcmp.c, rcskeep.c, rcssyn.c, rlog.c: + lint cleanups (from Guy Harris) + + * rcsdiff.c: 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. + + * maketime.c: include rcsparam.h + + * rcsbase.h: made removed BSD ifdef, now uses V4_2BSD + + * rcsedit.c: + Changes from the 43. version. Don't know the significance of the + first change involving "rewind". Also, additional "lint" cleanup. + (Guy Harris) + + * rcsfnms.c: + additional file types added from 4.3 BSD version, and SPARC assembler + comment character added. Also, more lint cleanups. (Guy Harris) + + * rcsgen.c: + 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) + + * rcslex.c: fixed to use "varargs" in "fprintf"; + this is required if it is to work on a SPARC machine such as a Sun-4 + + * rcsrev.c: more lint cleanups. + Also, the NOTREACHED comment is no longer necessary, + since there's now a return value there with a value. (Guy Harris) + + * Makefile: RCS version 4.0's Makefile + + * rcsfreeze.sh: Initial revision + +Mon Nov 23 16:06:50 1987 Paul Eggert <eggert@sm.unisys.com> + + * Makefile: Permit signal handlers to yield either 'void' or 'int'. + +Fri Oct 23 17:09:57 1987 Thomas Narten <narten@cs.purdue.edu> + + * ident.c: added exit(0) so exit return code would be non random + +Tue Sep 15 16:39:39 1987 Roy Morris <...!felix!roy> + + * rcsedit.c: added an initialization 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) + +Sun Oct 18 10:40:22 1987 Thomas Narten <narten@cs.purdue.edu> + + Updating version numbers. + Changes relative to revision 1.1 are actually relative to 4.x. + +Mon Aug 24 13:57:19 1987 Thomas Narten <narten@cs.purdue.edu> + + Sources now pass through lint + (if you ignore printf/sprintf/fprintf warnings). + +Mon Aug 10 11:52:53 1987 Paul Eggert <eggert@sm.unisys.com> + + * co.c: 'co -d' dumped core; this fixes it. + +Thu Jul 9 09:20:52 1987 Daniel Trinkle <trinkle@cs.purdue.edu> + + * ident.c: 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 dereferencing null pointers (i.e. Suns). + +Mon Jun 22 13:52:35 1987 Paul Eggert <eggert@sm.unisys.com> + + * Makefile: + Allow cc -R; add rclm; add $(LDFLAGS) when linking anything + + * rcs.c, rlog.c: Don't access freed storage. + + * rcs.c: If you lack locks, "rcs -u" now breaks the newest lock + regardless of owner. + +Fri Mar 27 14:21:53 1987 jenkins <jenkins@cs.purdue.edu> + + Port to suns + +Mon Mar 23 12:01:04 1987 Paul Eggert <eggert@sm.unisys.com> + + * ci.c, co.c, rcs.c, rcsdiff.c, rcsmerge.c, rcsutil.c: + Use execv(), not system(); yield proper exit status. + * co.c: fix putchar('\0') and diagnose() botches. + * rcsutil.c: Fix bug: catchints() didn't work if ignoreints() + had already been called. + * rcsmerge.c: Beware merging into a readonly file. + Beware merging a revision to itself (no change). + * rcsbase.h: Shrink stdio code size; remove lint; vfork=fork on USG + * merge.sh: + Check and yield proper exit status; clean up if interrupted; + keep temporary files private; fix "1 overlaps" message. + * ci.c, rcsdiff.c: Allow cc -R. + * co.c, rcs.c: Remove lint. + +Tue Mar 17 17:27:32 1987 loverso <loverso@cs.purdue.edu> + + * ci.c: Don't allow non-regular files (directories, etc) + to be checked in. + +Mon Mar 9 17:11:52 1987 Paul Eggert <eggert@sm.unisys.com> + + * rcsedit.c: Shrink stdio code size; allow cc -R. + Fix linecorr botch: dumped core on Suns sometimes on 'co -rold f1 f2'. + +Fri Feb 13 11:28:51 1987 Paul Eggert <eggert@sm.unisys.com> + + * rlog.c: Add -W flag. + * rcslex.c: Don't loop when writing error messages to a full filesystem + Flush stderr/stdout when mixing output. + Yield exit status compatible with diff(1). + * rcsgen.c, rcslex.c, rlog.c: Allow cc -R; remove lint. + * rcsfcmp.c, rcsgen.c, rcslex.c, rlog.c: Shrink stdio code size. + +Thu Feb 12 18:24:19 1987 Paul Eggert <eggert@sm.unisys.com> + + * ident.c: Add -efghi flags. + * maketime.c, rcsfnms.c, rcssyn.c, sccstorcs.c: Allow cc -R. + * rcsfnms.c: Fix troff macro comment leader bug; add Prolog. + * rcskeep.c: Speed up by making FILE *fp local, not global. + * rcsrev.c, All files above: Remove lint. + +Tue Jul 29 12:17:25 1986 Guy Harris <guy@sun.com> + + * maketime.c: More "lint" cleanups. + +Mon Jul 28 23:34:14 1986 Guy Harris <guy@sun.com> + + * Makefile: Netnews fixes from guy@sun + * sccstorcs.c, rlog.c, rcsutil.c, rcssyn.c, rcsrev.c, rcsmerge.c, + rcslex.c, rcskeep.c, rcsgen.c, rcsfnms.c, rcsfcmp.c, rcsedit.c, + rcsdiff.c, rcsbase.h, rcs.c, ident.c, co.c, ci.c: More "lint" cleanups. + +Thu Jul 3 13:09:55 1986 jdl <jdl@cs.purdue.edu> + + * rcsclean.sh: New file. + +Wed Jun 18 15:02:32 1986 Jay Lepreau <lepreau@cs.purdue.edu> + + * rcsdiff.c: I broke -c<number> in last rev. + Also, mixing a bopt with an otheropt was broken. + +Mon May 19 02:36:16 1986 Jay Lepreau <lepreau@cs.purdue.edu> + + * rcsdiff.c: Pass on new diff options, and allow them to be clustered. + +Thu May 15 02:25:37 1986 Jay Lepreau <lepreau@cs.purdue.edu> + + * Makefile: 4.3BSD revision + * rcsedit.c: Use "Locked" instead of state in $Head expansion if locked. + * rcsfnms.c: add suffix .el for gnulisp + * rcsgen.c: Fix immediate EOF from non-tty files: + avoid 0377's in description. + +Tue Dec 17 13:59:09 1985 albitz <albitz@cs.purdue.edu> + + * rcs.c: Changed setstate to rcs_setstate + because of conflict with random.o. + +Wed Jun 26 07:34:28 1985 svb <svb@cs.purdue.edu> + + * rcsfnms.c: Comment leader '% ' for '*.tex' files added. + +Wed Oct 17 21:12:11 1984 Admin <root@cs.purdue.edu> + + * sccstorcs.c: Added check for having multiple deltas in a row + for the same revision. --ks + +Tue Dec 20 16:04:20 1983 Walter F. Tichy <wft@purdue> + + * rcsbase.h: Increased logsize, added macro SMALLOG. + moved setting of STRICT_LOCKING to Makefile. + changed DOLLAR to UNKN (conflict with KDELIM). + +Thu Dec 15 12:28:54 1983 Walter F. Tichy <wft@purdue> + + * ci.c, co.c: ci -u and ci -l now set mode of working file properly. + * rcs.c: rcs -u now breaks most recent lock + if it can't find a lock by the caller. + + * rcsfnms.c: Added check for KDELIM in filenames to pairfilenames(). + +Mon Dec 5 13:40:54 1983 Walter F. Tichy <wft@purdue> + + * ci.c: Merged with 3.9.1.1: added calls to clearerr(stdin). + * ci.c, co.c, rlog.c: made rewriteflag external. + + * rcs.c: Added conditional compilation for sending mail. + Alternatives: V4_2BSD, V6, USG, and other. + +Fri Dec 2 22:47:45 1983 Walter F. Tichy <wft@purdue> + + * rcsfnms.c: Added csh, red, and sl filename suffixes. + +Wed Oct 19 04:22:11 1983 Jay Lepreau <lepreau@purdue> + + * rcsbase.h: Make teeny logsize big + * rcsgen.c: Added clearerr(stdin) for re-reading description from stdin. + +Thu May 12 13:10:30 1983 Walter F. Tichy <wft@purdue> + + * rcsedit.c: + 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. + Log no longer expands full path of RCS file. + +Wed May 11 14:24:13 1983 Walter F. Tichy <wft@purdue> + + * rlog.c: Added -b, updated getnumericrev() accordingly. + Replaced getpwuid() with getcaller(). + Added options -L and -R; Fixed selection bug with -l on multiple files. + Fixed error on dates of the form -d'>date' (rewrote getdatepair()). + + * rcsedit.c: Fixed truncation problem for $19chars followed by@@. + Added retry to expandline to resume after failed match which ended in $. + + * rcsfnms.c: Added initialization of Dbranch to InitAdmin(). + Changed pairfilenames(): + 1. added copying of path from workfile to RCS file, if RCS file omitted; + 2. added getting the file status of RCS and working files; + 3. added ignoring of directories. + Added comtable[] which pairs filename suffixes with comment leaders; + updated InitAdmin() accordingly. + +Tue May 10 17:03:06 1983 Walter F. Tichy <wft@purdue> + + * ci.c: Added option -d and -w, and updated assignment of date, etc. + to new delta. + Option -k generates std. log message; + fixed undef. pointer in reading of log. + Replaced getlock() with findlock(), + getpwuid() with getcaller(). + Moved all revision number generation to new routine addelta(). + Removed calls to stat(); now done by pairfilenames(). + Directed all interactive messages to stderr. + * ci.c, co.c: Added handling of default branches. + Replaced getpwuid() with getcaller(). + Replaced unlink()--link() with rename() + * ci.c, co.c, rcs.c: + Removed calls to stat(); now done by pairfilenames(). + Changed most calls to catchints() with restoreints(). + + * co.c: Added option -u and -f. + Changed and renamed rmoldfile() to rmworkfile(). + + * rcs.c: + Simplified breaklock(); added calls to findlock() and getcaller(). + Added option -b (default branch). Updated -s and -w for -b. + Removed check for exit status of delivermail(). + Directed all interactive output to stderr. + + * ident.c: Added option -q and input from reading stdin. + Marker matching is now done with trymatch() (independent of keywords). + + * rcsfcmp.c: Marker matching now uses trymatch(). + Marker pattern is now checked precisely. + + * rcsgen.c: Changed putamin() to abort if trying to reread redirected + stdin. Fixed getdesc() to output a prompt on initial newline. + + * rcskeep.c: Added new markers Id and RCSfile; extraction added. + Marker matching with trymatch(). + + * rcsutil.c: 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(). + +Wed May 4 09:12:41 1983 Walter F. Tichy <wft@purdue> + + * rcsbase.h: Added markers Id and RCSfile. + Added Dbranch for default branches. + + * rcskeys.c: Initial revision + +Tue May 3 22:13:19 1983 Walter F. Tichy <wft@purdue> + + * rcsdiff.c: Added default branch, option -q, exit status like diff. + Added fterror() to replace faterror(). + +Tue Apr 5 14:47:36 1983 Walter F. Tichy <wft@purdue> + + * rcsfnms.c: fixed Suffix in InitAdmin(). + +Mon Mar 28 11:14:57 1983 Walter F. Tichy <wft@purdue> + + * rcsmerge.c, rcssyn.c: Added handling of default branch. + +Fri Mar 25 18:12:51 1983 Walter F. Tichy <wft@purdue> + + * rcslex.c, rcsrev.c: Only changed $Header to $Id. + +Fri Feb 18 17:37:49 1983 Walter F. Tichy <wft@purdue> + + * ident.c: removed printing of new line after last file. + +Tue Feb 15 15:41:49 1983 Walter F. Tichy <wft@purdue> + + * ci.c, co.c, rcs.c, rcsutil.c: Added call to fastcopy() + to copy remainder of RCS file in blocks. + +Tue Jan 18 18:04:25 1983 Walter F. Tichy <wft@purdue> + + * rcs.c: Changed sendmail(): now uses delivermail, + and asks whether to break the lock. + +Mon Jan 17 18:01:04 1983 Walter F. Tichy <wft@purdue> + + * rcsfnms.c: Added getwd() and rename(); these can be removed by + defining V4_2BSD, since they are not needed in 4.2 bsd. + Changed sys/param.h to sys/types.h. + +Sat Jan 15 17:46:50 1983 Walter F. Tichy <wft@purdue> + + * rcs.c: 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. + + * rcsdiff.c: Expanded mainprogram to handle multiple RCS files. + + * rcsbase.h: 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. + + * rcssyn.c: Changed readdelta() to initialize selector and log-pointer. + Changed puttree to check for selector==DELETE; + putdtext() uses DELNUMFORM. + +Fri Jan 14 15:37:31 1983 Walter F. Tichy <wft@purdue> + + * ci.c, co.c, rcs.c: + Added ignoring of interrupts while new RCS file is renamed; + Avoids deletion of RCS files by interrupts. + +Thu Jan 6 09:33:45 1983 Walter F. Tichy <wft@purdue> + + * rcsdiff.c: Fixed passing of -c (context) option to diff. + +Fri Dec 24 15:29:00 1982 Walter F. Tichy <wft@purdue> + + * rcsdiff.c, rcsmerge.c: Added call to catchsig(). + * rlog.c: shortened output format. + * rcskeep.c: added missing #endif. + * rcsutil.c: added catchints(), ignoreints() for catching and ignoring + interrupts; fixed catchsig(). + +Fri Dec 10 16:22:37 1982 Walter F. Tichy <wft@purdue> + + * ci.c, rcs.c, rcsdiff.c: Corrected checking of return code from diff. + * rcs.c: Removed unused variables, + introduced variant COMPAT2 for skipping Suffix on -A files. + * rcsdiff.c, rcslex.c: Improved error messages. + * rcsmerge.c: Replaced getdelta() with gettree(). + * rcsbase.h: Added two forms of DATEFORM, one using %02d, + the other %.2d. + * rcslex.c: Changed exit status on error to 1. + +Wed Dec 8 21:34:49 1982 Walter F. Tichy <wft@purdue> + + * ci.c: Fixed return from addbranch(). + * ci.c, co.c, rcsutil.c, rlog.c: Using DATEFORM to format dates. + + * co.c: removed actual from call to preparejoin; + re-fixed printing of done at the end. + + * rcs.c: Replaced getdelta() with gettree(), + changed breaklock to update field lockedby, added some diagnostics. + + * rlog.c: removed call to checkaccesslist(). + * rlog.c, rcsfnms.c: removed unused variable. + + * rcssyn.c: renamed Commentleader to Commleader. + +Sat Dec 4 13:24:08 1982 Walter F. Tichy <wft@purdue> + + * ci.c: Updated field lockedby in removelock(), + moved getlogmsg() before calling diff. + * ci.c, co.c, rcsutil.c: Replaced SNOOPDIR with SNOOPFILE. + * ci.c, co.c, rcsdiff.c, rcsrev.c, rlog.c: + Replaced getdelta() with gettree(). + * co.c: Fixed printing of "done". + * rcsdiff.c: Changed diagnostics. + * ident.c, rcsbase.h: Added LOCKER. + * rlog.c: Removed updating of field lockedby. + * rcsbase.h: Locker, and USG (redefinition of rindex). + * rcsedit.c: Added expansion of keyword Locker. + * rcsfcmp.c, rcskeep.c: Initial revision + * rcsrev.c: Replaced getdelta() with gettree(). + * rcssyn.c: Added routine gettree(), + which updates keeplock after reading the delta tree. + * rcsutil.c: changed addlock() to update lockedby-field. + +Fri Dec 3 17:08:04 1982 Walter F. Tichy <wft@purdue> + + * rcs.c: + Replaced getlogin() with getpwuid(), fclose() with ffclose(), + /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x). + fixed -u for missing revno. + * rcs.c, rlog.c: Disambiguated structure members. + + * rlog.c: 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. + + * rcsbase.h: + 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. + + * rcsedit.c: 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. + + * rcsutil.c: Added check to addlock() ensuring only one lock per person. + Addlock also returns a pointer to the lock created. + Deleted fancydate(). + +Thu Dec 2 13:27:13 1982 Walter F. Tichy <wft@purdue> + + * ci.c: Added option -k. + +Sun Nov 28 19:45:37 1982 Walter F. Tichy <wft@purdue> + + * ci.c: 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. + * ci.c, co.c: + Replaced fclose() with ffclose(), + %02d with %.2d, getlogin() with getpwuid(). + + * co.c: Replaced mode generation for working file with WORKMODE. + Fixed nil printing. Fixed -j combined with -l and -p, and exit + or non-existing revisions in preparejoin(). + + * ident.c: removed Suffix; + added ungetc to avoid skipping over trailing KDELIM. + + * merge.sh: 4.3BSD revision + + * rcsmerge.c: Initial revision + + * rcsfnms.c: Changed mktempfile() to store the generated filenames. + Changed getfullRCSname() to store the file and pathname, + and to delete leading "../" and "./". + + * rcsgen.c: 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. + + * rcslex.c: 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). + + * rcsrev.c: fixed compartial() and compnum() for nil-parameters; + fixed nils in error messages. Testprogram output shortened. + + * rcssyn.c: Reading and printing of Suffix removed; + version COMPAT2 skips the Suffix for files of release 2 format. + Fixed problems with printing nil. + +Sat Nov 27 12:24:37 1982 Walter F. Tichy <wft@purdue> + + * rcsutil.c: 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. + +Sun Nov 14 14:49:30 1982 Walter F. Tichy <wft@purdue> + + * rcsedit.c: + 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. + +Fri Nov 12 14:29:40 1982 Walter F. Tichy <wft@purdue> + + * rcsfnms.c: changed pairfilenames() to handle file.sfx,v; + also deleted checkpathnosfx(), + checksuffix(), checkfullpath(). Semaphore name generation updated. + mktempfile() now checks for nil path; freefilename initialized properly. + Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. + Moved rmsema, trysema, trydiraccess, getfullRCSname + rom rcsutil.c to here. + +Mon Oct 18 20:57:23 1982 Walter F. Tichy <wft@purdue> + + * ci.c: + Fixed ci -l, added ci -u (both do an implicit co after the ci). + Changed conflicting identifiers. + * ci.c, co.c, rcs.c: Fixed call to getlogin(), added call to + getfullRCSname(). + * ci.c, co.c, rcs.c: An RCS file inherits its mode during the first ci + or rcs -i from the working file, otherwise it stays the same, + except that write permission is removed. + * ci.c, co.c, rcs.c, rcsedit.c, rcsgen.c: Added check for write error. + + * co.c: 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). + + * rcs.c: I replaced curdir() with getfullRCSname(), + cleaned up handling -U/L, and changed conflicting, long identifiers. + + * rcsdiff.c: Initial revision + + * rlog.c: call to curdir replaced with getfullRCSname(), + fixed call to getlogin(), cosmetic changes on output, + changed conflicting long identifiers. + + * rcsbase.h: added macro STRICT_LOCKING, removed RCSUMASK. + renamed JOINFILE[1,2] to JOINFIL[1,2]. + + * rcsedit.c: Renamed expandstring() to xpandstring(). + + * rcsfnms.c: InitAdmin() now initializes StrictLocks=STRICT_LOCKING + (def. in rcsbase.h). renamed checkpath() to checkfullpath(). + + * rcsgen.c: improved the prompt on putdesc(). + + * rcsrev.c: renamed compnum->cmpnum, compnumfld->cmpnumfld, + numericrevno->numricrevno. + + * rcssyn.c: renamed putdeltatext to putdtext. + + * rcsutil.c: added function getfullRCSname(). + +Wed Oct 13 16:04:59 1982 Walter F. Tichy <wft@purdue> + + * ci.c: added include file dbm.h for getting BYTESIZ. + This is used to check the return code from diff portably. + * ci.c, co.c, ident.c, rcs.c, rcsedit.c, rcsgen.c, rlog.c: + fixed type of variables receiving from getc() (char -> int). + + * co.c: removed unused variables. + + * rcsedit.c: made keyword expansion loop in expandline() + portable to machines without sign-extension. + + * rcsutil.c: Cleanup message is now suppressed in quiet mode. + +Mon Oct 11 19:41:17 1982 Walter F. Tichy <wft@purdue> + + * rcsbase.h: removed NBPW, NBPC, NCPW. + added typdef int void to aid compiling + + * rcslex.c: removed unused label out:; + * rcslex.c, rcssyn.c: + made sure all calls to getc() return into an integer, not a char. + + * rcsrev.c: changed expandsym() to check for source==nil; + returns zero length string in that case. + +Thu May 6 11:38:00 1982 Walter F. Tichy <wft@purdue> + + * maketime.c, partime.c: Initial revision diff --git a/gnu/usr.bin/rcs/src/Makefile.in b/gnu/usr.bin/rcs/src/Makefile.in new file mode 100644 index 00000000000..be892a6c26b --- /dev/null +++ b/gnu/usr.bin/rcs/src/Makefile.in @@ -0,0 +1,246 @@ +# Make RCS. + +# $Id: Makefile.in,v 1.1 1996/08/12 04:08:00 millert Exp $ + +# Copyright 1982, 1988, 1989 Walter Tichy +# Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + + +# default target +default :: all + +# See the file INSTALL.RCS for more information on the configuration section. +# ----- start of configuration section ----- + +#(Unix + +srcdir = @srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +CC = @CC@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +DEFS = @DEFS@ +DIFF = @DIFF@ +DIFF3 = @DIFF3@ +DIFF3_BIN = @DIFF3_BIN@ +DIFFFLAGS = @DIFFFLAGS@ +DIFF_L = @DIFF_L@ +DIFF_FAILURE = @DIFF_FAILURE@ +DIFF_SUCCESS = @DIFF_SUCCESS@ +DIFF_TROUBLE = @DIFF_TROUBLE@ +ED = @ED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +SENDMAIL = @SENDMAIL@ + + +# The following definitions can be tailored by hand; +# this shouldn't be needed for ordinary installations. + + bindir = $(exec_prefix)/bin + + ALL_CFLAGS = $(CPPFLAGS) -Dhas_conf_h $(DEFS) -I. -I$(srcdir) $(CFLAGS) + + COMPAT2 = 0 +#COMPAT2 = 1 + + LIBOBJS = + + LINK = $(CC) $(LDFLAGS) $(CFLAGS) + + LINT = lint -abchx# traditional and BSD lint +#LINT = lint# System V lint + +#RCSPREFIX should end in `/' if it is not empty. + RCSPREFIX = $(bindir)/ + + REMOVE = rm -f + + o = .o +#o = .s# Minix/PC with ACK cc + + x = + +#) +# On non-Unix hosts you must manually create and edit conf.h from conf.heg. + +# ----- end of configuration section ----- +# You shouldn't have to change anything past this point. + + +# Avoid brain damage in some versions of 'make'. +SHELL = /bin/sh + +PROGRAMS = ci$x co$x ident$x merge$x \ + rcs$x rcsclean$x rcsdiff$x rcsmerge$x rlog$x + +all :: $(PROGRAMS) + +.SUFFIXES : +.SUFFIXES : .c $o +.c$o : + $(CC) -c $(ALL_CFLAGS) $< + +installdirs :: ../mkinstalldirs + $(srcdir)/../mkinstalldirs $(bindir) + +install :: all installdirs + for p in $(PROGRAMS); do \ + $(INSTALL_PROGRAM) $$p $(bindir)/$$p; \ + done + +uninstall :: + for p in $(PROGRAMS); do \ + $(REMOVE) $(bindir)/$$p; \ + done + +# Install RCS and (if applicable) GNU diff before running these tests. +# To test RCS before installing it, see the file INSTALL.RCS. +RCSTEST = \ + ALL_CFLAGS='$(ALL_CFLAGS)' CC='$(CC)' DIFF='$(DIFF)' \ + LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \ + PATH=$(bindir):$$PATH \ + sh $(srcdir)/rcstest +installcheck :: + $(RCSTEST) +installdebug :: + $(RCSTEST) -v + +clean :: + $(REMOVE) a.* RCS/a.* + $(REMOVE) *$o + $(REMOVE) $(PROGRAMS) + $(REMOVE) conf.err core core.* *.core + +mostlyclean :: clean + +distclean :: mostlyclean + $(REMOVE) conf.h Makefile + +maintainer-clean :: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + $(REMOVE) TAGS + +#(Unix +conf.h : conf.sh Makefile + $(REMOVE) a.* + ALL_CFLAGS='$(ALL_CFLAGS)' \ + CC='$(CC)' \ + COMPAT2='$(COMPAT2)' \ + DIFF3='$(DIFF3)' DIFF3_BIN='$(DIFF3_BIN)' \ + DIFF='$(DIFF)' DIFFFLAGS='$(DIFFFLAGS)' DIFF_L='$(DIFF_L)' \ + DIFF_SUCCESS='$(DIFF_SUCCESS)' \ + DIFF_FAILURE='$(DIFF_FAILURE)' \ + DIFF_TROUBLE='$(DIFF_TROUBLE)' \ + ED='$(ED)' \ + LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \ + RCSPREFIX='$(RCSPREFIX)' \ + SENDMAIL='$(SENDMAIL)' \ + $(SHELL) -x $(srcdir)/conf.sh 3>&1 >a.h 2>conf.err + mv a.h $@ + $(REMOVE) a.* core core.* *.core +#) + +ci = ci$o rcslex$o rcssyn$o rcsgen$o rcsedit$o rcskeys$o rcsmap$o \ + rcsrev$o rcsutil$o rcsfnms$o maketime$o partime$o rcstime$o rcskeep$o \ + rcsfcmp$o version$o $(LIBOBJS) +ci$x : $(ci) + $(LINK) $(ci) -o $@ $(LIBS) + +co = co$o rcslex$o rcssyn$o rcsgen$o rcsedit$o rcskeys$o rcsmap$o \ + rcsrev$o rcsutil$o rcsfnms$o maketime$o partime$o rcstime$o rcskeep$o \ + version$o $(LIBOBJS) +co$x : $(co) + $(LINK) $(co) -o $@ $(LIBS) + +ident = ident$o rcsmap$o version$o $(LIBOBJS) +ident$x : $(ident) + $(LINK) $(ident) -o $@ $(LIBS) + +merge = merge$o merger$o rcsfnms$o rcslex$o \ + rcsmap$o rcsrev$o rcssyn$o rcsutil$o \ + rcskeep$o rcskeys$o maketime$o partime$o rcstime$o version$o \ + $(LIBOBJS) +merge$x : $(merge) + $(LINK) $(merge) -o $@ $(LIBS) + +rlog = rlog$o rcslex$o rcsmap$o rcssyn$o rcsrev$o rcsutil$o \ + maketime$o partime$o rcstime$o rcsfnms$o rcskeep$o rcskeys$o \ + version$o $(LIBOBJS) +rlog$x : $(rlog) + $(LINK) $(rlog) -o $@ $(LIBS) + +rcs = rcs$o rcslex$o rcssyn$o rcsrev$o rcsutil$o rcsgen$o \ + rcsedit$o rcskeys$o rcsmap$o rcsfnms$o rcskeep$o \ + maketime$o partime$o rcstime$o version$o $(LIBOBJS) +rcs$x : $(rcs) + $(LINK) $(rcs) -o $@ $(LIBS) + +rcsclean = rcsclean$o rcsedit$o rcsfcmp$o rcsfnms$o rcsgen$o rcskeys$o \ + rcslex$o rcsmap$o rcsrev$o rcssyn$o rcsutil$o rcskeep$o \ + maketime$o partime$o rcstime$o version$o $(LIBOBJS) +rcsclean$x : $(rcsclean) + $(LINK) $(rcsclean) -o $@ $(LIBS) + +rcsdiff = rcsdiff$o rcsutil$o rcsfnms$o rcsmap$o rcsrev$o rcssyn$o \ + rcslex$o maketime$o partime$o rcstime$o rcskeep$o rcskeys$o \ + version$o $(LIBOBJS) +rcsdiff$x : $(rcsdiff) + $(LINK) $(rcsdiff) -o $@ $(LIBS) + +rcsmerge = rcsmerge$o merger$o rcsutil$o rcsfnms$o rcsmap$o rcsrev$o \ + rcssyn$o rcslex$o rcskeep$o rcskeys$o \ + maketime$o partime$o rcstime$o version$o $(LIBOBJS) +rcsmerge$x : $(rcsmerge) + $(LINK) $(rcsmerge) -o $@ $(LIBS) + +SOURCE= ci.c co.c ident.c maketime.c merge.c merger.c partime.c rcs.c \ + rcsclean.c rcsdiff.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ + rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsmerge.c rcsrev.c rcssyn.c \ + rcstime.c rcsutil.c rlog.c version.c +OBJECT= ci$o co$o ident$o maketime$o merge$o merger$o partime$o rcs$o \ + rcsclean$o rcsdiff$o rcsedit$o rcsfcmp$o rcsfnms$o rcsgen$o \ + rcskeep$o rcskeys$o rcslex$o rcsmap$o rcsmerge$o rcsrev$o rcssyn$o \ + rcstime$o rcsutil$o rlog$o version$o + +lint :: conf.h + $(LINT) -DRCS_lint=1 $(ALL_CFLAGS) $(SOURCE) + +TAGS : $(SOURCE) + etags $(SOURCE) + +dvi info :: + +conf_h = conf.h +$(OBJECT) : $(conf_h) rcsbase.h +maketime$o partime$o rcstime$o : partime.h +maketime$o rcstime$o : maketime.h diff --git a/gnu/usr.bin/rcs/src/TAGS b/gnu/usr.bin/rcs/src/TAGS new file mode 100644 index 00000000000..deaf3f1c78f --- /dev/null +++ b/gnu/usr.bin/rcs/src/TAGS @@ -0,0 +1,478 @@ + +ci.c,330 +struct Symrev Symrev247,9189 +mainProg(278,10333 +cleanup(829,24592 +# define exiterr 841,24768 +exiterr(844,24805 +addelta(857,25039 +addbranch(979,29094 +addsyms(1076,32163 +incnum(1089,32367 +removelock(1124,32934 +getcurdate(1158,33964 +fixwork(1170,34176 +xpandfile(1196,34741 +getlogmsg(1244,35717 +addassoclst(1307,37437 + +co.c,211 +mainProg(195,6864 +cleanup(515,14384 +# define exiterr 528,14621 +exiterr(531,14658 +rmworkfile(545,14940 +rmlock(573,15683 +addjoin(620,17079 +preparejoin(660,17826 +getancestor(707,19329 +buildjoin(743,20315 + +ident.c,121 +mainProg(106,3340 +# define exiterr 160,4457 +exiterr(163,4497 +reportError(169,4547 +scanfile(180,4680 +match(225,5708 + +maketime.c,255 +# define P(33,974 +# define const35,998 +# define P(36,1014 +#define TM_YEAR_ORIGIN 59,1619 +isleap(62,1660 +month_days(75,1940 +time2tm(88,2218 +difftm(105,2616 +adjzone(136,3413 +tm2time(193,5143 +maketime(256,6910 +str2time(316,8474 +main(333,8786 + +merge.c,82 +badoption(37,1103 +mainProg(44,1180 +# define exiterr 106,2412 +exiterr(109,2452 + +merger.c,29 +libId(31,939 +merge(55,1433 + +partime.c,479 +# define P(33,993 +# define const35,1017 +# define P(36,1033 +#define isdigit(44,1141 +#define NAME_LENGTH_MAXIMUM 54,1386 +struct name_val name_val56,1417 +#define hr60nonnegative(83,2367 +#define hr60(84,2422 +#define zs(85,2493 +#define zd(86,2523 +lookup 183,6173 +undefine 207,6713 +parse_prefix 248,7962 +parse_fixed 290,8881 +parse_ranged 310,9316 +parse_decimal 323,9724 +parzone 357,10816 +parse_pattern_letter 449,12819 +merge_partime 639,17319 +partime 680,18593 + +rcs.c,813 +struct Lockrev Lockrev206,7573 +struct Symrev Symrev211,7652 +struct Message Message218,7776 +struct Status Status224,7869 +enum changeaccess changeaccess230,7969 +struct chaccess chaccess231,8004 +struct delrevpair delrevpair237,8106 +mainProg(279,9666 +cleanup(646,19634 +exiterr(656,19764 +getassoclst(666,19862 +getchaccess(706,20855 +getaccessor(723,21137 +getmessage(756,21855 +getstates(783,22308 +getdelrev(823,23313 +scanlogtext(886,24985 +rmnewlocklst(951,26634 +doaccess(971,26953 +sendmail(1011,27753 +breaklock(1078,29574 +searchcutpt(1113,30445 +branchpoint(1133,30919 +removerevs(1163,31728 +doassoc(1317,36481 +dolocks(1359,37508 +setlock(1422,39287 +domessages(1454,40029 +rcs_setstate(1479,40508 +buildeltatext(1508,41193 +buildtree(1566,42704 +#define go(1612,43977 +main(1615,44088 + +rcsclean.c,122 +mainProg(41,1125 +cleanup(240,4876 +# define exiterr 251,5033 +exiterr(254,5076 +unlock(263,5172 +get_directory(280,5517 + +rcsdiff.c,108 +mainProg(155,4924 +cleanup(442,12585 +# define exiterr 450,12701 +exiterr(453,12744 +setup_label(461,12834 + +rcsedit.c,1311 +libId(205,7712 +#define lockdirtp_index 225,8656 +#define newRCSdirtp_index 226,8682 +#define newworkdirtp_index 227,8719 +#define DIRTEMPNAMES 228,8768 +enum maker maker230,8815 +#define lockname 233,9002 +#define newRCSname 234,9055 +un_link(239,9145 +# define do_link(272,9748 +do_link(276,9850 +editEndsPrematurely(299,10207 +editLineNumberOverflow(305,10291 +# define movelines(314,10415 +movelines(318,10570 +insertline(355,11481 +deletelines(381,11992 +snapshotline(398,12360 +snapshotedit(411,12551 +finisheditline(423,12810 +finishedit(435,13050 +# define fopen_update_truncate(463,13792 +fopen_update_truncate(467,13964 +openfcopy(478,14142 +swapeditfiles(494,14392 +snapshotedit(511,14816 +finishedit(521,14992 +# define copylines(552,15577 +copylines(556,15704 +xpandstring(599,16635 +copystring(613,17039 +enterstring(659,17980 +edit_string(724,19079 +expandline(849,21837 +escape_string(990,25213 +keyreplace(1012,25818 +resolve_symlink(1236,30955 +rcswriteopen(1290,32095 +keepdirtemp(1476,37410 +makedirtemp(1492,37732 +dirtempunlink(1545,39037 +chnamemod(1565,39415 +setmtime(1662,41785 +findlock(1678,42110 +addlock(1711,42868 +addsymbol(1744,43678 +getcaller(1780,44483 +checkaccesslist(1792,44640 +dorewrite(1816,45191 +donerewrite(1868,46189 +ORCSclose(1922,47218 +ORCSerror(1933,47350 + +rcsfcmp.c,49 +libId(122,3523 +rcsfcmp(142,3844 +main(340,9041 + +rcsfnms.c,644 +libId(181,6669 +#define rcslen 199,7324 +#define TEMPNAMES 206,7477 +struct compair compair210,7593 +tmp(263,9234 +maketemp(278,9526 +tempunlink(320,10471 +bindex(341,10874 +suffix_matches(360,11273 +InitAdmin(384,11628 +bufalloc(408,12270 +bufrealloc(427,12648 +bufautoend(444,12965 +bufremember(453,13088 +bufenlarge(474,13470 +bufscat(488,13746 +bufscpy(499,13954 +basefilename(510,14096 +suffixlen(524,14339 +rcssuffix(543,14594 +rcsreadopen(571,15146 +finopen(583,15490 +fin2open(613,16348 +pairnames(657,17701 +getfullRCSname(792,21295 +dir_useful_len(872,23252 +isSLASH(895,23851 +getcwd(911,24003 +main(1032,26074 +exiterr(1081,27492 + +rcsgen.c,344 +libId(144,4668 +enum stringwork stringwork149,4865 +buildrevision(158,5069 +scandeltatext(216,7022 +cleanlogmsg(264,8424 +int ttystdin(288,8802 +getcstdin(300,8990 +yesorno(319,9245 +putdesc(347,9802 +getsstdin(422,11611 +putadmin(461,12400 +putdelta(527,14035 +puttree(554,14659 +putdtext(577,15078 +putstring(602,15711 +putdftext(628,16227 + +rcskeep.c,167 +libId(102,3119 +badly_terminated(269,7009 +getval(276,7106 +get0val(292,7515 +keepdate(346,8473 +keepid(380,9300 +keeprev(395,9553 +checknum(404,9708 +main(441,10207 + +rcskeys.c,33 +libId(67,1960 +trymatch(80,2245 + +rcslex.c,1643 +libId(172,5981 +# define hshsize 209,7469 +lookup(228,7808 +Lexinit(270,8710 +nextlex(300,9181 +eoflex(396,11109 +int getlex(432,11658 +getkeyopt(447,12052 +getkey(464,12413 +getkeystring(475,12636 +getid(488,12933 +struct hshentry * getnum(505,13314 +getphrases(522,13736 +readstring(670,16670 +printstring(702,17262 +savestring(736,17874 +checkidentifier(782,18924 +checkid(836,20183 +checksym(844,20292 +checksid(852,20404 +checkssym(860,20514 +# define Iclose(868,20589 + Iclose(873,20691 + Iclose(883,20848 + map_fd_deallocate(894,21033 + mmap_deallocate(908,21321 + read_deallocate(917,21530 + nothing_to_deallocate(925,21657 +fd2_RILE(936,21830 +Igetmore(1074,24867 +advise_access(1094,25254 +I_open(1106,25531 +Oerror(1134,26024 +void Ieof(1142,26112 +void Ierror(1143,26165 +void testIerror(1144,26209 +void testOerror(1145,26266 +void Ifclose(1147,26324 +void Ofclose(1148,26386 +void Izclose(1149,26448 +void Ozclose(1150,26499 +testIeof(1154,26575 +void Irewind(1161,26641 +void Orewind(1164,26719 +void aflush(1166,26790 +void eflush(1167,26848 +void oflush(1168,26912 +fatcleanup(1175,27016 +startsay(1183,27160 +fatsay(1194,27329 +errsay(1201,27391 +warnsay(1209,27458 +void eerror(1215,27517 +enerror(1218,27576 +void efaterror(1228,27678 +enfaterror(1231,27743 +error(1243,27881 +rcserror(1260,28197 +workerror(1277,28521 +fatserror(1294,28852 +faterror(1312,29226 +rcsfaterror(1329,29566 +warn(1346,29914 +rcswarn(1365,30245 +workwarn(1384,30589 +redefined(1402,30921 +diagnose(1410,31012 +afputc(1432,31537 +aputs(1442,31671 +fvfprintf(1460,31895 +aprintf(1490,32495 +main(1517,32900 +void exiterr(1566,34093 + +rcsmap.c,14 +libId(32,973 + +rcsmerge.c,64 +mainProg(121,3782 +# define exiterr 280,7892 +exiterr(283,7933 + +rcsrev.c,456 +libId(109,3579 +getbranchno(140,4498 +int cmpnum(164,5011 +int cmpnumfld(208,6014 +cmpdate(237,6692 +normalizeyear(258,7167 +cantfindbranch(275,7441 +absent(292,7870 +compartial(306,8072 +char * partialno(346,8944 +store1(373,9401 +struct hshentry * genrevs(389,9714 +genbranch(518,12960 +lookupsym(650,16659 +int expandsym(664,17048 +fexpandsym(679,17511 +namedrev(795,19775 +branchtip(828,20330 +tiprev(839,20530 +main(856,20781 +void exiterr(910,22415 + +rcssyn.c,372 +libId(157,5279 +getdnum(215,6642 +getadmin(226,6845 +str2expmode(349,9788 +strn2expmode(357,9931 +ignorephrases(371,10129 +getdelta(400,10683 +gettree(443,11729 +getdesc(461,12081 +getkeyval(481,12413 +unexpected_EOF(508,13000 +initdiffcmd(514,13075 +badDiffOutput(523,13221 +diffLineNumberTooLarge(530,13323 +getdiffcmd(537,13432 +main(652,15844 +void exiterr(680,16322 + +rcstime.c,116 +libId(33,1006 +str2time_checked(64,1926 +str2date(80,2298 +date2time(96,2582 +zone_set(106,2781 +date2str(125,3123 + +rcsutil.c,1194 +libId(190,6805 +memcpy(211,7185 +struct alloclist alloclist234,7680 +okalloc(243,7854 +testalloc(252,7948 +testrealloc(260,8091 +fremember(269,8267 +ftestalloc(280,8515 +ffree(288,8667 +ffree1(301,8874 +str_save(315,9132 +fstr_save(323,9267 +cgetenv(331,9438 +getusername(341,9629 + readAccessFilenameBuffer(411,11066 +# define accessName 421,11251 +# define psignal 427,11319 +my_psignal(430,11406 +catchsig(494,12993 +catchsigaction(501,13102 +ignoreints(588,14860 +restoreints(594,14898 + check_sig(611,15199 + setup_catchsig(619,15289 + setup_catchsig(661,16235 + setup_catchsig(684,16621 +catchints(727,17152 + catchmmapints(753,17984 +fastcopy(767,18225 +# define SSIZE_MAX 804,19116 +awrite(808,19169 +dupSafer(828,19614 +fdSafer(848,20069 +fopenSafer(863,20324 +# define dup(893,20833 +movefd(901,20953 +fdreopen(916,21216 +redirect(934,21529 +bufargcat(952,21896 +write_stderr(991,22682 +runv(1007,23018 +#define CARGSMAX 1165,26408 +run(1174,26644 +setRCSversion(1198,27071 +getRCSINIT(1229,27607 +#define cacheid(1309,28948 + uid_t ruid(1312,29045 + uid_t euid(1315,29104 +set_uid_to(1332,29569 +nosetid(1363,30121 +seteid(1369,30167 +setrid(1377,30262 +now(1386,30361 + +rlog.c,682 +struct rcslockers rcslockers161,5469 +struct stateattri stateattri166,5651 +struct authors authors171,5831 +struct Revpairs{Revpairs176,6022 +struct Datepairs{Datepairs183,6259 +mainProg(222,7773 +cleanup(472,14070 +# define exiterr 479,14157 +exiterr(482,14196 +putrunk(490,14248 +putree(503,14461 +putforest(520,14754 +putabranch(536,15047 +putadelta(553,15251 +readdeltalog(619,16992 +getscript(654,17851 +exttree(711,18873 +getlocker(735,19309 +getauthor(768,20182 +getstate(805,21141 +trunclocks(839,22049 +recentdate(864,22633 +extdate(895,23470 +extractdelta(948,24760 +getdatepair(992,26100 +getnumericrev(1077,28554 +checkrevpair(1174,30757 +getrevpairs(1197,31283 + +version.c,0 diff --git a/gnu/usr.bin/rcs/src/ci.c b/gnu/usr.bin/rcs/src/ci.c new file mode 100644 index 00000000000..370c9ebdece --- /dev/null +++ b/gnu/usr.bin/rcs/src/ci.c @@ -0,0 +1,1322 @@ +/* Check in revisions of RCS files from working files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: ci.c,v $ + * Revision 1.1 1996/08/12 04:08:02 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.30 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.29 1995/06/01 16:23:43 eggert + * (main): Add -kb. + * Use `cmpdate', not `cmpnum', to compare dates. + * This is for MKS RCS's incompatible 20th-century date format. + * Don't worry about errno after ftruncate fails. + * Fix input file rewinding bug when large_memory && !maps_memory + * and checking in a branch tip. + * + * (fixwork): Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * Revision 5.28 1994/03/20 04:52:58 eggert + * Do not generate a corrupted RCS file if the user modifies the working file + * while `ci' is running. + * Do not remove the lock when `ci -l' reverts. + * Move buffer-flushes out of critical sections, since they aren't critical. + * Use ORCSerror to clean up after a fatal error. + * Specify subprocess input via file descriptor, not file name. + * + * Revision 5.27 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.26 1993/11/03 17:42:27 eggert + * Add -z. Don't subtract from RCS file timestamp even if -T. + * Scan for and use Name keyword if -k. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.25 1992/07/28 16:12:44 eggert + * Add -i, -j, -V. Check that working and RCS files are distinct. + * + * Revision 5.24 1992/02/17 23:02:06 eggert + * `-rREV' now just specifies a revision REV; only bare `-r' reverts to default. + * Add -T. + * + * Revision 5.23 1992/01/27 16:42:51 eggert + * Always unlock branchpoint if caller has a lock. + * Add support for bad_chmod_close, bad_creat0. lint -> RCS_lint + * + * Revision 5.22 1992/01/06 02:42:34 eggert + * Invoke utime() before chmod() to keep some buggy systems happy. + * + * 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*,int)); +static int addelta P((void)); +static int addsyms P((char const*)); +static int fixwork P((mode_t,time_t)); +static int removelock P((struct hshentry*)); +static int xpandfile P((RILE*,struct hshentry const*,char const**,int)); +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 const*)); + +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, **nextassoc; + +mainProg(ciId, "ci", "$Id: ci.c,v 1.1 1996/08/12 04:08:02 millert Exp $") +{ + static char const cmdusage[] = + "\nci usage: ci -{fIklMqru}[rev] -d[date] -mmsg -{nN}name -sstate -ttext -T -Vn -wwho -xsuff -zzone file ..."; + static char const default_state[] = DEFAULTSTATE; + + char altdate[datesize]; + char olddate[datesize]; + char newdatebuf[datesize + zonelenmax]; + char targetdatebuf[datesize + zonelenmax]; + char *a, **newargv, *textfile; + char const *author, *krev, *rev, *state; + char const *diffname, *expname; + char const *newworkname; + int initflag, mustread; + int lockflag, lockthis, mtimeflag, removedlock, Ttimeflag; + int r; + int changedRCS, changework, dolog, newhead; + int usestatdate; /* Use mod time of file for -d. */ + mode_t newworkmode; /* mode for working file */ + time_t mtime, wtime; + struct hshentry *workdelta; + + setrid(); + + author = rev = state = textfile = 0; + initflag = lockflag = mustread = false; + mtimeflag = false; + Ttimeflag = false; + altdate[0]= '\0'; /* empty alternate date for -d */ + usestatdate=false; + suffixes = X_DEFAULT; + nextassoc = &assoclst; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + while (a = *++argv, 0<--argc && *a++=='-') { + switch (*a++) { + + case 'r': + if (*a) + goto revno; + keepworkingfile = lockflag = false; + break; + + case 'l': + keepworkingfile = lockflag = true; + revno: + if (*a) { + if (rev) warn("redefinition of revision number"); + rev = a; + } + break; + + case 'u': + keepworkingfile=true; lockflag=false; + goto revno; + + case 'i': + initflag = true; + goto revno; + + case 'j': + mustread = true; + 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) + error("missing message for -m option"); + break; + + case 'n': + if (!*a) { + error("missing symbolic name after -n"); + break; + } + checkssym(a); + addassoclst(false, a); + break; + + case 'N': + if (!*a) { + error("missing symbolic name after -N"); + break; + } + checkssym(a); + addassoclst(true, a); + break; + + case 's': + if (*a) { + if (state) redefined('s'); + checksid(a); + state = a; + } else + error("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 + error("missing author for -w option"); + break; + + case 'x': + suffixes = a; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'z': + zone_set(a); + break; + + case 'T': + if (!*a) { + Ttimeflag = true; + break; + } + /* fall into */ + default: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + /* Handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + targetdelta = 0; + ffree(); + + switch (pairnames(argc, argv, rcswriteopen, mustread, false)) { + + case -1: /* New RCS file */ +# if has_setuid && has_getuid + if (euid() != ruid()) { + workerror("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 */ + if (initflag) { + rcserror("already exists"); + continue; + } + rcsinitflag = !Head; + } + + /* + * RCSname contains the name of the RCS file, and + * workname contains the name of the working file. + * If the RCS file exists, finptr contains the file descriptor for the + * RCS file, and RCSstat is set. The admin node is initialized. + */ + + diagnose("%s <-- %s\n", RCSname, workname); + + if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { + eerror(workname); + continue; + } + + if (finptr) { + if (same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + if (!checkaccesslist()) + continue; + } + + krev = rev; + if (keepflag) { + /* get keyword values from working file */ + if (!getoldkeys(workptr)) continue; + if (!rev && !*(krev = prevrev.string)) { + workerror("can't find a revision number"); + continue; + } + if (!*prevdate.string && *altdate=='\0' && usestatdate==false) + workwarn("can't find a date"); + if (!*prevauthor.string && !author) + workwarn("can't find an author"); + if (!*prevstate.string && !state) + workwarn("can't find a state"); + } /* 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 = 0; + newdelta.lockedby = 0; /* This might be changed by addlock(). */ + newdelta.selector = true; + newdelta.name = 0; + clear_buf(&newdelta.ig); + clear_buf(&newdelta.igtext); + /* set author */ + if (author) + 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) + 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 && + cmpdate(newdelta.date,targetdelta->date) < 0) { + rcserror("Date %s precedes %s in revision %s.", + date2str(newdelta.date, newdatebuf), + date2str(targetdelta->date, targetdatebuf), + targetdelta->num + ); + continue; + } + + + if (lockflag && addlock(&newdelta, true) < 0) continue; + + if (keepflag && *prevname.string) + if (addsymbol(newdelta.num, prevname.string, false) < 0) + continue; + if (!addsyms(newdelta.num)) + continue; + + + putadmin(); + puttree(Head,frewrite); + putdesc(false,textfile); + + changework = Expand < MIN_UNCHANGED_EXPAND; + dolog = true; + lockthis = lockflag; + workdelta = &newdelta; + + /* build rest of file */ + if (rcsinitflag) { + diagnose("initial revision: %s\n", newdelta.num); + /* get logmessage */ + newdelta.log=getlogmsg(); + putdftext(&newdelta, workptr, frewrite, false); + RCSstat.st_mode = workstat.st_mode; + RCSstat.st_nlink = 0; + changedRCS = true; + } else { + diffname = maketemp(0); + newhead = Head == &newdelta; + if (!newhead) + foutptr = frewrite; + expname = buildrevision( + gendeltas, targetdelta, (FILE*)0, false + ); + if ( + !forceciflag && + strcmp(newdelta.state, targetdelta->state) == 0 && + (changework = rcsfcmp( + workptr, &workstat, expname, 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; + } + dolog = false; + if (! (changedRCS = lockflag<removedlock || assoclst)) + 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; + Orewind(frewrite); + + /* + * Work around a common ftruncate() bug: + * NFS won't let you truncate a file that you + * currently lack permissions for, even if you + * had permissions when you opened it. + * Also, Posix 1003.1b-1993 sec 5.6.7.2 p 128 l 1022 + * says ftruncate might fail because it's not supported. + */ +# if !has_ftruncate +# undef ftruncate +# define ftruncate(fd,length) (-1) +# endif + bad_truncate = ftruncate(fileno(frewrite), (off_t)0); + + 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 (lockthis<removedlock && removelock(workdelta)<0) + continue; + if (!addsyms(workdelta->num)) + continue; + if (dorewrite(true, true) != 0) + 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 { + int wfd = Ifileno(workptr); + struct stat checkworkstat; + char const *diffv[6 + !!OPEN_O_BINARY], **diffp; +# if large_memory && !maps_memory + FILE *wfile = workptr->stream; + long wfile_off; +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + off_t wfd_off; +# endif + + diagnose("new revision: %s; previous revision: %s\n", + newdelta.num, targetdelta->num + ); + newdelta.log = getlogmsg(); +# if !large_memory + Irewind(workptr); +# if has_fflush_input + if (fflush(workptr) != 0) + Ierror(); +# endif +# else +# if !maps_memory + if ( + (wfile_off = ftell(wfile)) == -1 + || fseek(wfile, 0L, SEEK_SET) != 0 +# if has_fflush_input + || fflush(wfile) != 0 +# endif + ) + Ierror(); +# endif +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + wfd_off = lseek(wfd, (off_t)0, SEEK_CUR); + if (wfd_off == -1 + || (wfd_off != 0 + && lseek(wfd, (off_t)0, SEEK_SET) != 0)) + Ierror(); +# endif + diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp = "--binary"; +# endif + *++diffp = newhead ? "-" : expname; + *++diffp = newhead ? expname : "-"; + *++diffp = 0; + switch (runv(wfd, diffname, diffv)) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: rcsfaterror("diff failed"); + } +# if !has_fflush_input && !(large_memory && maps_memory) + if (lseek(wfd, wfd_off, SEEK_CUR) == -1) + Ierror(); +# endif +# if large_memory && !maps_memory + if (fseek(wfile, wfile_off, SEEK_SET) != 0) + Ierror(); +# endif + if (newhead) { + Irewind(workptr); + putdftext(&newdelta, workptr, frewrite, false); + if (!putdtext(targetdelta,diffname,frewrite,true)) continue; + } else + if (!putdtext(&newdelta,diffname,frewrite,true)) continue; + + /* + * Check whether the working file changed during checkin, + * to avoid producing an inconsistent RCS file. + */ + if ( + fstat(wfd, &checkworkstat) != 0 + || workstat.st_mtime != checkworkstat.st_mtime + || workstat.st_size != checkworkstat.st_size + ) { + workerror("file changed during checkin"); + continue; + } + + changedRCS = true; + } + } + + /* Deduce time_t of new revision if it is needed later. */ + wtime = (time_t)-1; + if (mtimeflag | Ttimeflag) + wtime = date2time(workdelta->date); + + if (donerewrite(changedRCS, + !Ttimeflag ? (time_t)-1 + : finptr && wtime < RCSstat.st_mtime ? RCSstat.st_mtime + : wtime + ) != 0) + continue; + + if (!keepworkingfile) { + Izclose(&workptr); + r = un_link(workname); /* Get rid of old file */ + } else { + newworkmode = WORKMODE(RCSstat.st_mode, + ! (Expand==VAL_EXPAND || lockthis < StrictLocks) + ); + mtime = mtimeflag ? wtime : (time_t)-1; + + /* 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; + workdelta->name = + namedrev( + assoclst ? assoclst->ssymbol + : keepflag && *prevname.string ? prevname.string + : rev, + workdelta + ); + switch (xpandfile( + workptr, workdelta, &newworkname, dolog + )) { + 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: + Izclose(&workptr); + aflush(exfile); + ignoreints(); + r = chnamemod(&exfile, newworkname, + workname, 1, newworkmode, mtime + ); + keepdirtemp(newworkname); + restoreints(); + } + } + } + if (r != 0) { + eerror(workname); + continue; + } + diagnose("done\n"); + + } + + tempunlink(); + exitmain(exitstatus); +} /* end of main (ci) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Izclose(&workptr); + Ozclose(&exfile); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr ciExit +#endif + void +exiterr() +{ + ORCSerror(); + 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 int i; + int removedlock; + int 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) { + bufscpy(&newdelnum, Dbranch); + newdnumlength = countnumflds(Dbranch); + } + if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); + else if (newdnumlength==1) bufscat(&newdelnum, ".1"); + else if (newdnumlength>2) { + rcserror("Branch point doesn't exist for revision %s.", + newdelnum.string + ); + return -1; + } /* newdnumlength == 2 is OK; */ + Head = &newdelta; + newdelta.next = 0; + 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 = 0; + } else { + /* middle revision; start a new branch */ + bufscpy(&newdelnum, ""); + return addbranch(targetdelta, &newdelnum, 1); + } + 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)) { + rcserror("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) { + rcserror("revision %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++ != '.') + continue; + *--tp = 0; /* Kill final dot to get old delta temporarily. */ + if (!(targetdelta=genrevs(newdelnum.string,(char*)0,(char*)0,(char*)0,&gendeltas))) + return -1; + if (cmpnum(targetdelta->num, newdelnum.string) != 0) { + rcserror("can't find branch point %s", newdelnum.string); + return -1; + } + *tp = '.'; /* Restore final dot. */ + return addbranch(targetdelta, &newdelnum, 0); + } +} + + + + static int +addbranch(branchpoint, num, removedlock) + struct hshentry *branchpoint; + struct buf *num; + int removedlock; +/* 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. + * If REMOVEDLOCK is 1, a lock was already removed. + */ +{ + struct branchhead *bhead, **btrail; + struct buf branchnum; + int result; + int field, numlength; + static struct branchhead newbranch; /* new branch to be inserted */ + + numlength = countnumflds(num->string); + + if (!branchpoint->branches) { + /* 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 = 0; + + } 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 = 0; + } 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*)0, (char*)0, (char*)0, + &gendeltas + ); + bufautoend(&branchnum); + if (!targetdelta) + return -1; + if (cmpnum(num->string,targetdelta->num) <= 0) { + rcserror("revision %s too low; must be higher than %s", + num->string, targetdelta->num + ); + return -1; + } + if (!removedlock + && 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 = 0; + if (branchpoint->lockedby) + if (strcmp(branchpoint->lockedby, getcaller()) == 0) + return removelock(branchpoint); /* This returns 1. */ + return removedlock; +} + + 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) < 0) + 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 rcslock *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 { + rcserror("revision %s locked by %s", num, next->login); + return -1; + } + if (!StrictLocks && myself(RCSstat.st_uid)) + return 0; + rcserror("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 */ + + if (!buffer[0]) + time2date(now(), buffer); + return buffer; +} + + static int +#if has_prototypes +fixwork(mode_t newworkmode, time_t mtime) + /* The `#if has_prototypes' is needed because mode_t might promote to int. */ +#else + fixwork(newworkmode, mtime) + mode_t newworkmode; + time_t mtime; +#endif +{ + return + 1 < workstat.st_nlink + || (newworkmode&S_IWUSR && !myself(workstat.st_uid)) + || setmtime(workname, mtime) != 0 + ? -1 + : workstat.st_mode == newworkmode ? 0 +#if has_fchmod + : fchmod(Ifileno(workptr), newworkmode) == 0 ? 0 +#endif +#if bad_chmod_close + : -1 +#else + : chmod(workname, newworkmode) +#endif + ; +} + + static int +xpandfile(unexfile, delta, exname, dolog) + RILE *unexfile; + struct hshentry const *delta; + char const **exname; + int dolog; +/* + * Read unexfile and copy it to a + * file, 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 *EXNAME. + */ +{ + char const *targetname; + int e, r; + + targetname = makedirtemp(1); + if (!(exfile = fopenSafer(targetname, FOPEN_W_WORK))) { + eerror(targetname); + workerror("can't build working file"); + return -1; + } + r = 0; + if (MIN_UNEXPAND <= Expand) + fastcopy(unexfile,exfile); + else { + for (;;) { + e = expandline( + unexfile, exfile, delta, false, (FILE*)0, dolog + ); + if (e < 0) + break; + r |= e; + if (e <= 1) + break; + } + } + *exname = targetname; + 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 + zonelenmax); + 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 const *sp; +{ + struct Symrev *pt; + + pt = talloc(struct Symrev); + pt->ssymbol = sp; + pt->override = flag; + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; +} diff --git a/gnu/usr.bin/rcs/src/co.c b/gnu/usr.bin/rcs/src/co.c new file mode 100644 index 00000000000..f4a396d1e03 --- /dev/null +++ b/gnu/usr.bin/rcs/src/co.c @@ -0,0 +1,830 @@ +/* Check out working files from revisions of RCS files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: co.c,v $ + * Revision 1.1 1996/08/12 04:08:03 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.18 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.17 1995/06/01 16:23:43 eggert + * (main, preparejoin): Pass argument instead of using `join' static variable. + * (main): Add -kb. + * + * Revision 5.16 1994/03/17 14:05:48 eggert + * Move buffer-flushes out of critical sections, since they aren't critical. + * Use ORCSerror to clean up after a fatal error. Remove lint. + * Specify subprocess input via file descriptor, not file name. + * + * Revision 5.15 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.14 1993/11/03 17:42:27 eggert + * Add -z. Generate a value for the Name keyword. + * Don't arbitrarily limit the number of joins. + * Improve quality of diagnostics. + * + * Revision 5.13 1992/07/28 16:12:44 eggert + * Add -V. Check that working and RCS files are distinct. + * + * Revision 5.12 1992/02/17 23:02:08 eggert + * Add -T. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.10 1992/01/06 02:42:34 eggert + * Update usage string. + * + * Revision 5.9 1991/10/07 17:32:46 eggert + * -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 *addjoin P((char*)); +static char const *getancestor P((char const*,char const*)); +static int buildjoin P((char const*)); +static int preparejoin P((char*)); +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, *suffixarg, *versionarg, *zonearg; +static char const **joinlist; /* revisions to be joined */ +static int joinlength; +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 1.1 1996/08/12 04:08:03 millert Exp $") +{ + static char const cmdusage[] = + "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ..."; + + char *a, *joinflag, **newargv; + char const *author, *date, *rev, *state; + char const *joinname, *newdate, *neworkname; + int changelock; /* 1 if a lock has been changed, -1 if error */ + int expmode, r, tostdout, workstatstat; + int Ttimeflag; + struct buf numericrev; /* expanded revision number */ + char finaldate[datesize]; +# if OPEN_O_BINARY + int stdout_mode = 0; +# endif + + setrid(); + author = date = rev = state = 0; + joinflag = 0; + bufautobegin(&numericrev); + expmode = -1; + suffixes = X_DEFAULT; + tostdout = false; + Ttimeflag = 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("-u overridden by -l."); + } + lockflag = 1; + goto revno; + + case 'u': + if (0 < lockflag) { + warn("-l overridden by -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 (joinflag) redefined('j'); + joinflag = a; + } + break; + + case 'M': + mtimeflag = true; + goto revno; + + case 's': + if (*a) { + if (state) redefined('s'); + state = a; + } + break; + + case 'T': + if (*a) + goto unknown; + Ttimeflag = true; + 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 'z': + zonearg = *argv; + zone_set(a); + break; + + case 'k': /* set keyword expand mode */ + expandarg = *argv; + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(a))) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + /* Now handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); + + if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) + continue; + + /* + * RCSname contains the name of the RCS file, and finptr + * points at it. workname contains the name of the working file. + * Also, RCSstat has been set. + */ + diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); + + workstatstat = -1; + if (tostdout) { +# if OPEN_O_BINARY + int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0; + if (stdout_mode != newmode) { + stdout_mode = newmode; + oflush(); + VOID setmode(STDOUT_FILENO, newmode); + } +# endif + neworkname = 0; + neworkptr = workstdout = stdout; + } else { + workstatstat = stat(workname, &workstat); + if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + neworkname = makedirtemp(1); + if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) { + if (errno == EACCES) + workerror("permission denied on parent directory"); + else + eerror(neworkname); + continue; + } + } + + gettree(); /* reads in the delta tree */ + + if (!Head) { + /* no revisions; create empty file */ + diagnose("no revisions present; generating empty revision 0.0\n"); + if (lockflag) + warn( + "no revisions, so nothing can be %slocked", + lockflag < 0 ? "un" : "" + ); + Ozclose(&fcopy); + if (workstatstat == 0) + if (!rmworkfile()) continue; + changelock = 0; + newdate = 0; + } else { + int locks = lockflag ? findlock(false, &targetdelta) : 0; + if (rev) { + /* expand symbolic revision number */ + if (!expandsym(rev, &numericrev)) + continue; + } else { + switch (locks) { + 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, true); + + if ( + changelock < 0 + || (changelock && !checkaccesslist()) + || dorewrite(lockflag, changelock) != 0 + ) + continue; + + if (0 <= expmode) + Expand = expmode; + if (0 < lockflag && Expand == VAL_EXPAND) { + rcserror("cannot combine -kv and -l"); + continue; + } + + if (joinflag && !preparejoin(joinflag)) + 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; + targetdelta->name = namedrev(rev, targetdelta); + joinname = buildrevision( + gendeltas, targetdelta, + joinflag&&tostdout ? (FILE*)0 : neworkptr, + Expand < MIN_UNEXPAND + ); +# 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, + Ttimeflag ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + continue; + + if (changelock) { + locks += lockflag; + if (1 < locks) + rcswarn("You now have %d locks.", locks); + } + + newdate = targetdelta->date; + if (joinflag) { + newdate = 0; + if (!joinname) { + aflush(neworkptr); + joinname = neworkname; + } + if (Expand == BINARY_EXPAND) + workerror("merging binary files"); + if (!buildjoin(joinname)) + continue; + } + } + if (!tostdout) { + mode_t m = WORKMODE(RCSstat.st_mode, + ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks)) + ); + time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1; + aflush(neworkptr); + ignoreints(); + r = chnamemod(&neworkptr, neworkname, workname, 1, m, t); + keepdirtemp(neworkname); + restoreints(); + if (r != 0) { + eerror(workname); + error("see %s", neworkname); + continue; + } + diagnose("done\n"); + } + } + + tempunlink(); + Ofclose(workstdout); + exitmain(exitstatus); + +} /* end of main (co) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + ORCSclose(); +# if !large_memory + if (fcopy!=workstdout) Ozclose(&fcopy); +# endif + if (neworkptr!=workstdout) Ozclose(&neworkptr); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr coExit +#endif + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + +/***************************************************************** + * The following routines are auxiliary routines + *****************************************************************/ + + static int +rmworkfile() +/* + * Prepare to remove workname, 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): ", + workname, + myself(workstat.st_uid) ? "" : ", and you do not own it" + )) { + error(!quietflag && ttystdin() + ? "checkout aborted" + : "writable %s exists; checkout aborted", workname); + 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 rcslock * next, * trail; + char const *num; + struct rcslock dummy; + int whomatch, nummatch; + + num=delta->num; + dummy.nextlock=next=Locks; + trail = &dummy; + while (next) { + 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)) { + rcserror("revision %s locked by %s; use co -r or rcs -u", + num, next->login + ); + return -1; + } + trail=next; + next=next->nextlock; + } + if (next) { + /*found one; delete it */ + trail->nextlock=next->nextlock; + Locks=dummy.nextlock; + next->delta->lockedby = 0; + return 1; /*success*/ + } else return 0; /*no lock on delta*/ +} + + + + +/***************************************************************** + * The rest of the routines are for handling joins + *****************************************************************/ + + + static char * +addjoin(joinrev) + char *joinrev; +/* Add joinrev's number to joinlist, yielding address of char past joinrev, + * or 0 if no such revision exists. + */ +{ + register char *j; + register struct hshentry *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*)0,(char*)0,(char*)0,&joindeltas); + bufautoend(&numrev); + *j = terminator; + if (d) { + joinlist[++lastjoin] = d->num; + return j; + } + return 0; +} + + static int +preparejoin(j) + register char *j; +/* Parse join list J and place pointers to the + * revision numbers into joinlist. + */ +{ + lastjoin= -1; + for (;;) { + while ((*j==' ')||(*j=='\t')||(*j==',')) j++; + if (*j=='\0') break; + if (lastjoin>=joinlength-2) { + joinlist = + (joinlength *= 2) == 0 + ? tnalloc(char const *, joinlength = 16) + : trealloc(char const *, joinlist, joinlength); + } + 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 { + rcsfaterror("join pair incomplete"); + } + } 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 { + rcsfaterror("join pair incomplete"); + } + } + } + if (lastjoin < 1) + rcsfaterror("empty join"); + return true; +} + + + + static char const * +getancestor(r1, r2) + char const *r1, *r2; +/* Yield the common ancestor of r1 and r2 if successful, 0 otherwise. + * Work reliably only if r1 and r2 are not branch numbers. + */ +{ + static struct buf t1, t2; + + int 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 ? 2 : l1); + VOID partialno(&t2, r2, l2>2 ? 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); + } + rcserror("common ancestor of %s and %s undefined", r1, r2); + return 0; +} + + + + 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[11]; + char const **p; + + bufautobegin(&commarg); + bufautobegin(&subs); + rev2 = maketemp(0); + rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ + + cov[1] = CO; + /* cov[2] setup below */ + p = &cov[3]; + if (expandarg) *p++ = expandarg; + if (suffixarg) *p++ = suffixarg; + if (versionarg) *p++ = versionarg; + if (zonearg) *p++ = zonearg; + *p++ = quietarg; + *p++ = RCSname; + *p = 0; + + mergev[1] = MERGE; + mergev[2] = mergev[4] = "-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[2] = commarg.string; + if (runv(-1, rev2, cov)) + goto badmerge; + diagnose("revision %s\n",joinlist[i+1]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, joinlist[i+1]); + cov[2] = commarg.string; + if (runv(-1, rev3, cov)) + goto badmerge; + diagnose("merging...\n"); + mergev[3] = subs.string; + mergev[5] = joinlist[i+1]; + p = &mergev[6]; + if (quietflag) *p++ = quietarg; + if (lastjoin<=i+2 && workstdout) *p++ = "-p"; + *p++ = initialfile; + *p++ = rev2; + *p++ = rev3; + *p = 0; + switch (runv(-1, (char*)0, 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/gnu/usr.bin/rcs/src/conf.heg b/gnu/usr.bin/rcs/src/conf.heg new file mode 100644 index 00000000000..fbec3f68362 --- /dev/null +++ b/gnu/usr.bin/rcs/src/conf.heg @@ -0,0 +1,399 @@ +/* example RCS compile-time configuration */ + + /* $Id: conf.heg,v 1.1 1996/08/12 04:08:04 millert Exp $ */ + +/* + * This example RCS compile-time configuration describes a host that conforms + * to Standard C (1990) and to Posix 1003.1b-1993 with Memory Mapped Files, + * and has GNU diffutils 2.7 or later. If you can't get conf.sh to work, + * copy this file to conf.h and edit conf.h by hand; see ../INSTALL.RCS. + */ + +#define exitmain(n) return n /* how to exit from main() */ +/* #define _POSIX_C_SOURCE 2147483647L */ /* if strict C + Posix 1003.1b-1993 or later */ +/* #define _POSIX_SOURCE */ /* if strict C + Posix 1003.1-1990 */ + +#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 <mach/mach.h> */ +/* #include <net/errno.h> */ +#include <pwd.h> +/* #include <siginfo.h> */ +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/wait.h> +/* #include <ucontext.h> */ +#include <unistd.h> +#include <utime.h> +/* #include <vfork.h> */ + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +#define has_sys_param_h 0 /* Does #include <sys/param.h> work? */ +/* extern int errno; */ /* Uncomment if <errno.h> doesn't declare errno. */ +#define has_readlink 0 /* Does readlink() work? */ +#define readlink_isreg_errno EINVAL /* errno after readlink on regular file */ + +#if has_readlink && !defined(MAXSYMLINKS) +# if has_sys_param_h +# include <sys/param.h> +# endif +# ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 /* BSD; not standard yet */ +# endif +#endif + +/* Comment out the typedefs below if the types are already declared. */ +/* Fix any uncommented typedefs that are wrong. */ +/* typedef int mode_t; */ +/* typedef long off_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; */ + +/* Comment out the keyword definitions below if the keywords work. */ +/* #define const */ +/* #define volatile */ + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +#define has_prototypes 1 /* Do function prototypes work? */ +#define has_stdarg 1 /* Does <stdarg.h> work? */ +/* #define has_varargs ? */ /* Does <varargs.h> work? */ +#define va_start_args 2 /* How many args does va_start() take? */ + +#if O_BINARY + /* Text and binary i/o behave differently. */ + /* This is incompatible with Posix and Unix. */ +# define FOPEN_RB "rb" +# define FOPEN_R_WORK (Expand==BINARY_EXPAND ? "r" : "rb") +# define FOPEN_WB "wb" +# define FOPEN_W_WORK (Expand==BINARY_EXPAND ? "w" : "wb") +# define FOPEN_WPLUS_WORK (Expand==BINARY_EXPAND ? "w+" : "w+b") +# define OPEN_O_BINARY O_BINARY +#else + /* + * Text and binary i/o behave the same. + * Omit "b", since some nonstandard hosts reject it. + */ +# define FOPEN_RB "r" +# define FOPEN_R_WORK "r" +# define FOPEN_WB "w" +# define FOPEN_W_WORK "w" +# define FOPEN_WPLUS_WORK "w+" +# define OPEN_O_BINARY 0 +#endif + +/* This may need changing on non-Unix systems (notably DOS). */ +#define OPEN_CREAT_READONLY (S_IRUSR|S_IRGRP|S_IROTH) /* lock file mode */ +#define OPEN_O_LOCK 0 /* extra open flags for creating lock file */ +#define OPEN_O_WRONLY O_WRONLY /* main open flag for creating a lock file */ + +/* Define or comment out the following symbols as needed. */ +#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 bad_chmod_close 0 /* Can chmod() close file descriptors? */ +#define bad_creat0 0 /* Do writes fail after creat(f,0)? */ +#define bad_fopen_wplus 0 /* Does fopen(f,"w+") fail to truncate f? */ +#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */ +#define has_attribute_noreturn 0 /* Does __attribute__((noreturn)) work? */ +#if has_attribute_noreturn +# define exiting __attribute__((noreturn)) +#else +# define exiting +#endif +#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */ +#define void_closedir 0 /* Does closedir() yield void? */ +#define has_fchmod 1 /* Does fchmod() work? */ +#define has_fflush_input 1 /* Does fflush() work on input files? */ +#define has_fputs 1 /* Does fputs() work? */ +#define has_ftruncate 1 /* Does ftruncate() work? */ +#define has_getuid 1 /* Does getuid() work? */ +#define has_getpwuid 1 /* Does getpwuid() work? */ +#define has_memcmp 1 /* Does memcmp() work? */ +#define has_memcpy 1 /* Does memcpy() work? */ +#define has_memmove 1 /* Does memmove() work? */ +#define has_map_fd 0 /* Does map_fd() work? */ +#define has_mmap 1 /* Does mmap() work on regular files? */ +#define has_madvise 0 /* Does madvise() work? */ +#define mmap_signal SIGBUS /* signal received if you reference nonexistent part of mmapped file */ +#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 bad_NFS_rename 0 /* Can rename(A,B) falsely report success? */ +/* typedef int void; */ /* Some ancient compilers need this. */ +#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define has_seteuid 0 /* Does seteuid() work? See ../INSTALL.RCS. */ +#define has_setreuid 0 /* Does setreuid() work? See ../INSTALL.RCS. */ +#define has_setuid 1 /* Does setuid() exist? */ +#define has_sigaction 1 /* Does struct sigaction work? */ +#define has_sa_sigaction 1 /* Does struct sigaction have sa_sigaction? */ +#define has_signal 1 /* Does signal() work? */ +#define signal_type void /* type returned by signal handlers */ +#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ +/* #define has_sigblock ? */ /* Does sigblock() work? */ +/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */ +typedef size_t fread_type; /* type returned by fread() and fwrite() */ +typedef size_t freadarg_type; /* type of their size arguments */ +typedef void *malloc_type; /* type returned by malloc() */ +#define has_getcwd 1 /* Does getcwd() work? */ +/* #define has_getwd ? */ /* Does getwd() work? */ +#define needs_getabsname 0 /* Must we define getabsname? */ +#define has_mktemp 0 /* Does mktemp() work? */ +#define has_NFS 0 /* Might NFS be used? */ +#define has_psiginfo 0 /* Does psiginfo() work? */ +#define has_psignal 0 /* Does psignal() work? */ +#define has_si_errno 0 /* Does siginfo_t have si_errno? */ +#define has_sys_siglist 0 /* Does sys_siglist[] work? */ +/* #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_waitpid 1 /* Does waitpid() work? */ +#define bad_wait_if_SIGCHLD_ignored 1 /* Does ignoring SIGCHLD break wait()? */ +#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */ +#define has_printf_dot 1 /* Does "%.2d" print leading 0? */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +#define has_attribute_format_printf 0 /* Does __attribute__((format(printf,N,N+1))) work? */ +#if has_attribute_format_printf +# define printf_string(m, n) __attribute__((format(printf, m, n))) +#else +# define printf_string(m, n) +#endif +#if has_attribute_format_printf && has_attribute_noreturn + /* Work around a bug in GCC 2.5.x. */ +# define printf_string_exiting(m, n) __attribute__((format(printf, m, n), noreturn)) +#else +# define printf_string_exiting(m, n) printf_string(m, n) exiting +#endif +/* #define has__doprintf ? */ /* Does _doprintf() work? */ +/* #define has__doprnt ? */ /* Does _doprnt() work? */ +/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */ +#define large_memory 1 /* Can main memory hold entire RCS files? */ +#ifndef LONG_MAX +#define LONG_MAX 2147483647L /* long maximum */ +#endif +/* Do struct stat s and t describe the same file? Answer d if unknown. */ +#define same_file(s,t,d) ((s).st_ino==(t).st_ino && (s).st_dev==(t).st_dev) +#define has_utimbuf 1 /* Does struct utimbuf work? */ +#define CO "/usr/local/bin/co" /* name of 'co' program */ +#define COMPAT2 0 /* Are version 2 files supported? */ +#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 DIFFFLAGS "-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 filename separator */ +#define SLASHes '/' /* `case SLASHes:' labels all filename separators */ +#define isSLASH(c) ((c) == SLASH) /* Is arg a filename separator? */ +#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */ +#define X_DEFAULT ",v/" /* default value for -x option */ +#define SLASHSLASH_is_SLASH 0 /* Are // and / the same directory? */ +#define ALL_ABSOLUTE 1 /* Do all subprograms satisfy ROOTPATH? */ +#define DIFF_ABSOLUTE 1 /* Is ROOTPATH(DIFF) true? */ +#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. */ + + +/* The rest is for the benefit of non-standard, traditional hosts. */ +/* Don't bother to declare functions that in traditional hosts do not appear, */ +/* or are declared in .h files, or return int or void. */ + + +/* traditional BSD */ + +#if has_sys_siglist && !defined(sys_siglist) + extern char const * const sys_siglist[]; +#endif + + +/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */ + +/* <fcntl.h> */ +#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 +#endif +#ifndef O_EXCL +#define O_EXCL 0 +#endif + +/* <sys/stat.h> */ +#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> */ +#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) & 0377) == 0) +#endif +#ifndef WTERMSIG +#define WTERMSIG(stat_val) ((stat_val) & 0177) +#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */ +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(stat_val) ((unsigned)(stat_val) - 1 < 0377) +#endif + +/* <unistd.h> */ +char *getlogin P((void)); +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +#endif +#if has_fork && !has_vfork +# undef vfork +# define vfork fork +#endif +#if has_getcwd || !has_getwd + char *getcwd P((char*,size_t)); +#else + char *getwd P((char*)); +#endif +#if has_setuid && !has_seteuid +# undef seteuid +# define seteuid setuid +#endif +#if has_spawn +# if ALL_ABSOLUTE +# define spawn_RCS spawnv +# else +# define spawn_RCS spawnvp +# endif +#else +# if ALL_ABSOLUTE +# define exec_RCS execv +# else +# define exec_RCS execvp +# endif +#endif + +/* utime.h */ +#if !has_utimbuf + struct utimbuf { time_t actime, modtime; }; +#endif + + +/* Standard C library */ + +/* <stdio.h> */ +#ifndef L_tmpnam +#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#if has_mktemp + char *mktemp P((char*)); /* traditional */ +#else + char *tmpnam P((char*)); +#endif + +/* <stdlib.h> */ +char *getenv P((char const*)); +void _exit P((int)) exiting; +void exit P((int)) exiting; +malloc_type malloc P((size_t)); +malloc_type realloc P((malloc_type,size_t)); +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#endif + +/* <string.h> */ +char *strcpy P((char*,char const*)); +char *strchr P((char const*,int)); +char *strrchr P((char const*,int)); +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/gnu/usr.bin/rcs/src/conf.sh b/gnu/usr.bin/rcs/src/conf.sh new file mode 100644 index 00000000000..542d330480b --- /dev/null +++ b/gnu/usr.bin/rcs/src/conf.sh @@ -0,0 +1,2288 @@ +#! /bin/sh +# Output RCS compile-time configuration. +Id='$Id: conf.sh,v 1.1 1996/08/12 04:08:05 millert Exp $' +# Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. +: ${RCSPREFIX=/usr/local/bin/} +: ${ALL_CFLAGS=-Dhas_conf_h} +: ${CC=cc} +: ${COMPAT2=0} +: ${DIFF=${RCSPREFIX}diff} +: ${DIFF3=${DIFF}3} +: ${DIFF3_BIN=1} +: ${DIFFFLAGS=-an} +: ${DIFF_L=1} +: ${DIFF_SUCCESS=0} ${DIFF_FAILURE=1} ${DIFF_TROUBLE=2} +: ${ED=/bin/ed} +: ${SENDMAIL='"/usr/lib/sendmail"'} +# : ${LDFLAGS=} ${LIBS=} tickles old shell bug + +C="$CC $ALL_CFLAGS" +CL="$CC $ALL_CFLAGS $LDFLAGS -o a.out" +L=$LIBS + +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 + +n=' +' +case `echo -n` in +-n) + ech='echo' dots='... \c';; +*) + ech='echo -n' dots='... ' +esac + +$ech >&3 "$0: testing permissions $dots" +rm -f a.d && +date >a.d && +chmod 0 a.d && +{ test -w a.d || cp /dev/null a.d 2>/dev/null; } && { + echo >&3 "$n$0: This command should not be run with superuser permissions." + exit 1 +} +echo >&3 OK +rm -f a.d || exit + +$ech >&3 "$0: testing compiler for plausibility $dots" +echo 'main() { return 0; }' >a.c +rm -f a.exe a.out || exit +$CL a.c $L >&2 || { + echo >&3 "$n$0: The command '$CL a.c $L' failed on a trivial program." + exit 1 +} +echo 'this is not a C source file' >a.c +rm -f a.exe a.out || exit +$CL a.c $L >&2 && { + echo >&3 "$n$0: The command '$CL a.c $L' succeeds when it should fail." + exit 1 +} +echo >&3 OK + +$ech >&3 "$0: configuring exitmain $dots" +cat >a.c <<EOF +#include "a.h" +int main(argc,argv) int argc; char **argv; { return argc-1; } +EOF +rm -f a.exe a.out || exit +if $CL a.c $L >&2 +then A_H=a.h +else + echo >&3 failed + $ech >&3 "$0: attempting to work around Domain/OS brain damage $dots" + cat >a.c <<EOF +#include "a.hap" +int main(argc,argv) int argc; char **argv; { return argc-1; } +EOF + cat <a.h >a.hap && + $CL a.c $L >&2 || exit 1 + # The Domain/OS C compiler refuses to read a.h because the file + # is currently open for writing. Work around this brain damage by + # copying it to a.hap before each compilation; include a.hap instead. + A_H=a.hap +fi +if test -f a.out +then aout=./a.out +elif test -f a.exe +then aout=./a.exe +else + echo >&3 "$n$0: C compiler creates neither a.out nor a.exe." + exit 1 +fi +e='exit(n), 3 /* lint fodder */' +if $aout - +then : +elif $aout +then e=n +fi +case $e in +n) echo >&3 OK;; +*) echo >&3 "return does not work; using exit instead" +esac +echo "#define exitmain(n) return $e /* how to exit from main() */" + +: PREPARE_CC +case $A_H in +a.h) + PREPARE_CC="rm -f $aout";; +*) + echo "rm -f $aout \$1 && cat <a.h >$A_H" >a.pre + PREPARE_CC="sh a.pre" +esac + +for id in _POSIX_C_SOURCE _POSIX_SOURCE +do + $ech >&3 "$0: configuring $id $dots" + cat >a.c <<EOF +#include "$A_H" +#include <stdio.h> +int +main() { +# ifdef fileno +# define f(x) fileno(x) +# else + /* Force a compile-time error if fileno isn't declared. */ + int (*p)() = fileno; +# define f(x) (*p)(x) +# endif + /* Some buggy linkers seem to need the getchar. */ + exitmain(getchar() != '#' || fileno(stdout) != 1); +} +#if syntax_error +syntax error +#endif +EOF + a='/* ' z='*/ ' + case $id in + _POSIX_SOURCE) + version=1003.1-1990 + value=;; + _POSIX_C_SOURCE) + version='1003.1b-1993 or later' + value='2147483647L ';; + esac + $PREPARE_CC || exit + if ($CL a.c $L && $aout <a.c) >&2 + then : + elif $PREPARE_CC || exit; ($CL -D$id=$value a.c $L && $aout <a.c) >&2 + then a= z= + fi + case $a in + ?*) echo >&3 OK;; + '') echo >&3 "must define it, unfortunately" + esac + echo "$a#define $id $value$z/* if strict C + Posix $version */" +done + +cat <<'EOF' + +#include <errno.h> +#include <stdio.h> +#include <time.h> + +/* Comment out #include lines below that do not work. */ +EOF + +$ech >&3 "$0: configuring how to check for syntax errors $dots" +# Run `$CS a.c $LS' instead of `$CL a.c $L' for compile-time checking only. +# This speeds up the configuration process. +if + rm -f a.s && $C -S a.c >&2 && test -s a.s && rm -f a.s && + if $C -S -Dsyntax_error=1 a.c >&2 && test -s a.s + then false + else : + fi +then + # Generate assembly language output. + CS="$C -S" LS= o=a.s PREPARE_CC="$PREPARE_CC $o" +elif + rm -f a.o a.obj && $C -c a.c >&2 && + if test -s a.o + then o=a.o + elif test -s a.obj + then o=a.obj + else false + fi && + if $C -c -Dsyntax_error=1 a.c >&2 && test -s $o + then false + else : + fi +then + # Generate object code. + CS="$C -c" LS= PREPARE_CC="$PREPARE_CC $o" +else + # Generate an executable. + CS=$CL LS=$L o=$aout +fi +CS_OK="test -s $o" +echo >&3 $CS + +# 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 mach/mach net/errno \ + pwd siginfo signal stdlib string \ + sys/mman sys/wait ucontext unistd utime vfork +do + i="#include <$h.h>" + $ech >&3 "$0: configuring $i $dots" + cat >a.c <<EOF +#include "$A_H" +$i +int main(){ exitmain(0); } +EOF + ok=OK + $PREPARE_CC || exit + $CS a.c $LS >&2 && $CS_OK || { + case $h in + string) + i='#include <strings.h>' + ok="$i instead";; + *) + i="/* $i */" + ok="commenting it out" + esac + case $h in + signal) has_signal=0 + esac + } + echo >&3 "$ok" + echo "$i" +done + +cat <<'EOF' + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +EOF + +# has_sys_param_h +$ech >&3 "$0: configuring has_sys_param_h $dots" +cat >a.c <<EOF +#include "$A_H" +#include <sys/param.h> +int main() { exitmain(0); } +EOF +$PREPARE_CC || exit +if $CS a.c $LS >&2 && $CS_OK +then h=1 ok=OK +else h=0 ok=absent +fi +echo >&3 $ok +echo "#define has_sys_param_h $h /* Does #include <sys/param.h> work? */" + +# We must do errno next, because has_readlink needs it. +/* <errno.h> */ +$ech >&3 "$0: configuring errno $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(errno != errno); } +EOF +$PREPARE_CC || exit +if $CS a.c $LS >&2 +then a='/* ' z=' */' ok=OK +else a= z= ok='declaration missing' +fi +echo >&3 $ok +echo "${a}extern int errno;$z /* Uncomment if <errno.h> doesn't declare errno. */" +rm -f a.c || exit + +# We must do has_readlink next, because it might generate +# #include directives that affect later definitions. + +$ech >&3 "$0: configuring has_readlink, readlink_isreg_errno $dots" +cat >a.c <<EOF +#include "$A_H" +static char b[7]; +int +main() { + if (readlink("a.sym2",b,7) == 6 && strcmp(b,"a.sym1") == 0 && + readlink("a.c",b,7) == -1 && errno != ENOENT + ) { + if (errno == EINVAL) + printf("EINVAL\n"); + else + printf("%d\n", errno); + exitmain(ferror(stdout) || fclose(stdout)!=0); + } + exitmain(1); +} +EOF +$PREPARE_CC a.sym* || exit +readlink_isreg_errno='?' +if (ln -s a.sym1 a.sym2 && $CL a.c $L) >&2 && readlink_isreg_errno=`$aout` +then h=1 +else h=0 +fi +echo >&3 $h, $readlink_isreg_errno +cat <<EOF +#define has_readlink $h /* Does readlink() work? */ +#define readlink_isreg_errno $readlink_isreg_errno /* errno after readlink on regular file */ + +#if has_readlink && !defined(MAXSYMLINKS) +# if has_sys_param_h +# include <sys/param.h> +# endif +# ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 /* BSD; not standard yet */ +# endif +#endif +EOF + +# *_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 off_t pid_t sig_atomic_t size_t ssize_t time_t uid_t +do + $ech >&3 "$0: configuring $t $dots" + case $t in + size_t) i=unsigned;; + off_t|time_t) i=long;; + *) i=int;; + esac + $PREPARE_CC || exit + if $CS -Dt=$t a.c $LS >&2 && $CS_OK + then ok=OK a='/* ' z=' */' + else ok=$i a= z= + fi + echo >&3 $ok + echo "${a}typedef $i $t;$z" +done + +cat <<'EOF' + +/* Comment out the keyword definitions below if the keywords work. */ +EOF + +for i in const volatile +do + $ech >&3 "$0: configuring $i $dots" + cat >a.c <<EOF +# include "$A_H" + int main(); + enum Boolean { false, true }; + int hpux8_05barf(); + static enum Boolean $i zero; + int hpux8_05barf(x) int x; { return x; } + static enum Boolean $i * $i azero = &zero; + static enum Boolean $i * $i * $i aazero = &azero; + static enum Boolean * $i arzero[1]; + static sig_atomic_t $i sigzero; + int sco3_2v4barf() { + /* SCO 3.2v4 native compiler barfs on this. */ + char *t; + char $i *s = 0 ? (char *)0 : (char $i *)0; + *t++ = '\0'; + } + int main() { + enum Boolean *p = arzero[sigzero]; + switch (zero) { + case false: exitmain(!p || **aazero); + default: exitmain(hpux8_05barf(1)); + } + } +EOF + a= z= ok='broken' + $PREPARE_CC || exit + if $CS a.c $LS >&2 && $CS_OK + then + cat >a.c <<EOF + char $i *p; + char *q; + typedef unsigned char $i *Iptr_type; + struct { Iptr_type lim; } s, *f = &s; + int main() { + Iptr_type lim; + lim = f->lim; + p = p == q ? p : ""; + p = p == "" ? p : q; + return !!lim; + } +EOF + $PREPARE_CC || exit + if $CS a.c $LS >&2 && $CS_OK + then + case $i in + const) + # Check for common execv misdeclaration. + cat >a.c <<EOF +# include "$A_H" + static char * const *p; + int main() { return execv("/bin/sh", p); } +EOF + $PREPARE_CC || exit + $CS a.c $LS >&2 && $CS_OK + esac && a='/* ' z=' */' ok=OK + fi + fi + echo >&3 $ok + echo "$a#define $i$z" +done + +echo >&3 "$0: configuring has_prototypes, has_stdarg, has_varargs, va_start_args $dots" +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" + +struct buf { int x; }; +int pairnames P((int,char**,FILE*(*)P((struct buf*,struct stat*,int)),int,int)); /* a la rcsbase.h */ +FILE *(*rcsopen)P((struct buf*,struct stat*,int)); /* a la rcsfnms.c */ + +static char *e(p,i) char **p; int i; { return p[i]; } +#if has_prototypes +static char *f(char *(*g)(char**,int), char **p, ...) +#else +static char *f(g, p, va_alist) char *(*g)(); char **p; va_dcl +#endif +{ + char *s; + va_list v; + vararg_start(v,p); + s = g(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(e,argv,0) != argv[0] || f(e,argv,1) != argv[1]); +} +EOF +for has_prototypes in 1 0 +do + for has_stdarg in 1 v 0 + do + case $has_stdarg in + 1) has_varargs=-1;; + 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 + $PREPARE_CC || exit + $CL \ + -Dhas_prototypes=$has_prototypes \ + -Dhas_stdarg=$has_stdarg \ + -Dhas_varargs=$has_varargs \ + -Dva_start_args=$va_start_args \ + a.c $L >&2 && $aout && break + done && break + done && break +done || { + echo >&3 $0: cannot deduce has_prototypes, has_stdarg, va_start_args + exit 1 +} +echo >&3 $has_prototypes, $has_stdarg, $has_varargs, $va_start_args +case $has_varargs in +-1) a='/* ' z='*/ ' has_varargs='?';; +*) a= z= +esac +cat - a.ha <<EOF + +/* Define boolean symbols to be 0 (false, the default), or 1 (true). */ +#define has_prototypes $has_prototypes /* Do function prototypes work? */ +#define has_stdarg $has_stdarg /* Does <stdarg.h> work? */ +$a#define has_varargs $has_varargs $z/* Does <varargs.h> work? */ +#define va_start_args $va_start_args /* How many args does va_start() take? */ + +#if O_BINARY + /* Text and binary i/o behave differently. */ + /* This is incompatible with Posix and Unix. */ +# define FOPEN_RB "rb" +# define FOPEN_R_WORK (Expand==BINARY_EXPAND ? "r" : "rb") +# define FOPEN_WB "wb" +# define FOPEN_W_WORK (Expand==BINARY_EXPAND ? "w" : "wb") +# define FOPEN_WPLUS_WORK (Expand==BINARY_EXPAND ? "w+" : "w+b") +# define OPEN_O_BINARY O_BINARY +#else + /* + * Text and binary i/o behave the same. + * Omit "b", since some nonstandard hosts reject it. + */ +# define FOPEN_RB "r" +# define FOPEN_R_WORK "r" +# define FOPEN_WB "w" +# define FOPEN_W_WORK "w" +# define FOPEN_WPLUS_WORK "w+" +# define OPEN_O_BINARY 0 +#endif + +/* This may need changing on non-Unix systems (notably DOS). */ +#define OPEN_CREAT_READONLY (S_IRUSR|S_IRGRP|S_IROTH) /* lock file mode */ +#define OPEN_O_LOCK 0 /* extra open flags for creating lock file */ +#define OPEN_O_WRONLY O_WRONLY /* main open flag for creating a lock file */ + +/* Define or comment out the following symbols as needed. */ +EOF + +$ech >&3 "$0: configuring bad_chmod_close $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef O_RDONLY +# define O_RDONLY 0 +#endif +int +main() { + int f; + exitmain( + (f = open("a.c", O_RDONLY)) < 0 || + chmod("a.c", 0) != 0 || + close(f) != 0 + ); +} +EOF +$PREPARE_CC || exit +if $CL a.c $L >&2 && $aout +then b=0 ok=OK +else b=1 ok='will work around bug' +fi +echo >&3 $ok +echo "#define bad_chmod_close $b /* Can chmod() close file descriptors? */" +rm -f a.c || exit + +$ech >&3 "$0: configuring bad_creat0 $dots" +cat >a.c <<EOF +#include "$A_H" +#if defined(O_CREAT) && defined(O_WRONLY) +# define creat0(f) open(f, O_CREAT|O_WRONLY, 0) +#else +# define creat0(f) creat(f, 0) +#endif +char buf[17000]; +int +main() { + int f; + exitmain( + (f = creat0("a.d")) < 0 || + write(f, buf, sizeof(buf)) != sizeof(buf) || + close(f) != 0 + ); +} +EOF +$PREPARE_CC a.d || exit +if $CL a.c $L >&2 && $aout && test -f a.d && test ! -w a.d +then b=0 ok=OK +else b=1 ok='will work around bug' +fi +echo >&3 $ok +echo "#define bad_creat0 $b /* Do writes fail after creat(f,0)? */" +rm -f a.d || exit + +$ech >&3 "$0: configuring bad_fopen_wplus $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(!fopen("a.d","w+")); } +EOF +$PREPARE_CC || exit +if echo nonempty >a.d && $CL a.c $L >&2 && $aout && test ! -s a.d +then b=0 ok=OK +else b=1 ok='will work around bug' +fi +echo >&3 $ok +echo "#define bad_fopen_wplus $b /* Does fopen(f,\"w+\") fail to truncate f? */" + +echo "#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */" + +$ech >&3 "$0: configuring has_attribute_noreturn $dots" +cat >a.c <<EOF +#include "$A_H" +static void e P((int)) __attribute__((noreturn)); +static void e(i) int i; { exit(i); } +int main() { e(0); } +EOF +$PREPARE_CC || exit +h=0 ok='does not work' +if out=`$CS a.c $LS 2>&1` +then + case $out in + *noreturn*) ;; + *) h=1 ok=OK + esac +fi +echo >&2 "$out" +echo >&3 $ok +cat <<EOF +#define has_attribute_noreturn $h /* Does __attribute__((noreturn)) work? */ +#if has_attribute_noreturn +# define exiting __attribute__((noreturn)) +#else +# define exiting +#endif +EOF +rm -f a.c || exit + +$ech >&3 "$0: configuring has_dirent, void_closedir $dots" +cat >a.c <<EOF +#include "$A_H" +#if void_closedir +# define close_directory(d) (closedir(d), 0) +#else +# define close_directory(d) closedir(d) +#endif +int +main() { + DIR *d = opendir("."); + struct dirent *e; + while ((e = readdir(d))) + if (strcmp(e->d_name, "a.c") == 0 && close_directory(d) == 0) + exitmain(0); + exitmain(1); +} +EOF +$PREPARE_CC || exit +has_dirent=0 ok='does not work' +void_closedir=? a='/* ' z='*/ ' +for v in 0 1 +do + if $CL -Dvoid_closedir=$v a.c $L >&2 && $aout + then + has_dirent=1 ok=OK + void_closedir=$v a= z= + case $v in + 1) ok='OK, but closedir yields void' + esac + break + fi +done +echo >&3 $ok +echo "#define has_dirent $has_dirent /* Do opendir(), readdir(), closedir() work? */" +echo "$a#define void_closedir $void_closedir $z/* Does closedir() yield void? */" + +$ech >&3 "$0: configuring has_fchmod $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +int main() { exitmain(fchmod(STDIN_FILENO,0) != 0); } +EOF +$PREPARE_CC || exit +if $CL a.c $L >&2 && $aout <a.c && test ! -r a.c +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_fchmod $h /* Does fchmod() work? */" +rm -f a.c || exit + +$ech >&3 "$0: configuring has_fflush_input $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +int main() { + exitmain( + getchar() == EOF + || fseek(stdin, 0L, SEEK_SET) != 0 + || fflush(stdin) != 0 + || lseek(STDIN_FILENO, (off_t)0, SEEK_CUR) != 0 + ); +} +EOF +$PREPARE_CC || exit +if $CL a.c $L >&2 && $aout <a.c +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_fflush_input $h /* Does fflush() work on input files? */" +rm -f a.c || exit + +$ech >&3 "$0: configuring has_fputs $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(fputs("Hello\"\nworld", stdout) < 0); } +EOF +Hello='Hello" +world' +$PREPARE_CC a.a || exit +if $CL a.c $L >&2 && $aout >a.a && x=`$aout` && test " $x" = " $Hello" +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_fputs $h /* Does fputs() work? */" + +$ech >&3 "$0: configuring has_ftruncate $dots" +cat >a.c <<EOF +#include "$A_H" +/* + * We'd like to test ftruncate(creat(f,0), 0), + * since that's the way RCS uses it, + * but a common bug causes it to fail over NFS. + * Since we must defend against this bug at run time anyway, + * we don't bother to check for it at compile time. + * So we test ftruncate(creat(f,0200), 0) instead. + */ +#if defined(O_CREAT) && defined(O_WRONLY) && defined(S_IWUSR) +# define creat0200(f) open(f, O_CREAT|O_WRONLY, S_IWUSR) +#else +# define creat0200(f) creat(f, 0200) +#endif +int +main(argc, argv) int argc; char **argv; { + int f = creat0200(argv[1]); + if (f<0 || write(f,"abc",3)!=3 || ftruncate(f,(off_t)0)!=0 || close(f)!=0) + exitmain(1); + exitmain(0); +} +EOF +$PREPARE_CC a.a || exit +if ($CL a.c $L && $aout a.a && test -w a.a && test ! -s a.a) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_ftruncate $h /* Does ftruncate() work? */" + +$ech >&3 "$0: configuring has_getuid $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(getuid()!=getuid()); } +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then has_getuid=1 ok=OK +else has_getuid=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_getuid $has_getuid /* Does getuid() work? */" + +case $has_getuid in +0) + a='/* ' z='*/ ' h=?;; +*) + $ech >&3 "$0: configuring has_getpwuid $dots" + a= z= + cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(!getpwuid(0)); } +EOF + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then h=1 ok=OK + else h=0 ok='does not work' + fi + echo >&3 $ok +esac +echo "$a#define has_getpwuid $h $z/* Does getpwuid() work? */" + +$ech >&3 "$0: configuring has_kill $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(kill(getpid(), 0) != 0); } +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then has_kill=1 ok=OK +else has_kill=0 ok='does not work' +fi +echo >&3 $ok +# Used only by this script, not by RCS, so we don't output it to stdout. + +$ech >&3 "$0: configuring has_memcmp $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(memcmp("beautiful","beautiful",10) != 0); } +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_memcmp $h /* Does memcmp() work? */" + +$ech >&3 "$0: configuring has_memcpy $dots" +cat >a.c <<EOF +#include "$A_H" +char a[3]; +int +main() { + memcpy(a,"xy",3); + exitmain(strcmp(a,"xy")!=0); +} +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_memcpy $h /* Does memcpy() work? */" + +$ech >&3 "$0: configuring has_memmove $dots" +cat >a.c <<EOF +#include "$A_H" +static char a[4]; +int +main() { + strcpy(a, "xy"); + memmove(a+1, a, 3); + exitmain(strcmp(a,"xxy")!=0); +} +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_memmove $h /* Does memmove() work? */" + +$ech >&3 "$0: configuring has_map_fd, has_mmap, has_madvise, mmap_signal $dots" +rm -f a.c a.d a.e || exit +cat >a.c <<EOF +#define CHAR1 '#' /* the first character in this file */ +#include "$A_H" +static char *a; +static 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 +#ifndef WTERMSIG +#define WTERMSIG(s) ((s)&0177) +#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */ +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(s) (((s)&0377) != 0177 && WTERMSIG(s) != 0) +#endif +#ifndef MAP_FAILED +#define MAP_FAILED (-1) +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +int +main(argc, argv) int argc; char **argv; { + int s = 0; +#if TRY_MAP_FD + kern_return_t kr; + vm_address_t va; +#endif + + if (fstat(STDIN_FILENO, &b) != 0) { + perror("fstat"); + exitmain(1); + } +# if TRY_MAP_FD + kr = map_fd(STDIN_FILENO, 0, &va, TRUE, b.st_size); + if (kr != KERN_SUCCESS) { + mach_error("map_fd", kr); + exitmain(1); + } + a = (char *) va; +# else + a = mmap( + (char *)0, b.st_size, PROT_READ, MAP_SHARED, + STDIN_FILENO, (off_t)0 + ); + if (a == (char *)MAP_FAILED) { + perror("mmap"); + exitmain(1); + } + if (!MADVISE_OK) { + perror("madvise"); + exitmain(1); + } +# endif + if (*a != CHAR1) + exitmain(1); + if (1 < argc) { + pid_t p, w; + int f = creat(argv[1], 0); + /* + * Some buggy hosts yield ETXTBSY if you try to use creat + * to truncate a file that is mmapped. On such hosts, + * don't bother to try to figure out what mmap_signal is. + */ +# ifndef ETXTBSY +# define ETXTBSY (-1) +# endif + if (f<0 ? errno!=ETXTBSY : close(f)!=0) { + perror(argv[1]); + exitmain(1); + } + if ((p = fork()) < 0) { + perror("fork"); + exitmain(1); + } + if (!p) + /* Refer to nonexistent storage, causing a signal in the child. */ + _exit(a[0] != 0); + while ((w = wait(&s)) != p) + if (w < 0) { + perror("wait"); + exitmain(1); + } + s = WIFSIGNALED(s) ? WTERMSIG(s) : 0; + } +# if TRY_MAP_FD + kr = vm_deallocate(task_self(), va, (vm_size_t) b.st_size); + if (kr != KERN_SUCCESS) { + mach_error("vm_deallocate", kr); + exitmain(1); + } +# else + if (munmap(a, b.st_size) != 0) { + perror("munmap"); + exitmain(1); + } +# endif + if (1 < argc) { +# ifdef SIGBUS + if (s == SIGBUS) { printf("SIGBUS\n"); s = 0; } +# endif +# ifdef SIGSEGV + if (s == SIGSEGV) { printf("SIGSEGV\n"); s = 0; } +# endif + if (s) printf("%d\n", s); + } + exitmain(ferror(stdout) || fclose(stdout)!=0); +} +EOF +# AIX 3.2.0 read-only mmap updates last-modified time of file! Check for this. +sleep 2 +cp a.c a.d || exit +sleep 2 +has_map_fd=? has_mmap=? has_madvise=? mmap_signal= +case `(uname -s -r -v) 2>/dev/null` in +'HP-UX '[A-Z].08.07*) ;; + # mmap can crash the OS under HP-UX 8.07, so don't even test for it. +'HP-UX '[A-Z].09.*) ;; + # HP-UX 9.0[135]? s700 mmap has a data integrity problem + # when a diskless cnode accesses data on the cnode's server disks. + # We don't know of any way to test whether the bug is present. + # HP patches PHKL_4605 and PHKL_4607 should fix the bug; + # see <http://support.mayfield.hp.com/slx/html/ptc_hpux.html>. + # The above code (perhaps rashly) assumes HP-UX 10 supports mmap. +'SunOS 5.4 Generic' | 'SunOS 5.4 Generic_101945-?') ;; + # Early editions of SunOS 5.4 are reported to have problems with mmap + # that generate NUL bytes in RCS files with a Solaris 2.2 NFS server. + # This has been reported to be fixed as of patch 101945-10. +*) + $PREPARE_CC || exit + if ($CL -DTRY_MAP_FD=1 a.c $L && $aout <a.c) >&2 + then + has_map_fd=1 + else + has_map_fd=0 has_mmap=0 has_madvise=0 + if ($CL -DMADVISE_OK=1 a.c $L && $aout <a.c) >&2 + then + case `ls -t a.c a.d` in + a.d*) + has_mmap=1 + rm -f a.ous + mv $aout a.ous + $PREPARE_CC || exit + if ($CL a.c $L && $aout <a.c) >&2 + then has_madvise=1; rm -f a.ous + else rm -f $aout && mv a.ous $aout + fi || exit + esac + fi + fi + case $has_map_fd$has_mmap in + *1*) + # Find out what signal is sent to RCS + # when someone unexpectedly truncates a file + # while RCS has it mmapped. + rm -f a.e && cp a.c a.e && + mmap_signal=`$aout a.e <a.e` || exit + esac +esac +echo >&3 $has_map_fd, $has_mmap, $has_madvise, $mmap_signal +case $has_map_fd in +'?') a='/* ' z='*/ ';; +*) a= z=;; +esac +echo "$a#define has_map_fd $has_map_fd $z/* Does map_fd() work? */" +case $has_mmap in +'?') a='/* ' z='*/ ';; +*) a= z=;; +esac +echo "$a#define has_mmap $has_mmap $z/* Does mmap() work on regular files? */" +echo "$a#define has_madvise $has_madvise $z/* Does madvise() work? */" +case $mmap_signal in +?*) a= z=;; +'') a='/* ' z='*/ ' mmap_signal='?' +esac +echo "$a#define mmap_signal $mmap_signal $z/* signal received if you reference nonexistent part of mmapped file */" + +$ech >&3 "$0: configuring has_rename, bad_a_rename, bad_b_rename $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(rename("a.a","a.b") != 0); } +EOF +echo a >a.a && $PREPARE_CC a.b || exit +if ($CL a.c $L && $aout && test -f a.b) >&2 +then + h=1 + rm -f a.a a.b && + echo a >a.a && chmod -w a.a || exit + if $aout && 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 $aout && 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 >&3 $h, $a, $b +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? */" +echo "#define bad_NFS_rename 0 /* Can rename(A,B) falsely report success? */" + +$ech >&3 "$0: configuring void, VOID $dots" +cat >a.c <<EOF +#include "$A_H" +void f() {} +int main() {f(); exitmain(0);} +EOF +$PREPARE_CC || exit +if $CS a.c $LS >&2 && $CS_OK +then + v='(void) ' + echo '/* typedef int void; */ /* Some ancient compilers need this. */' + ok=OK +else + v= + echo 'typedef int void;' + ok='your compiler is a museum piece' +fi +echo >&3 $ok +echo "#define VOID $v/* 'VOID e;' discards the value of an expression 'e'. */" + +case $has_getuid in +0) + a='/* ' z='*/ ' has_seteuid=?;; +*) + $ech >&3 "$0: configuring has_seteuid $dots" + a= z= + cat >a.c <<EOF +#include "$A_H" +int +main() { +/* Guess, don't test. Ugh. Testing would require running conf.sh setuid. */ +/* If the guess is wrong, a setuid RCS will detect the problem at runtime. */ +#if !_POSIX_VERSION + exitmain(1); +#else + exitmain(seteuid(geteuid()) != 0); +#endif +} +EOF + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then has_seteuid=1 ok='OK, I guess' + else has_seteuid=0 ok='does not work' + fi + echo >&3 $ok +esac +echo "$a#define has_seteuid $has_seteuid $z/* Does seteuid() work? See ../INSTALL.RCS. */" + +echo "#define has_setreuid 0 /* Does setreuid() work? See ../INSTALL.RCS. */" + +$ech >&3 "$0: configuring has_setuid $dots" +h=$has_seteuid +case $h in +0) + cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(setuid(getuid()) != 0); } +EOF + $PREPARE_CC || exit + ($CL a.c $L && $aout) >&2 && h=1 ok='OK, I guess' +esac +echo >&3 $ok +echo "$a#define has_setuid $h $z/* Does setuid() exist? */" + +$ech >&3 "$0: configuring has_sigaction $dots" +cat >a.c <<EOF +#include "$A_H" +static sig_atomic_t volatile gotsig; +#ifdef SA_SIGINFO + static void catchsig(i, s, v) int i; siginfo_t *s; void *v; { gotsig = 1; } +#else + static void catchsig(i) int i; { gotsig = 1; } +#endif +int +main(argc, argv) int argc; char **argv; { + struct sigaction s; + if (sigaction(SIGINT, (struct sigaction*)0, &s) != 0) + exitmain(1); +# if has_sa_sigaction + s.sa_sigaction = catchsig; +# else + s.sa_handler = catchsig; +# endif +# ifdef SA_SIGINFO + s.sa_flags |= SA_SIGINFO; +# endif + if (sigaddset(&s.sa_mask, SIGINT) != 0) + exitmain(1); + if (sigaction(SIGINT, &s, (struct sigaction*)0) != 0) + exitmain(1); +# if has_kill + kill(getpid(), SIGINT); +# else + raise(SIGINT); +# endif + exitmain(gotsig != 1); +} +EOF +$PREPARE_CC || exit +if ($CL -Dhas_kill=$has_kill a.c $L && $aout) >&2 +then has_sigaction=1 ok=OK +else has_sigaction=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_sigaction $has_sigaction /* Does struct sigaction work? */" +$ech >&3 "$0: configuring has_sa_sigaction $dots" +has_sa_sigaction=0 ok='does not work' +case $has_sigaction in +1) + $PREPARE_CC || exit + if ($CL -Dhas_kill=$has_kill -Dhas_sa_sigaction=1 a.c $L && $aout) >&2 + then has_sa_sigaction=1 ok=OK + fi +esac +echo >&3 $ok +echo "#define has_sa_sigaction $has_sa_sigaction /* Does struct sigaction have sa_sigaction? */" + +$ech >&3 "$0: configuring has_signal, signal_type, sig_zaps_handler $dots" +case $has_signal,$has_sigaction in +1,0) + cat >a.c <<EOF +#include "$A_H" +#if !defined(signal) && declare_signal + signal_type (*signal P((int,signal_type(*)signal_args)))signal_args; +#endif +static signal_type nothing(i) int i; {} +int +main(argc, argv) int argc; char **argv; { + signal(SIGINT, nothing); +# if has_kill + while (--argc) + kill(getpid(), SIGINT); + exitmain(0); +# else + /* Pretend that sig_zaps_handler; better safe than sorry. */ + exitmain(2 < argc); +# endif +} +EOF + for declare_signal in 1 0 + do + for signal_type in void int + do + for signal_args in 'P((int))' '()' + do + $PREPARE_CC || exit + ($CL \ + -Ddeclare_signal=$declare_signal \ + -Dhas_kill=$has_kill \ + -Dsignal_args="$signal_args" \ + -Dsignal_type=$signal_type \ + a.c $L && $aout 1) >&2 && break + done && break + done && break + done || { + echo >&3 $0: cannot deduce signal_type + exit 1 + } + if $aout 1 2 >&2 + then sig_zaps_handler=0 + else sig_zaps_handler=1 + fi;; +*) + signal_type=void + sig_zaps_handler=0 +esac +echo >&3 $has_signal, $signal_type, $sig_zaps_handler +cat <<EOF +#define has_signal $has_signal /* Does signal() work? */ +#define signal_type $signal_type /* type returned by signal handlers */ +#define sig_zaps_handler $sig_zaps_handler /* Must a signal handler reinvoke signal()? */ +EOF + +a='/* ' z='*/ ' +b='/* ' y='*/ ' +case $has_sigaction in +1) + h=?;; +*) + $ech >&3 "$0: configuring has_sigblock, sigmask $dots" + ok=OK + 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)); +# if has_kill + exitmain(kill(getpid(), SIGHUP) != 0); +# else + exitmain(raise(SIGHUP) != 0); +# endif +} +EOF + if + $PREPARE_CC || exit + ($CL -Dhas_kill=$has_kill a.c $L && $aout) >&2 + then h=1 + elif + $PREPARE_CC || exit + ($CL -Dhas_kill=$has_kill -Ddefine_sigmask=1 a.c $L && $aout) >&2 + then h=1 b= y= ok='definition needed' + else h=0 + fi + echo >&3 "$h, $ok" +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. */" + +$ech >&3 "$0: configuring fread_type, freadarg_type $dots" +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 + $PREPARE_CC || exit + ( + $CL \ + -Ddeclare_fread=$declare_fread \ + -Dfreadarg_type=$freadarg_type \ + -Dfread_type=$fread_type \ + a.c $L && + $aout <a.c + ) >&2 && break + done && break + done && break +done || { + echo >&3 $0: cannot deduce fread types + exit 1 +} +echo >&3 $fread_type, $freadarg_type +cat <<EOF +typedef $fread_type fread_type; /* type returned by fread() and fwrite() */ +typedef $freadarg_type freadarg_type; /* type of their size arguments */ +EOF + +$ech >&3 "$0: configuring malloc_type $dots" +cat >a.c <<EOF +#include "$A_H" +typedef void *malloc_type; +#ifndef malloc + malloc_type malloc(); +#endif +static malloc_type identity P((malloc_type)); +static malloc_type identity(x) malloc_type x; { return x; } +int main() { exitmain(!identity(malloc(1))); } +EOF +$PREPARE_CC || exit +if $CS a.c $LS >&2 && $CS_OK +then t=void +else t=char +fi +echo >&3 $t +echo "typedef $t *malloc_type; /* type returned by malloc() */" + +$ech >&3 "$0: configuring has_getcwd $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef getcwd + char *getcwd(); +#endif +static char buf[10000]; +int main() { exitmain(!getcwd(buf,10000)); } +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then has_getcwd=1 ok=OK +else has_getcwd=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_getcwd $has_getcwd /* Does getcwd() work? */" + +case $has_getcwd in +1) + a='/* ' z='*/ ' h=?;; +*) + a= z= + $ech >&3 "$0: configuring has_getwd $dots" + cat >a.c <<EOF +#include "$A_H" +#include <sys/param.h> +#ifndef getwd + char *getwd(); +#endif +static char buf[MAXPATHLEN]; +int main() { exitmain(!getwd(buf)); } +EOF + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then h=1 ok=OK + else h=0 ok='does not work' + fi + echo >&3 $ok +esac +echo "$a#define has_getwd $h $z/* Does getwd() work? */" +echo "#define needs_getabsname 0 /* Must we define getabsname? */" + +$ech >&3 "$0: configuring has_mktemp $dots" +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 +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok=absent +fi +echo >&3 $ok +echo "#define has_mktemp $h /* Does mktemp() work? */" + +: configuring has_NFS +echo "#define has_NFS 1 /* Might NFS be used? */" + +case $has_signal,$has_sigaction in +1,0) + has_psiginfo=0;; +*) + $ech >&3 "$0: configuring has_psiginfo $dots" + cat >a.c <<EOF +#include "$A_H" +static signal_type +catchsig(s, i, c) int s; siginfo_t *i; void *c; { + if (i) + psiginfo(i, "test"); + exit(0); +} +int +main() { + struct sigaction s; + if (sigaction(SIGINT, (struct sigaction*)0, &s) != 0) + exitmain(1); +# if has_sa_sigaction + s.sa_sigaction = catchsig; +# else + s.sa_handler = catchsig; +# endif + if (sigaddset(&s.sa_mask, SIGINT) != 0) + exitmain(1); + s.sa_flags |= SA_SIGINFO; + if (sigaction(SIGINT, &s, (struct sigaction*)0) != 0) + exitmain(1); +# if has_kill + kill(getpid(), SIGINT); +# else + raise(SIGINT); +# endif + exitmain(1); +} +EOF + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then has_psiginfo=1 ok=OK + else has_psiginfo=0 ok=absent + fi + echo >&3 $ok +esac +echo "#define has_psiginfo $has_psiginfo /* Does psiginfo() work? */" + +case $has_signal in +1) + $ech >&3 "$0: configuring has_psignal $dots" + cat >a.c <<EOF +#include "$A_H" +int main() { psignal(SIGINT, ""); exitmain(0); } +EOF + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then has_psignal=1 ok=OK + else has_psignal=0 ok=absent + fi + echo >&3 $ok;; +*) has_psignal=0 +esac +echo "#define has_psignal $has_psignal /* Does psignal() work? */" + +case $has_psiginfo in +1) + $ech >&3 "$0: configuring has_si_errno $dots" + cat >a.c <<EOF +#include "$A_H" +siginfo_t a; +int main() { exitmain(a.si_errno); } +EOF + $PREPARE_CC || exit + if $CS a.c $LS >&2 && $CS_OK + then h=1 ok=OK + else h=0 ok=absent + fi + echo >&3 $ok + a= z=;; +*) h=? a='/* ' z='*/ ' +esac +echo "$a#define has_si_errno $h $z/* Does siginfo_t have si_errno? */" + +case $has_signal,$has_psignal in +1,0) + $ech >&3 "$0: configuring has_sys_siglist $dots" + cat >a.c <<EOF +#include "$A_H" +#if !defined(sys_siglist) && declare_sys_siglist + extern char const * const sys_siglist[]; +#endif +int main() { exitmain(!sys_siglist[1][0]); } +EOF + $PREPARE_CC || exit + h=0 ok=absent + for d in 1 0 + do ($CL -Ddeclare_sys_siglist=$d a.c $L && $aout) >&2 && + h=1 && ok=OK && break + done + echo >&3 $ok + a= z=;; +*) h=? a='/* ' z='*/ ' +esac +echo "$a#define has_sys_siglist $h $z/* Does sys_siglist[] work? */" + +$ech >&3 "$0: configuring strchr $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef strchr + char *strchr(); +#endif +int main() {exitmain(!strchr("abc", 'c'));} +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then a='/* ' z='*/ ' ok=OK +else a= z= ok='does not work' +fi +echo >&3 $ok +echo "$a#define strchr index $z/* Use old-fashioned name for strchr()? */" + +$ech >&3 "$0: configuring strrchr $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef strrchr + char *strrchr(); +#endif +int main() {exitmain(!strrchr("abc", 'c'));} +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then a='/* ' z='*/ ' ok=OK +else a= z= ok='does not work' +fi +echo >&3 $ok +echo "$a#define strrchr rindex $z/* Use old-fashioned name for strrchr()? */" + +$ech >&3 "$0: configuring bad_unlink $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(unlink("a.c") != 0); } +EOF +$PREPARE_CC && chmod -w a.c || exit +if ($CL a.c $L && $aout) >&2 && test ! -f a.c +then b=0 ok=OK +else b=1 ok='will work around bug' +fi +rm -f a.c || exit +echo >&3 $ok +echo "#define bad_unlink $b /* Does unlink() fail on unwritable files? */" + +$ech >&3 "$0: configuring has_vfork $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#if !TRY_VFORK +# undef vfork +# define vfork fork +#endif +#if !TRY_WAITPID +# undef waitpid +# define waitpid(p,s,o) wait(s) +#endif + +int +main() { + pid_t parent = getpid(); + pid_t child = vfork(); + + if (child == 0) { + /* + * On sparc systems, changes by the child to local and incoming + * argument registers are propagated back to the parent. + * The compiler is told about this with #include <vfork.h>, + * but some compilers (e.g. gcc -O) don't grok <vfork.h>. + * Test for this by using lots of local variables, at least + * as many local variables as 'main' has allocated so far + * including compiler temporaries. 4 locals are enough for + * gcc 1.40.3 on a sparc, but we use 8 to be safe. + * A buggy compiler should reuse the register of 'parent' + * for one of the local variables, since it will think that + * 'parent' can't possibly be used any more in this routine. + * Assigning to the local variable will thus munge 'parent' + * in the parent process. + */ + pid_t + p = getpid(), + p1 = getpid(), p2 = getpid(), + p3 = getpid(), p4 = getpid(), + p5 = getpid(), p6 = getpid(), + p7 = getpid(); + /* + * Convince the compiler that p..p7 are live; otherwise, it might + * use the same hardware register for all 8 local variables. + */ + if (p!=p1 || p!=p2 || p!=p3 || p!=p4 || p!=p5 || p!=p6 || p!=p7) + _exit(1); + + /* + * On some systems (e.g. IRIX 3.3), + * vfork doesn't separate parent from child file descriptors. + * If the child closes a descriptor before it execs or exits, + * this munges the parent's descriptor as well. + * Test for this by closing stdout in the child. + */ + _exit(close(STDOUT_FILENO) != 0); + + } else { + int status; + struct stat st; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Was there some problem in waiting for the child? */ + || waitpid(child, &status, 0) != child + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(STDOUT_FILENO, &st) != 0 + ); + } +} +EOF +$PREPARE_CC || exit +if ($CL -DTRY_VFORK=1 a.c $L && $aout) >&2 +then has_vfork=1 ok=OK +else has_vfork=0 ok='absent or broken' +fi +echo >&3 $ok +echo "#define has_vfork $has_vfork /* Does vfork() work? */" +h=$has_vfork +case $h in +0) + $ech >&3 "$0: configuring has_fork $dots" + $PREPARE_CC || exit + ok='does not work' + ($CL a.c $L && $aout) >&2 && h=1 ok=OK + echo >&3 $ok +esac +echo "#define has_fork $h /* Does fork() work? */" +$PREPARE_CC || exit +$ech >&3 "$0: configuring has_waitpid $dots" +if ($CL -DTRY_VFORK=$has_vfork -DTRY_WAITPID=1 a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_spawn 0 /* Does spawn*() work? */" +echo "#define has_waitpid $h /* Does waitpid() work? */" + +$ech >&3 "$0: configuring bad_wait_if_SIGCHLD_ignored $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif +int main() { + signal(SIGCHLD, SIG_IGN); + { +# if has_fork + int status; + pid_t p = fork(); + if (p < 0) { + perror("fork"); + exitmain(2); + } + if (p == 0) + _exit(0); + while (wait(&status) != p) { + if (errno == ECHILD) + exitmain(1); + if (errno != EINTR) { + perror("wait"); + exitmain(2); + } + } +# else +# if has_system + if (system("true") != 0) + exitmain(1); +# endif +# endif + } + exitmain(0); +} +EOF +$PREPARE_CC || exit +b=0 ok=OK +if $CL a.c $L >&2 +then + $aout >&2 + case $? in + 0) ;; + 1) b=1 ok='will work around bug';; + *) exit + esac +fi +rm -f a.c || exit +echo >&3 $ok +echo "#define bad_wait_if_SIGCHLD_ignored $b /* Does ignoring SIGCHLD break wait()? */" + + +echo '#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */' + +$ech >&3 "$0: configuring has_printf_dot $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { printf("%.2d", 1); exitmain(ferror(stdout) || fclose(stdout)!=0); } +EOF +$PREPARE_CC && $CL a.c $L >&2 && r=`$aout` || exit +case $r in +01) h=1 ok=OK;; +*) h=0 ok='does not work' +esac +echo >&3 $ok +echo "#define has_printf_dot $h /* Does \"%.2d\" print leading 0? */" + +$ech >&3 "$0: configuring has_vfprintf, has_attribute_format_printf $dots" +cat >a.c <<EOF +#include "$A_H" +#if has_attribute_format_printf +# define printf_string(m, n) __attribute__((format(printf, m, n))) +#else +# define printf_string(m, n) +#endif +int p P((char const*,...)) printf_string(1, 2); +#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("hello") != 5); } +EOF +$PREPARE_CC || exit +h=0 p=0 +if ($CL a.c $L && sh -c 'pid=$$; (sleep 3; kill $pid)& exec '$aout) >&2 +then + h=1 + $PREPARE_CC || exit + $CS -Dhas_attribute_format_printf=1 a.c >&2 && $CS_OK && p=1 +else + status=$? + sh -c 'pid=$$; (sleep 3; kill $pid)& exec sleep 6' >&2 + if test $? = $status + then + echo >&3 "$0: stdio library loops forever. Giving up. +$0: (Perhaps you are using Solaris 2.x /usr/ucb/cc?) +$0: Please use a working stdio library instead." + exit 1 + fi +fi +echo >&3 $h, $p +cat <<EOF +#define has_vfprintf $h /* Does vfprintf() work? */ +#define has_attribute_format_printf $p /* Does __attribute__((format(printf,N,N+1))) work? */ +#if has_attribute_format_printf +# define printf_string(m, n) __attribute__((format(printf, m, n))) +#else +# define printf_string(m, n) +#endif +#if has_attribute_format_printf && has_attribute_noreturn + /* Work around a bug in GCC 2.5.x. */ +# define printf_string_exiting(m, n) __attribute__((format(printf, m, n), noreturn)) +#else +# define printf_string_exiting(m, n) printf_string(m, n) exiting +#endif +EOF + +case $h in +1) + h=? a='/* ' z='*/ ';; +*) + $ech >&3 "$0: configuring has__doprintf $dots" + a= z= + cat >a.c <<EOF +#include "$A_H" +#if has_prototypes +static int +p(char const*format,...) +#else +/*VARARGS1*/ static 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(0); } +EOF + $PREPARE_CC || exit + if ($CL -DTRY__DOPRINTF=1 a.c $L && $aout) >&2 + then h=1 ok=OK + else h=0 ok='does not work' + fi + echo >&3 $ok +esac +echo "$a#define has__doprintf $h $z/* Does _doprintf() work? */" +case $h in +0) + $ech >&3 "$0: configuring has__doprnt $dots" + $PREPARE_CC || exit + if ($CL a.c $L && $aout) >&2 + then h=1 ok=OK + else h=0 ok='does not work' + fi + echo >&3 $ok + a= z=;; +*) + h=? a='/* ' z='*/ ' +esac +echo "$a#define has__doprnt $h $z/* Does _doprnt() work? */" + +$ech >&3 "$0: configuring EXIT_FAILURE $dots" +cat >a.c <<EOF +#include "$A_H" +int main() { exitmain(EXIT_FAILURE); } +EOF +$PREPARE_CC || exit +if $CL a.c $L >&2 && $aout +then a= z= ok='will work around bug' +else a='/* ' z='*/ ' ok=OK +fi +echo >&3 $ok +echo "$a#undef EXIT_FAILURE $z/* Uncomment this if EXIT_FAILURE is broken. */" + +: configuring large_memory +case "$has_map_fd$has_mmap" in +*1*) l=1;; +*) l=0 +esac +echo "#define large_memory $l /* Can main memory hold entire RCS files? */" + +$ech >&3 "$0: configuring LONG_MAX $dots" +cat >a.c <<EOF +#include "$A_H" +static unsigned long ulong_max; +static long long_max; +int +main() { + ulong_max--; + long_max = ulong_max >> 1; + printf("#ifndef LONG_MAX\n"); + printf("#define LONG_MAX %ldL /* long maximum */\n", long_max); + printf("#endif\n"); + exitmain(ferror(stdout) || fclose(stdout)!=0); +} +EOF +$PREPARE_CC && $CL a.c $L >&2 && $aout || exit +echo >&3 OK + +: configuring same_file +echo "/* Do struct stat s and t describe the same file? Answer d if unknown. */" +echo "#define same_file(s,t,d) ((s).st_ino==(t).st_ino && (s).st_dev==(t).st_dev)" + +$ech >&3 "$0: configuring struct utimbuf $dots" +cat >a.c <<EOF +#include "$A_H" +static struct utimbuf s; +int main() { s.actime = s.modtime = 1; exitmain(utime("a.c", &s) != 0); } +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then h=1 ok=OK +else h=0 ok='does not work' +fi +echo >&3 $ok +echo "#define has_utimbuf $h /* Does struct utimbuf work? */" + +: configuring CO +echo "#define CO \"${RCSPREFIX}co\" /* name of 'co' program */" + +: configuring COMPAT2 +echo "#define COMPAT2 $COMPAT2 /* Are version 2 files supported? */" + +: configuring DIFF +echo "#define DIFF \"${DIFF}\" /* name of 'diff' program */" + +: configuring DIFF3 +echo "#define DIFF3 \"${DIFF3}\" /* name of 'diff3' program */" + +: configuring DIFF3_BIN +echo "#define DIFF3_BIN $DIFF3_BIN /* Is diff3 user-visible (not the /usr/lib auxiliary)? */" + +: configuring DIFFFLAGS +echo "#define DIFFFLAGS \"$DIFFFLAGS\" /* Make diff output suitable for RCS. */" + +: configuring DIFF_L +echo "#define DIFF_L $DIFF_L /* Does diff -L work? */" + +: configuring 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 + +: configuring ED +echo "#define ED \"${ED}\" /* name of 'ed' program (used only if !DIFF3_BIN) */" + +: configuring MERGE +echo "#define MERGE \"${RCSPREFIX}merge\" /* name of 'merge' program */" + +: configuring '*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 # \\\\ instead of \\ doesn't hurt, and avoids common bugs + SLASH='\' + qSLASH="'\\\\'" + SLASHes="$qSLASH: case '/': case ':'" + isSLASH='int isSLASH P((int));' + ROOTPATH="(isSLASH((p)[0]) || (p)[0] && (p)[1]==':')" + X_DEFAULT="$SLASH,v";; +*) + echo >&3 $0: cannot deduce SLASH + exit 1 +esac +cat <<EOF +#define TMPDIR "${SLASH}tmp" /* default directory for temporary files */ +#define SLASH $qSLASH /* principal filename separator */ +#define SLASHes $SLASHes /* \`case SLASHes:' labels all filename separators */ +$isSLASH /* Is arg a filename separator? */ +#define ROOTPATH(p) $ROOTPATH /* Is p an absolute pathname? */ +#define X_DEFAULT "$X_DEFAULT" /* default value for -x option */ +EOF + +$ech >&3 "$0: configuring SLASHSLASH_is_SLASH $dots" +cat >a.c <<EOF +#include "$A_H" +static struct stat s, ss; +static char f[3]; +int +main() { + f[0] = SLASH; if (stat(f, &s ) != 0) exitmain(1); + f[1] = SLASH; if (stat(f, &ss) != 0) exitmain(1); + exitmain(!same_file(s, ss, 0)); +} +EOF +$PREPARE_CC || exit +if ($CL a.c $L && $aout) >&2 +then eq=1 ok=OK +else eq=0 ok=no +fi +echo >&3 $ok +echo "#define SLASHSLASH_is_SLASH $eq /* Are // and / the same directory? */" + +$ech >&3 "$0: configuring ALL_ABSOLUTE, DIFF_ABSOLUTE $dots" +cat >a.c <<EOF +#include "$A_H" +#ifndef isSLASH +static int +isSLASH(c) int c; { + switch (c) { case SLASHes: return 1; } return 0; +} +#endif +int +main(argc, argv) int argc; char **argv; { + exitmain(1<argc && !ROOTPATH(argv[1])); +} +EOF +$PREPARE_CC && ($CL a.c $L && $aout) >&2 || exit +a=1 +for i in "$DIFF" "$DIFF3" "$ED" "$RCSPREFIX" "$SENDMAIL" +do + case $i in + \"*\") i=`expr "$i" : '"\(.*\)"'` + esac + case $i in + ?*) $aout "$i" || { a=0; break; } + esac +done +echo "#define ALL_ABSOLUTE $a /* Do all subprograms satisfy ROOTPATH? */" +if $aout "$DIFF" +then a=1 +else a=0 +fi +echo "#define DIFF_ABSOLUTE $a /* Is ROOTPATH(DIFF) true? */" +echo >&3 OK + +: configuring SENDMAIL +case $SENDMAIL in +'') a='/* ' z='*/ ';; +*) a= z= +esac +echo "$a#define SENDMAIL $SENDMAIL $z/* how to send mail */" + +: configuring TZ_must_be_set +echo "#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */" + + +$ech >&3 "$0: configuring standard library declarations $dots" + +cat <<'EOF' + + + +/* Adjust the following declarations as needed. */ +EOF + +cat >a.ha <<EOF + + +/* The rest is for the benefit of non-standard, traditional hosts. */ +/* Don't bother to declare functions that in traditional hosts do not appear, */ +/* or are declared in .h files, or return int or void. */ + + +/* traditional BSD */ + +#if has_sys_siglist && !defined(sys_siglist) + extern char const * const sys_siglist[]; +#endif + + +/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */ + +/* <fcntl.h> */ +#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 +#endif +#ifndef O_EXCL +#define O_EXCL 0 +#endif + +/* <sys/stat.h> */ +#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> */ +#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) & 0377) == 0) +#endif +#ifndef WTERMSIG +#define WTERMSIG(stat_val) ((stat_val) & 0177) +#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */ +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(stat_val) ((unsigned)(stat_val) - 1 < 0377) +#endif + +/* <unistd.h> */ +char *getlogin P((void)); +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +#endif +#if has_fork && !has_vfork +# undef vfork +# define vfork fork +#endif +#if has_getcwd || !has_getwd + char *getcwd P((char*,size_t)); +#else + char *getwd P((char*)); +#endif +#if has_setuid && !has_seteuid +# undef seteuid +# define seteuid setuid +#endif +#if has_spawn +# if ALL_ABSOLUTE +# define spawn_RCS spawnv +# else +# define spawn_RCS spawnvp +# endif +#else +# if ALL_ABSOLUTE +# define exec_RCS execv +# else +# define exec_RCS execvp +# endif +#endif + +/* utime.h */ +#if !has_utimbuf + struct utimbuf { time_t actime, modtime; }; +#endif + + +/* Standard C library */ + +/* <stdio.h> */ +#ifndef L_tmpnam +#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#if has_mktemp + char *mktemp P((char*)); /* traditional */ +#else + char *tmpnam P((char*)); +#endif + +/* <stdlib.h> */ +char *getenv P((char const*)); +void _exit P((int)) exiting; +void exit P((int)) exiting; +malloc_type malloc P((size_t)); +malloc_type realloc P((malloc_type,size_t)); +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#endif + +/* <string.h> */ +char *strcpy P((char*,char const*)); +char *strchr P((char const*,int)); +char *strrchr P((char const*,int)); +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 || + { $PREPARE_CC || exit; $CS -DH=$H a.c $LS >&2 && $CS_OK; } +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 + $PREPARE_CC || exit + if $CS -DH=$I a.c $LS >&2 && $CS_OK + then L=$M1 + else u=$M + fi + done + + # Comment out the bad line. + badline=`sed -n "$L{p;q;}" a.h$H` + echo >&3 "$n$0: commenting out \`$badline' $dots" + sed "$L$commentOut" a.h$H >a.h$I || exit + + H=$I + L=`expr $L + 1` +done + +cat a.h$H + +echo >&3 OK diff --git a/gnu/usr.bin/rcs/src/ident.c b/gnu/usr.bin/rcs/src/ident.c new file mode 100644 index 00000000000..bfa395a72c7 --- /dev/null +++ b/gnu/usr.bin/rcs/src/ident.c @@ -0,0 +1,274 @@ +/* Identify RCS keyword strings in files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: ident.c,v $ + * Revision 1.1 1996/08/12 04:08:07 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.9 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.8 1995/06/01 16:23:43 eggert + * (exiterr, reportError): New functions, needed for DOS and OS/2 ports. + * (scanfile): Use them. + * + * Revision 5.7 1994/03/20 04:52:58 eggert + * Remove `exiting' from identExit. + * + * Revision 5.6 1993/11/09 17:40:15 eggert + * Add -V. + * + * Revision 5.5 1993/11/03 17:42:27 eggert + * Test for char == EOF, not char < 0. + * + * Revision 5.4 1992/01/24 18:44:19 eggert + * lint -> RCS_lint + * + * 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 int scanfile P((FILE*,char const*,int)); +static void reportError P((char const*)); + +mainProg(identId, "ident", "$Id: ident.c,v 1.1 1996/08/12 04:08:07 millert Exp $") +/* Ident searches the named files for all occurrences + * of the pattern $@: text $ where @ is a keyword. + */ + +{ + FILE *fp; + int quiet = 0; + int status = EXIT_SUCCESS; + char const *a; + + while ((a = *++argv) && *a=='-') + while (*++a) + switch (*a) { + case 'q': + quiet = 1; + break; + + case 'V': + VOID printf("RCS version %s\n", RCS_version_string); + quiet = -1; + break; + + default: + VOID fprintf(stderr, + "ident: usage: ident -{qV} [file...]\n" + ); + exitmain(EXIT_FAILURE); + break; + } + + if (0 <= quiet) + if (!a) + VOID scanfile(stdin, (char*)0, quiet); + else + do { + if (!(fp = fopen(a, FOPEN_RB))) { + reportError(a); + status = EXIT_FAILURE; + } else if ( + scanfile(fp, a, quiet) != 0 + || (argv[1] && putchar('\n') == EOF) + ) + break; + } while ((a = *++argv)); + + if (ferror(stdout) || fclose(stdout)!=0) { + reportError("standard output"); + status = EXIT_FAILURE; + } + exitmain(status); +} + +#if RCS_lint +# define exiterr identExit +#endif + void +exiterr() +{ + _exit(EXIT_FAILURE); +} + + static void +reportError(s) + char const *s; +{ + int e = errno; + VOID fprintf(stderr, "%s error: ", cmdid); + errno = e; + perror(s); +} + + + static int +scanfile(file, name, quiet) + register FILE *file; + char const *name; + int quiet; +/* Function: scan an open file with descriptor file for keywords. + * Return -1 if there's a write error; exit immediately on a read error. + */ +{ + register int c; + + if (name) { + VOID printf("%s:\n", name); + if (ferror(stdout)) + return -1; + } else + name = "standard input"; + c = 0; + while (c != EOF || ! (feof(file)|ferror(file))) { + if (c == KDELIM) { + if ((c = match(file))) + continue; + if (ferror(stdout)) + return -1; + quiet = true; + } + c = getc(file); + } + if (ferror(file) || fclose(file) != 0) { + reportError(name); + /* + * The following is equivalent to exit(EXIT_FAILURE), but we invoke + * exiterr to keep lint happy. The DOS and OS/2 ports need exiterr. + */ + VOID fflush(stderr); + VOID fflush(stdout); + exiterr(); + } + if (!quiet) + VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name); + return 0; +} + + + + 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 == EOF && feof(fp) | ferror(fp)) + 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 == EOF && 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 printf(" %c%s\n", KDELIM, line); + return 0; +} diff --git a/gnu/usr.bin/rcs/src/maketime.c b/gnu/usr.bin/rcs/src/maketime.c new file mode 100644 index 00000000000..a9d63a4662f --- /dev/null +++ b/gnu/usr.bin/rcs/src/maketime.c @@ -0,0 +1,344 @@ +/* Convert struct partime into time_t. */ + +/* Copyright 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if has_conf_h +# include "conf.h" +#else +# ifdef __STDC__ +# define P(x) x +# else +# define const +# define P(x) () +# endif +# include <stdlib.h> +# include <time.h> +#endif + +#include "partime.h" +#include "maketime.h" + +char const maketId[] + = "$Id: maketime.c,v 1.1 1996/08/12 04:08:07 millert Exp $"; + +static int isleap P((int)); +static int month_days P((struct tm const*)); +static time_t maketime P((struct partime const*,time_t)); + +/* +* For maximum portability, use only localtime and gmtime. +* Make no assumptions about the time_t epoch or the range of time_t values. +* Avoid mktime because it's not universal and because there's no easy, +* portable way for mktime to yield the inverse of gmtime. +*/ + +#define TM_YEAR_ORIGIN 1900 + + static int +isleap(y) + int y; +{ + return (y&3) == 0 && (y%100 != 0 || y%400 == 0); +} + +static int const month_yday[] = { + /* days in year before start of months 0-12 */ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 +}; + +/* Yield the number of days in TM's month. */ + static int +month_days(tm) + struct tm const *tm; +{ + int m = tm->tm_mon; + return month_yday[m+1] - month_yday[m] + + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN)); +} + +/* +* Convert UNIXTIME to struct tm form. +* Use gmtime if available and if !LOCALZONE, localtime otherwise. +*/ + struct tm * +time2tm(unixtime, localzone) + time_t unixtime; + int localzone; +{ + struct tm *tm; +# if TZ_must_be_set + static char const *TZ; + if (!TZ && !(TZ = getenv("TZ"))) + faterror("The TZ environment variable is not set; please set it to your timezone"); +# endif + if (localzone || !(tm = gmtime(&unixtime))) + tm = localtime(&unixtime); + return tm; +} + +/* Yield A - B, measured in seconds. */ + time_t +difftm(a, b) + struct tm const *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int difference_in_day_of_year = a->tm_yday - b->tm_yday; + int intervening_leap_days = ( + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + ); + time_t difference_in_years = ay - by; + time_t difference_in_days = ( + difference_in_years*365 + + (intervening_leap_days + difference_in_day_of_year) + ); + return + ( + ( + 24*difference_in_days + + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +/* +* Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth. +* Adjust only T's year, mon, mday, hour, min and sec members; +* plus adjust wday if it is defined. +*/ + void +adjzone(t, seconds) + register struct tm *t; + long seconds; +{ + /* + * This code can be off by a second if SECONDS is not a multiple of 60, + * if T is local time, and if a leap second happens during this minute. + * But this bug has never occurred, and most likely will not ever occur. + * Liberia, the last country for which SECONDS % 60 was nonzero, + * switched to UTC in May 1972; the first leap second was in June 1972. + */ + int leap_second = t->tm_sec == 60; + long sec = seconds + (t->tm_sec - leap_second); + if (sec < 0) { + if ((t->tm_min -= (59-sec)/60) < 0) { + if ((t->tm_hour -= (59-t->tm_min)/60) < 0) { + t->tm_hour += 24; + if (TM_DEFINED(t->tm_wday) && --t->tm_wday < 0) + t->tm_wday = 6; + if (--t->tm_mday <= 0) { + if (--t->tm_mon < 0) { + --t->tm_year; + t->tm_mon = 11; + } + t->tm_mday = month_days(t); + } + } + t->tm_min += 24 * 60; + } + sec += 24L * 60 * 60; + } else + if (60 <= (t->tm_min += sec/60)) + if (24 <= (t->tm_hour += t->tm_min/60)) { + t->tm_hour -= 24; + if (TM_DEFINED(t->tm_wday) && ++t->tm_wday == 7) + t->tm_wday = 0; + if (month_days(t) < ++t->tm_mday) { + if (11 < ++t->tm_mon) { + ++t->tm_year; + t->tm_mon = 0; + } + t->tm_mday = 1; + } + } + t->tm_min %= 60; + t->tm_sec = (int) (sec%60) + leap_second; +} + +/* +* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise. +* Use only TM's year, mon, mday, hour, min, and sec members. +* Ignore TM's old tm_yday and tm_wday, but fill in their correct values. +* Yield -1 on failure (e.g. a member out of range). +* Posix 1003.1-1990 doesn't allow leap seconds, but some implementations +* have them anyway, so allow them if localtime/gmtime does. +*/ + time_t +tm2time(tm, localzone) + struct tm *tm; + int localzone; +{ + /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */ + static time_t t_cache[2]; + static struct tm tm_cache[2]; + + time_t d, gt; + struct tm const *gtm; + /* + * The maximum number of iterations should be enough to handle any + * combinations of leap seconds, time zone rule changes, and solar time. + * 4 is probably enough; we use a bigger number just to be safe. + */ + int remaining_tries = 8; + + /* Avoid subscript errors. */ + if (12 <= (unsigned)tm->tm_mon) + return -1; + + tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday + - (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN)); + + /* Make a first guess. */ + gt = t_cache[localzone]; + gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone); + + /* Repeatedly use the error from the guess to improve the guess. */ + while ((d = difftm(tm, gtm)) != 0) { + if (--remaining_tries == 0) + return -1; + gt += d; + gtm = time2tm(gt,localzone); + } + t_cache[localzone] = gt; + tm_cache[localzone] = *gtm; + + /* + * Check that the guess actually matches; + * overflow can cause difftm to yield 0 even on differing times, + * or tm may have members out of range (e.g. bad leap seconds). + */ + if ( (tm->tm_year ^ gtm->tm_year) + | (tm->tm_mon ^ gtm->tm_mon) + | (tm->tm_mday ^ gtm->tm_mday) + | (tm->tm_hour ^ gtm->tm_hour) + | (tm->tm_min ^ gtm->tm_min) + | (tm->tm_sec ^ gtm->tm_sec)) + return -1; + + tm->tm_wday = gtm->tm_wday; + return gt; +} + +/* +* Check *PT and convert it to time_t. +* If it is incompletely specified, use DEFAULT_TIME to fill it out. +* Use localtime if PT->zone is the special value TM_LOCAL_ZONE. +* Yield -1 on failure. +* ISO 8601 day-of-year and week numbers are not yet supported. +*/ + static time_t +maketime(pt, default_time) + struct partime const *pt; + time_t default_time; +{ + int localzone, wday; + struct tm tm; + struct tm *tm0 = 0; + time_t r; + + tm0 = 0; /* Keep gcc -Wall happy. */ + localzone = pt->zone==TM_LOCAL_ZONE; + + tm = pt->tm; + + if (TM_DEFINED(pt->ymodulus) || !TM_DEFINED(tm.tm_year)) { + /* Get tm corresponding to current time. */ + tm0 = time2tm(default_time, localzone); + if (!localzone) + adjzone(tm0, pt->zone); + } + + if (TM_DEFINED(pt->ymodulus)) + tm.tm_year += + (tm0->tm_year + TM_YEAR_ORIGIN)/pt->ymodulus * pt->ymodulus; + else if (!TM_DEFINED(tm.tm_year)) { + /* Set default year, month, day from current time. */ + tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN; + if (!TM_DEFINED(tm.tm_mon)) { + tm.tm_mon = tm0->tm_mon; + if (!TM_DEFINED(tm.tm_mday)) + tm.tm_mday = tm0->tm_mday; + } + } + + /* Convert from partime year (Gregorian) to Posix year. */ + tm.tm_year -= TM_YEAR_ORIGIN; + + /* Set remaining default fields to be their minimum values. */ + if (!TM_DEFINED(tm.tm_mon)) tm.tm_mon = 0; + if (!TM_DEFINED(tm.tm_mday)) tm.tm_mday = 1; + if (!TM_DEFINED(tm.tm_hour)) tm.tm_hour = 0; + if (!TM_DEFINED(tm.tm_min)) tm.tm_min = 0; + if (!TM_DEFINED(tm.tm_sec)) tm.tm_sec = 0; + + if (!localzone) + adjzone(&tm, -pt->zone); + wday = tm.tm_wday; + + /* Convert and fill in the rest of the tm. */ + r = tm2time(&tm, localzone); + + /* Check weekday. */ + if (r != -1 && TM_DEFINED(wday) && wday != tm.tm_wday) + return -1; + + return r; +} + +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ + time_t +str2time(source, default_time, default_zone) + char const *source; + time_t default_time; + long default_zone; +{ + struct partime pt; + + if (*partime(source, &pt)) + return -1; + if (pt.zone == TM_UNDEFINED_ZONE) + pt.zone = default_zone; + return maketime(&pt, default_time); +} + +#if TEST +#include <stdio.h> + int +main(argc, argv) int argc; char **argv; +{ + time_t default_time = time((time_t *)0); + long default_zone = argv[1] ? atol(argv[1]) : 0; + char buf[1000]; + while (fgets(buf, sizeof buf, stdin)) { + time_t t = str2time(buf, default_time, default_zone); + printf("%s", asctime(gmtime(&t))); + } + return 0; +} +#endif diff --git a/gnu/usr.bin/rcs/src/maketime.h b/gnu/usr.bin/rcs/src/maketime.h new file mode 100644 index 00000000000..fbe12562051 --- /dev/null +++ b/gnu/usr.bin/rcs/src/maketime.h @@ -0,0 +1,39 @@ +/* Yield time_t from struct partime yielded by partime. */ + +/* Copyright 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if defined(__STDC__) || has_prototypes +# define __MAKETIME_P(x) x +#else +# define __MAKETIME_P(x) () +#endif + +struct tm *time2tm __MAKETIME_P((time_t,int)); +time_t difftm __MAKETIME_P((struct tm const *, struct tm const *)); +time_t str2time __MAKETIME_P((char const *, time_t, long)); +time_t tm2time __MAKETIME_P((struct tm *, int)); +void adjzone __MAKETIME_P((struct tm *, long)); diff --git a/gnu/usr.bin/rcs/src/merge.c b/gnu/usr.bin/rcs/src/merge.c new file mode 100644 index 00000000000..d9e147539b3 --- /dev/null +++ b/gnu/usr.bin/rcs/src/merge.c @@ -0,0 +1,113 @@ +/* merge - three-way file merge */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +static void badoption P((char const*)); + +static char const usage[] = + "\nmerge: usage: merge [-AeEpqxX3] [-L lab [-L lab [-L lab]]] file1 file2 file3"; + + static void +badoption(a) + char const *a; +{ + error("unknown option: %s%s", a, usage); +} + + +mainProg(mergeId, "merge", "$Id: merge.c,v 1.1 1996/08/12 04:08:09 millert Exp $") +{ + register char const *a; + char const *arg[3], *label[3], *edarg = 0; + int labels, tostdout; + + labels = 0; + tostdout = false; + + for (; (a = *++argv) && *a++ == '-'; --argc) { + switch (*a++) { + case 'A': case 'E': case 'e': + if (edarg && edarg[1] != (*argv)[1]) + error("%s and %s are incompatible", + edarg, *argv + ); + edarg = *argv; + break; + + case 'p': tostdout = true; break; + case 'q': quietflag = true; break; + + case 'L': + if (3 <= labels) + faterror("too many -L options"); + if (!(label[labels++] = *++argv)) + faterror("-L needs following argument"); + --argc; + break; + + case 'V': + printf("RCS version %s\n", RCS_version_string); + exitmain(0); + + default: + badoption(a - 2); + continue; + } + if (*a) + badoption(a - 2); + } + + 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]; + + for (; labels < 3; labels++) + label[labels] = arg[labels]; + + if (nerror) + exiterr(); + exitmain(merge(tostdout, edarg, label, arg)); +} + + +#if RCS_lint +# define exiterr mergeExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} diff --git a/gnu/usr.bin/rcs/src/merger.c b/gnu/usr.bin/rcs/src/merger.c new file mode 100644 index 00000000000..8a68ab8978d --- /dev/null +++ b/gnu/usr.bin/rcs/src/merger.c @@ -0,0 +1,148 @@ +/* three-way file merge internals */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mergerId, "$Id: merger.c,v 1.1 1996/08/12 04:08:10 millert Exp $") + + static char const *normalize_arg P((char const*,char**)); + 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; + if (*s == '-') { + *b = t = testalloc(strlen(s) + 3); + VOID sprintf(t, ".%c%s", SLASH, s); + return t; + } else { + *b = 0; + return s; + } +} + + int +merge(tostdout, edarg, label, argv) + int tostdout; + char const *edarg; + char const *const label[3]; + char const *const argv[3]; +/* + * Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2', + * where TOSTDOUT specifies whether -p is present, + * EDARG gives the editing type (e.g. "-A", or null for the default), + * LABEL gives l0, l1 and l2, 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 (!edarg) + edarg = "-E"; + +#if DIFF3_BIN + t = 0; + if (!tostdout) + t = maketemp(0); + s = run( + -1, t, + DIFF3, edarg, "-am", + "-L", label[0], + "-L", label[1], + "-L", label[2], + a[0], a[1], a[2], (char*)0 + ); + switch (s) { + case DIFF_SUCCESS: + break; + case DIFF_FAILURE: + warn("conflicts during merge"); + break; + default: + exiterr(); + } + if (t) { + if (!(f = fopenSafer(argv[0], "w"))) + efaterror(argv[0]); + if (!(rt = Iopen(t, "r", (struct stat*)0))) + efaterror(t); + fastcopy(rt, f); + Ifclose(rt); + Ofclose(f); + } +#else + for (i=0; i<2; i++) + switch (run( + -1, d[i]=maketemp(i), + DIFF, a[i], a[2], (char*)0 + )) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: faterror("diff failed"); + } + t = maketemp(2); + s = run( + -1, t, + DIFF3, edarg, d[0], d[1], a[0], a[1], a[2], + label[0], label[2], (char*)0 + ); + if (s != DIFF_SUCCESS) { + s = DIFF_FAILURE; + warn("overlaps or other problems during merge"); + } + if (!(f = fopenSafer(t, "a+"))) + efaterror(t); + aputs(tostdout ? "1,$p\n" : "w\n", f); + Orewind(f); + aflush(f); + if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0)) + exiterr(); + Ofclose(f); +#endif + + tempunlink(); + for (i=3; 0<=--i; ) + if (b[i]) + tfree(b[i]); + return s; +} diff --git a/gnu/usr.bin/rcs/src/partime.c b/gnu/usr.bin/rcs/src/partime.c new file mode 100644 index 00000000000..24ee1f91938 --- /dev/null +++ b/gnu/usr.bin/rcs/src/partime.c @@ -0,0 +1,701 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#if has_conf_h +# include "conf.h" +#else +# ifdef __STDC__ +# define P(x) x +# else +# define const +# define P(x) () +# endif +# include <limits.h> +# include <time.h> +#endif + +#include <ctype.h> +#undef isdigit +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */ + +#include "partime.h" + +char const partimeId[] + = "$Id: partime.c,v 1.1 1996/08/12 04:08:10 millert Exp $"; + + +/* Lookup tables for names of months, weekdays, time zones. */ + +#define NAME_LENGTH_MAXIMUM 4 + +struct name_val { + char name[NAME_LENGTH_MAXIMUM]; + int val; +}; + + +static char const *parse_decimal P((char const*,int,int,int,int,int*,int*)); +static char const *parse_fixed P((char const*,int,int*)); +static char const *parse_pattern_letter P((char const*,int,struct partime*)); +static char const *parse_prefix P((char const*,struct partime*,int*)); +static char const *parse_ranged P((char const*,int,int,int,int*)); +static int lookup P((char const*,struct name_val const[])); +static int merge_partime P((struct partime*, struct partime const*)); +static void undefine P((struct partime*)); + + +static struct name_val const month_names[] = { + {"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5}, + {"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11}, + {"", TM_UNDEFINED} +}; + +static struct name_val const weekday_names[] = { + {"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6}, + {"", TM_UNDEFINED} +}; + +#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) +#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) +#define zs(t,s) {s, hr60(t)} +#define zd(t,s,d) zs(t, s), zs((t)+100, d) + +static struct name_val const zone_names[] = { + zs(-1000, "hst"), /* Hawaii */ + zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */ + zd(- 900,"akst","akdt"),/* Alaska */ + zd(- 800, "pst", "pdt"),/* Pacific */ + zd(- 700, "mst", "mdt"),/* Mountain */ + zd(- 600, "cst", "cdt"),/* Central */ + zd(- 500, "est", "edt"),/* Eastern */ + zd(- 400, "ast", "adt"),/* Atlantic */ + zd(- 330, "nst", "ndt"),/* Newfoundland */ + zs( 000, "utc"), /* Coordinated Universal */ + zs( 000, "cut"), /* " */ + zs( 000, "ut"), /* Universal */ + zs( 000, "z"), /* Zulu (required by ISO 8601) */ + zd( 000, "gmt", "bst"),/* Greenwich Mean, British Summer */ + zs( 000, "wet"), /* Western Europe */ + zs( 100, "met"), /* Middle Europe */ + zs( 100, "cet"), /* Central Europe */ + zs( 200, "eet"), /* Eastern Europe */ + zs( 530, "ist"), /* India */ + zd( 900, "jst", "jdt"),/* Japan */ + zd( 900, "kst", "kdt"),/* Korea */ + zd( 1200,"nzst","nzdt"),/* New Zealand */ + { "lt", 1 }, +#if 0 + /* The following names are duplicates or are not well attested. */ + zs(-1100, "sst"), /* Samoa */ + zs(-1000, "tht"), /* Tahiti */ + zs(- 930, "mqt"), /* Marquesas */ + zs(- 900, "gbt"), /* Gambier */ + zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */ + zs(- 830, "pit"), /* Pitcairn */ + zd(- 500, "cst", "cdt"),/* Cuba */ + zd(- 500, "ast", "adt"),/* Acre */ + zd(- 400, "wst", "wdt"),/* Western Brazil */ + zd(- 400, "ast", "adt"),/* Andes */ + zd(- 400, "cst", "cdt"),/* Chile */ + zs(- 300, "wgt"), /* Western Greenland */ + zd(- 300, "est", "edt"),/* Eastern South America */ + zs(- 300, "mgt"), /* Middle Greenland */ + zd(- 200, "fst", "fdt"),/* Fernando de Noronha */ + zs(- 100, "egt"), /* Eastern Greenland */ + zs(- 100, "aat"), /* Atlantic Africa */ + zs(- 100, "act"), /* Azores and Canaries */ + zs( 000, "wat"), /* West Africa */ + zs( 100, "cat"), /* Central Africa */ + zd( 100, "mez","mesz"),/* Mittel-Europaeische Zeit */ + zs( 200, "sat"), /* South Africa */ + zd( 200, "ist", "idt"),/* Israel */ + zs( 300, "eat"), /* East Africa */ + zd( 300, "ast", "adt"),/* Arabia */ + zd( 300, "msk", "msd"),/* Moscow */ + zd( 330, "ist", "idt"),/* Iran */ + zs( 400, "gst"), /* Gulf */ + zs( 400, "smt"), /* Seychelles & Mascarene */ + zd( 400, "esk", "esd"),/* Yekaterinburg */ + zd( 400, "bsk", "bsd"),/* Baku */ + zs( 430, "aft"), /* Afghanistan */ + zd( 500, "osk", "osd"),/* Omsk */ + zs( 500, "pkt"), /* Pakistan */ + zd( 500, "tsk", "tsd"),/* Tashkent */ + zs( 545, "npt"), /* Nepal */ + zs( 600, "bgt"), /* Bangladesh */ + zd( 600, "nsk", "nsd"),/* Novosibirsk */ + zs( 630, "bmt"), /* Burma */ + zs( 630, "cct"), /* Cocos */ + zs( 700, "ict"), /* Indochina */ + zs( 700, "jvt"), /* Java */ + zd( 700, "isk", "isd"),/* Irkutsk */ + zs( 800, "hkt"), /* Hong Kong */ + zs( 800, "pst"), /* Philippines */ + zs( 800, "sgt"), /* Singapore */ + zd( 800, "cst", "cdt"),/* China */ + zd( 800, "ust", "udt"),/* Ulan Bator */ + zd( 800, "wst", "wst"),/* Western Australia */ + zd( 800, "ysk", "ysd"),/* Yakutsk */ + zs( 900, "blt"), /* Belau */ + zs( 900, "mlt"), /* Moluccas */ + zd( 900, "vsk", "vsd"),/* Vladivostok */ + zd( 930, "cst", "cst"),/* Central Australia */ + zs( 1000, "gst"), /* Guam */ + zd( 1000, "gsk", "gsd"),/* Magadan */ + zd( 1000, "est", "est"),/* Eastern Australia */ + zd( 1100,"lhst","lhst"),/* Lord Howe */ + zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */ + zs( 1100,"ncst"), /* New Caledonia */ + zs( 1130,"nrft"), /* Norfolk */ + zd( 1200, "ask", "asd"),/* Anadyr */ + zs( 1245,"nz-chat"), /* Chatham */ + zs( 1300, "tgt"), /* Tongatapu */ +#endif + {"", -1} +}; + + static int +lookup (s, table) + char const *s; + struct name_val const table[]; +/* Look for a prefix of S in TABLE, returning val for first matching entry. */ +{ + int j; + char buf[NAME_LENGTH_MAXIMUM]; + + for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { + unsigned char c = *s++; + buf[j] = isupper (c) ? tolower (c) : c; + if (!isalpha (c)) + break; + } + for (; table[0].name[0]; table++) + for (j = 0; buf[j] == table[0].name[j]; ) + if (++j == NAME_LENGTH_MAXIMUM || !table[0].name[j]) + goto done; + done: + return table[0].val; +} + + + static void +undefine (t) struct partime *t; +/* Set *T to ``undefined'' values. */ +{ + t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon + = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday + = t->ymodulus = t->yweek + = TM_UNDEFINED; + t->zone = TM_UNDEFINED_ZONE; +} + +/* +* Array of patterns to look for in a date string. +* Order is important: we look for the first matching pattern +* whose values do not contradict values that we already know about. +* See `parse_pattern_letter' below for the meaning of the pattern codes. +*/ +static char const * const patterns[] = { + /* + * These traditional patterns must come first, + * to prevent an ISO 8601 format from misinterpreting their prefixes. + */ + "E_n_y", "x", /* RFC 822 */ + "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ + "y/N/D$", /* traditional RCS */ + + /* ISO 8601:1988 formats, generalized a bit. */ + "y-N-D$", "4ND$", "Y-N$", + "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", + "--N$", "---D$", "DT", + "Y-d$", "4d$", "R=d$", "-d$", "dT", + "y-W-X", "yWX", "y=W", + "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", + "-w-X", "w-XT", "---X$", "XT", "4$", + "T", + "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", + "Y", "Z", + + 0 +}; + + static char const * +parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi; +/* +* Parse an initial prefix of STR, setting *T accordingly. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +* Start with pattern *PI; if success, set *PI to the next pattern to try. +* Set *PI to -1 if we know there are no more patterns to try; +* if *PI is initially negative, give up immediately. +*/ +{ + int i = *pi; + char const *pat; + unsigned char c; + + if (i < 0) + return 0; + + /* Remove initial noise. */ + while (!isalnum (c = *str) && c != '-' && c != '+') { + if (!c) { + undefine (t); + *pi = -1; + return str; + } + str++; + } + + /* Try a pattern until one succeeds. */ + while ((pat = patterns[i++]) != 0) { + char const *s = str; + undefine (t); + do { + if (!(c = *pat++)) { + *pi = i; + return s; + } + } while ((s = parse_pattern_letter (s, c, t)) != 0); + } + + return 0; +} + + static char const * +parse_fixed (s, digits, res) char const *s; int digits, *res; +/* +* Parse an initial prefix of S of length DIGITS; it must be a number. +* Store the parsed number into *RES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + int n = 0; + char const *lim = s + digits; + while (s < lim) { + unsigned d = *s++ - '0'; + if (9 < d) + return 0; + n = 10*n + d; + } + *res = n; + return s; +} + + static char const * +parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res; +/* +* Parse an initial prefix of S of length DIGITS; +* it must be a number in the range LO through HI. +* Store the parsed number into *RES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + s = parse_fixed (s, digits, res); + return s && lo<=*res && *res<=hi ? s : 0; +} + + static char const * +parse_decimal (s, digits, lo, hi, resolution, res, fres) + char const *s; + int digits, lo, hi, resolution, *res, *fres; +/* +* Parse an initial prefix of S of length DIGITS; +* it must be a number in the range LO through HI +* and it may be followed by a fraction that is to be computed using RESOLUTION. +* Store the parsed number into *RES; store the fraction times RESOLUTION, +* rounded to the nearest integer, into *FRES. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + s = parse_fixed (s, digits, res); + if (s && lo<=*res && *res<=hi) { + int f = 0; + if ((s[0]==',' || s[0]=='.') && isdigit ((unsigned char) s[1])) { + char const *s1 = ++s; + int num10 = 0, denom10 = 10, product; + while (isdigit ((unsigned char) *++s)) + denom10 *= 10; + s = parse_fixed (s1, s - s1, &num10); + product = num10*resolution; + f = (product + (denom10>>1)) / denom10; + f -= f & (product%denom10 == denom10>>1); /* round to even */ + if (f < 0 || product/resolution != num10) + return 0; /* overflow */ + } + *fres = f; + return s; + } + return 0; +} + + char * +parzone (s, zone) char const *s; long *zone; +/* +* Parse an initial prefix of S; it must denote a time zone. +* Set *ZONE to the number of seconds east of GMT, +* or to TM_LOCAL_ZONE if it is the local time zone. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + char sign; + int hh, mm, ss; + int minutesEastOfUTC; + long offset, z; + + /* + * The formats are LT, n, n DST, nDST, no, o + * where n is a time zone name + * and o is a time zone offset of the form [-+]hh[:mm[:ss]]. + */ + switch (*s) { + case '-': case '+': + z = 0; + break; + + default: + minutesEastOfUTC = lookup (s, zone_names); + if (minutesEastOfUTC == -1) + return 0; + + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + + /* Don't modify LT. */ + if (minutesEastOfUTC == 1) { + *zone = TM_LOCAL_ZONE; + return (char *) s; + } + + z = minutesEastOfUTC * 60L; + + /* Look for trailing " DST". */ + if ( + (s[-1]=='T' || s[-1]=='t') && + (s[-2]=='S' || s[-2]=='s') && + (s[-3]=='D' || s[-3]=='t') + ) + goto trailing_dst; + while (isspace ((unsigned char) *s)) + s++; + if ( + (s[0]=='D' || s[0]=='d') && + (s[1]=='S' || s[1]=='s') && + (s[2]=='T' || s[2]=='t') + ) { + s += 3; + trailing_dst: + *zone = z + 60*60; + return (char *) s; + } + + switch (*s) { + case '-': case '+': break; + default: return (char *) s; + } + } + sign = *s++; + + if (!(s = parse_ranged (s, 2, 0, 23, &hh))) + return 0; + mm = ss = 0; + if (*s == ':') + s++; + if (isdigit ((unsigned char) *s)) { + if (!(s = parse_ranged (s, 2, 0, 59, &mm))) + return 0; + if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) { + if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss))) + return 0; + } + } + if (isdigit ((unsigned char) *s)) + return 0; + offset = (hh*60 + mm)*60L + ss; + *zone = z + (sign=='-' ? -offset : offset); + /* + * ?? Are fractions allowed here? + * If so, they're not implemented. + */ + return (char *) s; +} + + static char const * +parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t; +/* +* Parse an initial prefix of S, matching the pattern whose code is C. +* Set *T accordingly. +* Return the first character after the prefix, or 0 if it couldn't be parsed. +*/ +{ + switch (c) { + case '$': /* The next character must be a non-digit. */ + if (isdigit ((unsigned char) *s)) + return 0; + break; + + case '-': case '/': case ':': + /* These characters stand for themselves. */ + if (*s++ != c) + return 0; + break; + + case '4': /* 4-digit year */ + s = parse_fixed (s, 4, &t->tm.tm_year); + break; + + case '=': /* optional '-' */ + s += *s == '-'; + break; + + case 'A': /* AM or PM */ + /* + * This matches the regular expression [AaPp][Mm]?. + * It must not be followed by a letter or digit; + * otherwise it would match prefixes of strings like "PST". + */ + switch (*s++) { + case 'A': case 'a': + if (t->tm.tm_hour == 12) + t->tm.tm_hour = 0; + break; + + case 'P': case 'p': + if (t->tm.tm_hour != 12) + t->tm.tm_hour += 12; + break; + + default: return 0; + } + switch (*s) { + case 'M': case 'm': s++; break; + } + if (isalnum (*s)) + return 0; + break; + + case 'D': /* day of month [01-31] */ + s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); + break; + + case 'd': /* day of year [001-366] */ + s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); + t->tm.tm_yday--; + break; + + case 'E': /* extended day of month [1-9, 01-31] */ + s = parse_ranged (s, ( + isdigit ((unsigned char) s[0]) && + isdigit ((unsigned char) s[1]) + ) + 1, 1, 31, &t->tm.tm_mday); + break; + + case 'h': /* hour [00-23 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac); + t->tm.tm_min = frac / 60; + t->tm.tm_sec = frac % 60; + } + break; + + case 'm': /* minute [00-59 followed by optional fraction] */ + s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); + break; + + case 'n': /* month name [e.g. "Jan"] */ + if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + break; + + case 'N': /* month [01-12] */ + s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); + t->tm.tm_mon--; + break; + + case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ + s = parse_fixed (s, 1, &t->tm.tm_year); + t->ymodulus = 10; + break; + + case_R: + case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ + s = parse_fixed (s, 2, &t->tm.tm_year); + t->ymodulus = 100; + break; + + case 's': /* second [00-60 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); + t->tm.tm_sec += frac; + } + break; + + case 'T': /* 'T' or 't' */ + switch (*s++) { + case 'T': case 't': break; + default: return 0; + } + break; + + case 't': /* traditional hour [1-9 or 01-12] */ + s = parse_ranged (s, ( + isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1]) + ) + 1, 1, 12, &t->tm.tm_hour); + break; + + case 'w': /* 'W' or 'w' only (stands for current week) */ + switch (*s++) { + case 'W': case 'w': break; + default: return 0; + } + break; + + case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ + switch (*s++) { + case 'W': case 'w': break; + default: return 0; + } + s = parse_ranged (s, 2, 0, 53, &t->yweek); + break; + + case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ + s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); + t->tm.tm_wday--; + break; + + case 'x': /* weekday name [e.g. "Sun"] */ + if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (isalpha ((unsigned char) *s)) + s++; + break; + + case 'y': /* either R or Y */ + if ( + isdigit ((unsigned char) s[0]) && + isdigit ((unsigned char) s[1]) && + !isdigit ((unsigned char) s[2]) + ) + goto case_R; + /* fall into */ + case 'Y': /* year in full [4 or more digits] */ + { + int len = 0; + while (isdigit ((unsigned char) s[len])) + len++; + if (len < 4) + return 0; + s = parse_fixed (s, len, &t->tm.tm_year); + } + break; + + case 'Z': /* time zone */ + s = parzone (s, &t->zone); + break; + + case '_': /* possibly empty sequence of non-alphanumerics */ + while (!isalnum (*s) && *s) + s++; + break; + + default: /* bad pattern */ + return 0; + } + return s; +} + + static int +merge_partime (t, u) struct partime *t; struct partime const *u; +/* +* If there is no conflict, merge into *T the additional information in *U +* and return 0. Otherwise do nothing and return -1. +*/ +{ +# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) + if ( + conflict (t->tm.tm_sec, u->tm.tm_sec) || + conflict (t->tm.tm_min, u->tm.tm_min) || + conflict (t->tm.tm_hour, u->tm.tm_hour) || + conflict (t->tm.tm_mday, u->tm.tm_mday) || + conflict (t->tm.tm_mon, u->tm.tm_mon) || + conflict (t->tm.tm_year, u->tm.tm_year) || + conflict (t->tm.tm_wday, u->tm.tm_yday) || + conflict (t->ymodulus, u->ymodulus) || + conflict (t->yweek, u->yweek) || + ( + t->zone != u->zone && + t->zone != TM_UNDEFINED_ZONE && + u->zone != TM_UNDEFINED_ZONE + ) + ) + return -1; +# undef conflict +# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); + merge_ (t->tm.tm_sec, u->tm.tm_sec) + merge_ (t->tm.tm_min, u->tm.tm_min) + merge_ (t->tm.tm_hour, u->tm.tm_hour) + merge_ (t->tm.tm_mday, u->tm.tm_mday) + merge_ (t->tm.tm_mon, u->tm.tm_mon) + merge_ (t->tm.tm_year, u->tm.tm_year) + merge_ (t->tm.tm_wday, u->tm.tm_yday) + merge_ (t->ymodulus, u->ymodulus) + merge_ (t->yweek, u->yweek) +# undef merge_ + if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone; + return 0; +} + + char * +partime (s, t) char const *s; struct partime *t; +/* +* Parse a date/time prefix of S, putting the parsed result into *T. +* Return the first character after the prefix. +* The prefix may contain no useful information; +* in that case, *T will contain only undefined values. +*/ +{ + struct partime p; + + undefine (t); + while (*s) { + int i = 0; + char const *s1; + do { + if (!(s1 = parse_prefix (s, &p, &i))) + return (char *) s; + } while (merge_partime (t, &p) != 0); + s = s1; + } + return (char *) s; +} diff --git a/gnu/usr.bin/rcs/src/partime.h b/gnu/usr.bin/rcs/src/partime.h new file mode 100644 index 00000000000..5d3983fbb04 --- /dev/null +++ b/gnu/usr.bin/rcs/src/partime.h @@ -0,0 +1,71 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#define TM_UNDEFINED (-1) +#define TM_DEFINED(x) (0 <= (x)) + +#define TM_UNDEFINED_ZONE ((long) -24 * 60 * 60) +#define TM_LOCAL_ZONE (TM_UNDEFINED_ZONE - 1) + +struct partime { + /* + * This structure describes the parsed time. + * Only the following tm_* values in it are used: + * sec, min, hour, mday, mon, year, wday, yday. + * If TM_UNDEFINED(value), the parser never found the value. + * The tm_year field is the actual year, not the year - 1900; + * but see ymodulus below. + */ + struct tm tm; + + /* + * If !TM_UNDEFINED(ymodulus), + * then tm.tm_year is actually modulo ymodulus. + */ + int ymodulus; + + /* + * Week of year, ISO 8601 style. + * If TM_UNDEFINED(yweek), the parser never found yweek. + * Weeks start on Mondays. + * Week 1 includes Jan 4. + */ + int yweek; + + /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */ + long zone; +}; + +#if defined(__STDC__) || has_prototypes +# define __PARTIME_P(x) x +#else +# define __PARTIME_P(x) () +#endif + +char *partime __PARTIME_P((char const *, struct partime *)); +char *parzone __PARTIME_P((char const *, long *)); diff --git a/gnu/usr.bin/rcs/src/rcs.c b/gnu/usr.bin/rcs/src/rcs.c new file mode 100644 index 00000000000..f4625b12183 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcs.c @@ -0,0 +1,1633 @@ +/* Change RCS file attributes. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcs.c,v $ + * Revision 1.1 1996/08/12 04:08:12 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.21 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.20 1995/06/01 16:23:43 eggert + * (main): Warn if no options were given. Punctuate messages properly. + * + * (sendmail): Rewind mailmess before flushing it. + * Output another warning if mail should work but fails. + * + * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference. + * + * Revision 5.19 1994/03/17 14:05:48 eggert + * Use ORCSerror to clean up after a fatal error. Remove lint. + * Specify subprocess input via file descriptor, not file name. Remove lint. + * Flush stderr after prompt. + * + * Revision 5.18 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.17 1993/11/03 17:42:27 eggert + * Add -z. Don't lose track of -m or -t when there are no other changes. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.16 1992/07/28 16:12:44 eggert + * rcs -l now asks whether you want to break the lock. + * Add -V. Set RCS file's mode and time at right moment. + * + * Revision 5.15 1992/02/17 23:02:20 eggert + * Add -T. + * + * Revision 5.14 1992/01/27 16:42:53 eggert + * Add -M. Avoid invoking umask(); it's one less thing to configure. + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.13 1992/01/06 02:42:34 eggert + * Avoid changing RCS file in common cases where no change can occur. + * + * 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 branchpoint P((struct hshentry*,struct hshentry*)); +static int breaklock P((struct hshentry const*)); +static int buildeltatext P((struct hshentries const*)); +static int doaccess P((void)); +static int doassoc P((void)); +static int dolocks P((void)); +static int domessages P((void)); +static int rcs_setstate P((char const*,char const*)); +static int removerevs P((void)); +static int sendmail P((char const*,char const*)); +static int setlock P((char const*)); +static struct Lockrev **rmnewlocklst P((char const*)); +static struct hshentry *searchcutpt P((char const*,int,struct hshentries*)); +static void buildtree P((void)); +static void cleanup 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 scanlogtext P((struct hshentry*,int)); + +static struct buf numrev; +static char const *headstate; +static int chgheadstate, exitstatus, lockhead, unlockcaller; +static int suppress_mail; +static struct Lockrev *newlocklst, *rmvlocklst; +static struct Message *messagelst, **nextmessage; +static struct Status *statelst, **nextstate; +static struct Symrev *assoclst, **nextassoc; +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 1.1 1996/08/12 04:08:12 millert Exp $") +{ + static char const cmdusage[] = + "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iILqTU} -ksubst -mrev:msg -{nN}name[:[rev]] -orange -sstate[:rev] -t[text] -Vn -xsuff -zzone file ..."; + + char *a, **newargv, *textfile; + char const *branchsym, *commsyml; + int branchflag, changed, expmode, initflag; + int strictlock, strict_selected, textflag; + int keepRCStime, Ttimeflag; + size_t commsymlen; + struct buf branchnum; + struct Lockrev *lockpt; + struct Lockrev **curlock, **rmvlock; + struct Status * curstate; + + nosetid(); + + nextassoc = &assoclst; + nextchaccess = &chaccess; + nextmessage = &messagelst; + nextstate = &statelst; + branchsym = commsyml = textfile = 0; + branchflag = strictlock = false; + bufautobegin(&branchnum); + commsymlen = 0; + curlock = &newlocklst; + rmvlock = &rmvlocklst; + expmode = -1; + suffixes = X_DEFAULT; + initflag= textflag = false; + strict_selected = 0; + Ttimeflag = false; + + /* preprocessing command options */ + if (1 < argc && argv[1][0] != '-') + warn("No options were given; this usage is obsolescent."); + + 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; + commsymlen = strlen(a); + break; + + case 'a': /* add new accessor */ + getaccessor(*argv+1, append); + break; + + case 'A': /* append access list according to accessfile */ + if (!*a) { + error("missing pathname after -A"); + break; + } + *argv = a; + if (0 < pairnames(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; + } + *curlock = lockpt = talloc(struct Lockrev); + lockpt->revno = a; + lockpt->nextrev = 0; + curlock = &lockpt->nextrev; + break; + + case 'u': /* release lock of a locked revision */ + if (!*a) { + unlockcaller=true; + break; + } + *rmvlock = lockpt = talloc(struct Lockrev); + lockpt->revno = a; + lockpt->nextrev = 0; + rmvlock = &lockpt->nextrev; + curlock = rmnewlocklst(lockpt->revno); + break; + + case 'L': /* set strict locking */ + if (strict_selected) { + if (!strictlock) /* Already selected -U? */ + warn("-U overridden by -L"); + } + strictlock = true; + strict_selected = true; + break; + + case 'U': /* release strict locking */ + if (strict_selected) { + if (strictlock) /* Already selected -L? */ + warn("-L overridden by -U"); + } + strict_selected = true; + 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 'M': /* do not send mail */ + suppress_mail = true; + 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 'T': /* do not update last-mod time for minor changes */ + if (*a) + goto unknown; + Ttimeflag = true; + break; + + case 'I': + interactiveflag = true; + break; + + case 'q': + quietflag = true; + break; + + case 'x': + suffixes = a; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'z': + zone_set(a); + break; + + case 'k': /* set keyword expand mode */ + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(a))) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + /* Now handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + + ffree(); + + if ( initflag ) { + switch (pairnames(argc, argv, rcswriteopen, false, false)) { + case -1: break; /* not exist; ok */ + case 0: continue; /* error */ + case 1: rcserror("already exists"); + continue; + } + } + else { + switch (pairnames(argc, argv, rcswriteopen, true, false)) { + case -1: continue; /* not exist */ + case 0: continue; /* errors */ + case 1: break; /* file exists; ok*/ + } + } + + + /* + * RCSname contains the name of the RCS file, and + * workname 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", RCSname); + + changed = initflag | textflag; + keepRCStime = Ttimeflag; + if (!initflag) { + if (!checkaccesslist()) continue; + gettree(); /* Read the delta tree. */ + } + + /* update admin. node */ + if (strict_selected) { + changed |= StrictLocks ^ strictlock; + StrictLocks = strictlock; + } + if ( + commsyml && + ( + commsymlen != Comment.size || + memcmp(commsyml, Comment.string, commsymlen) != 0 + ) + ) { + Comment.string = commsyml; + Comment.size = strlen(commsyml); + changed = true; + } + if (0 <= expmode && Expand != expmode) { + Expand = expmode; + changed = true; + } + + /* update default branch */ + if (branchflag && expandsym(branchsym, &branchnum)) { + if (countnumflds(branchnum.string)) { + if (cmpnum(Dbranch, branchnum.string) != 0) { + Dbranch = branchnum.string; + changed = true; + } + } else + if (Dbranch) { + Dbranch = 0; + changed = true; + } + } + + changed |= doaccess(); /* Update access list. */ + + changed |= doassoc(); /* Update association list. */ + + changed |= dolocks(); /* Update locks. */ + + changed |= domessages(); /* Update log messages. */ + + /* update state attribution */ + if (chgheadstate) { + /* change state of default branch or head */ + if (!Dbranch) { + if (!Head) + rcswarn("can't change states in an empty tree"); + else if (strcmp(Head->state, headstate) != 0) { + Head->state = headstate; + changed = true; + } + } else + changed |= rcs_setstate(Dbranch,headstate); + } + for (curstate = statelst; curstate; curstate = curstate->nextstatus) + changed |= rcs_setstate(curstate->revno,curstate->status); + + cuthead = cuttail = 0; + if (delrev.strt && removerevs()) { + /* rebuild delta tree if some deltas are deleted */ + if ( cuttail ) + VOID genrevs( + cuttail->num, (char *)0, (char *)0, (char *)0, + &gendeltas + ); + buildtree(); + changed = true; + keepRCStime = false; + } + + if (nerror) + continue; + + putadmin(); + if ( Head ) + puttree(Head, frewrite); + putdesc(textflag,textfile); + + if ( Head) { + if (delrev.strt || messagelst) { + if (!cuttail || buildeltatext(gendeltas)) { + advise_access(finptr, MADV_SEQUENTIAL); + scanlogtext((struct hshentry *)0, false); + /* copy rest of delta text nodes that are not deleted */ + changed = true; + } + } + } + + if (initflag) { + /* Adjust things for donerewrite's sake. */ + if (stat(workname, &RCSstat) != 0) { +# if bad_creat0 + mode_t m = umask(0); + (void) umask(m); + RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m; +# else + changed = -1; +# endif + } + RCSstat.st_nlink = 0; + keepRCStime = false; + } + if (donerewrite(changed, + keepRCStime ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + break; + + diagnose("done\n"); + } + + tempunlink(); + exitmain(exitstatus); +} /* end of main (rcs) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + + void +exiterr() +{ + ORCSerror(); + 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') + continue; + temp = sp; + sp = checksym(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 = 0; + else { + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; + pt->revno = sp; + } + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; +} + + + static void +getchaccess(login, command) + char const *login; + enum changeaccess command; +{ + register struct chaccess *pt; + + pt = talloc(struct chaccess); + pt->login = login; + pt->command = command; + pt->nextchaccess = 0; + *nextchaccess = pt; + 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 == ',') + continue; + if ( c == '\0') { + if (command == erase && sp-opt == 1) { + getchaccess((char*)0, 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; + *nextmessage = pt; + nextmessage = &pt->nextmessage; +} + + + 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') + continue; + 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') + continue; + pt = talloc(struct Status); + pt->status = temp; + pt->revno = sp; + pt->nextstatus = 0; + *nextstate = pt; + nextstate = &pt->nextstatus; +} + + + + 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') + continue; + + /* 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') + continue; + pt->strt = sp; pt->code = 1; + while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp); + *sp = '\0'; + pt->end = 0; + 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->code = 0; + pt->end = 0; + return; + } + if (c != separator) { + error("invalid range %s %s after -o", pt->strt, sp); + } + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; + if (!c) { /* -orev: */ + pt->code = 2; + pt->end = 0; + 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. + * For the one given by delta (if delta), 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. + */ +{ + struct hshentry const *nextdelta; + struct cbuf cb; + + for (;;) { + foutptr = 0; + if (eoflex()) { + if(delta) + rcsfaterror("can't find delta for revision %s", + delta->num + ); + return; /* no more delta text nodes */ + } + nextlex(); + if (!(nextdelta=getnum())) + fatserror("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); + nextlex(); + delta->igtext = getphrases(Ktext); + } else { + if (nextdelta->log.string && nextdelta->selector) { + foutptr = 0; + readstring(); + foutptr = frewrite; + putstring(foutptr, false, nextdelta->log, true); + afputc(nextc, foutptr); + } else + readstring(); + ignorephrases(Ktext); + } + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + /* got the one we're looking for */ + if (edit) + editstring((struct hshentry*)0); + else + enterstring(); +} + + + + static struct Lockrev ** +rmnewlocklst(which) + char const *which; +/* Remove lock to revision WHICH from newlocklst. */ +{ + struct Lockrev *pt, **pre; + + pre = &newlocklst; + while ((pt = *pre)) + if (strcmp(pt->revno, which) != 0) + pre = &pt->nextrev; + else { + *pre = pt->nextrev; + tfree(pt); + } + return pre; +} + + + + static int +doaccess() +{ + register struct chaccess *ch; + register struct access **p, *t; + register int changed = false; + + for (ch = chaccess; ch; ch = ch->nextchaccess) { + switch (ch->command) { + case erase: + if (!ch->login) { + if (AccessList) { + AccessList = 0; + changed = true; + } + } else + for (p = &AccessList; (t = *p); p = &t->nextaccess) + if (strcmp(ch->login, t->login) == 0) { + *p = t->nextaccess; + changed = true; + break; + } + break; + case append: + for (p = &AccessList; ; p = &t->nextaccess) + if (!(t = *p)) { + *p = t = ftalloc(struct access); + t->login = ch->login; + t->nextaccess = 0; + changed = true; + break; + } else if (strcmp(ch->login, t->login) == 0) + break; + break; + } + } + return changed; +} + + + 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, status; + FILE * mailmess; +#endif + + + aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who); + if (suppress_mail) + return true; + 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 = fopenSafer(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", + basefilename(RCSname), Delta, getfullRCSname(), getcaller() + ); + aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr); + eflush(); + + 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); + eflush(); + } + } + } + Orewind(mailmess); + aflush(mailmess); + status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0); + Ozclose(&mailmess); + if (status == 0) + return true; + warn("Mail failed."); +#endif + warn("Mail notification of broken locks is not available."); + warn("Please tell `%s' why you broke the lock.", who); + return(true); +} + + + + static int +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 rcslock *next, **trail; + char const *num; + + num=delta->num; + for (trail = &Locks; (next = *trail); trail = &next->nextlock) + if (strcmp(num, next->delta->num) == 0) { + if ( + strcmp(getcaller(),next->login) != 0 + && !sendmail(num, next->login) + ) { + rcserror("revision %s still locked by %s", + num, next->login + ); + return false; + } + diagnose("%s unlocked\n", next->delta->num); + *trail = next->nextlock; + next->delta->lockedby = 0; + return true; + } + rcserror("no lock set on revision %s", num); + return false; +} + + + + static struct hshentry * +searchcutpt(object, length, store) + char const *object; + int length; + struct hshentries *store; +/* Function: Search store and return entry with number being object. */ +/* cuttail = 0, if the entry is Head; otherwise, cuttail */ +/* is the entry point to the one with number being object */ + +{ + cuthead = 0; + 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 rcslock const *lockpt; + + for (pt = strt; pt != tail; pt = pt->next) { + if ( pt->branches ){ /* a branch point */ + rcserror("can't remove branch point %s", pt->num); + return true; + } + for (lockpt = Locks; lockpt; lockpt = lockpt->nextlock) + if (lockpt->delta == pt) { + rcserror("can't remove locked revision %s", pt->num); + return true; + } + pt->selector = false; + diagnose("deleting revision %s\n",pt->num); + } + return false; +} + + + + 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 (0, if delstrt is head), and the */ +/* revision after the last removed revision in cuttail (0 */ +/* if the last is a leaf */ + +{ + struct hshentry *target, *target2, *temp; + int length; + int cmp; + + if (!expandsym(delrev.strt, &numrev)) return 0; + target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); + if ( ! target ) return 0; + cmp = cmpnum(target->num, numrev.string); + 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 (cmp) { + rcserror("Revision %s doesn't exist.", numrev.string); + return 0; + } + else + temp = searchcutpt(numrev.string, length, gendeltas); + cuttail = target->next; + if ( branchpoint(temp, cuttail) ) { + cuttail = 0; + return 0; + } + delstrt = temp; /* first revision to be removed */ + return 1; + } + + if (length & 1) { /* invalid branch after -o */ + rcserror("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 = 0; + return 0; + } + delstrt = temp; + return 1; + } + + if (delrev.code == 2) { /* -o rev- */ + if ( length == 2 ) { + temp = searchcutpt(target->num, 1,gendeltas); + if (cmp) + cuttail = target; + else + cuttail = target->next; + } + else { + if (cmp) { + cuthead = target; + if ( !(temp = target->next) ) return 0; + } + else + temp = searchcutpt(target->num, length, gendeltas); + getbranchno(temp->num, &numrev); /* get branch number */ + VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas); + } + if ( branchpoint( temp, cuttail ) ) { + cuttail = 0; + 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)) + ) { + rcserror("invalid revision range %s-%s", + target->num, numrev.string + ); + return 0; + } + + target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); + if ( ! target2 ) return 0; + + if ( length > 2) { /* delete revisions on branches */ + if ( cmpnum(target->num, target2->num) > 0) { + cmp = cmpnum(target2->num, numrev.string); + temp = target; + target = target2; + target2 = temp; + } + if (cmp) { + if ( ! cmpnum(target->num, target2->num) ) { + rcserror("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 + cmp = cmpnum(target2->num, numrev.string); + if (cmp) { + if ( ! cmpnum(target->num, target2->num) ) { + rcserror("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 = 0; + return 0; + } + delstrt = temp; + return 1; +} + + + + static int +doassoc() +/* Add or delete (if !revno) association that is stored in assoclst. */ +{ + char const *p; + int changed = false; + struct Symrev const *curassoc; + struct assoc **pre, *pt; + + /* add new associations */ + for (curassoc = assoclst; curassoc; curassoc = curassoc->nextsym) { + char const *ssymbol = curassoc->ssymbol; + + if (!curassoc->revno) { /* delete symbol */ + for (pre = &Symbols; ; pre = &pt->nextassoc) + if (!(pt = *pre)) { + rcswarn("can't delete nonexisting symbol %s", ssymbol); + break; + } else if (strcmp(pt->symbol, ssymbol) == 0) { + *pre = pt->nextassoc; + changed = true; + break; + } + } + else { + if (curassoc->revno[0]) { + p = 0; + if (expandsym(curassoc->revno, &numrev)) + p = fstr_save(numrev.string); + } else if (!(p = tiprev())) + rcserror("no latest revision to associate with symbol %s", + ssymbol + ); + if (p) + changed |= addsymbol(p, ssymbol, curassoc->override); + } + } + return changed; +} + + + + static int +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; + int changed = false; + + if (unlockcaller) { /* find lock for caller */ + if ( Head ) { + if (Locks) { + switch (findlock(true, &target)) { + case 0: + /* remove most recent lock */ + changed |= breaklock(Locks->delta); + break; + case 1: + diagnose("%s unlocked\n",target->num); + changed = true; + break; + } + } else { + rcswarn("No locks are set."); + } + } else { + rcswarn("can't unlock an empty tree"); + } + } + + /* remove locks which are stored in rmvlocklst */ + for (lockpt = rmvlocklst; lockpt; lockpt = lockpt->nextrev) + if (expandsym(lockpt->revno, &numrev)) { + target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't unlock nonexisting revision %s", + lockpt->revno + ); + else + changed |= breaklock(target); + /* breaklock does its own diagnose */ + } + + /* add new locks which stored in newlocklst */ + for (lockpt = newlocklst; lockpt; lockpt = lockpt->nextrev) + changed |= setlock(lockpt->revno); + + if (lockhead) /* lock default branch or head */ + if (Dbranch) + changed |= setlock(Dbranch); + else if (Head) + changed |= setlock(Head->num); + else + rcswarn("can't lock an empty tree"); + return changed; +} + + + + static int +setlock(rev) + char const *rev; +/* Function: Given a revision or branch number, finds the corresponding + * delta and locks it for caller. + */ +{ + struct hshentry *target; + int r; + + if (expandsym(rev, &numrev)) { + target = genrevs(numrev.string, (char*)0, (char*)0, + (char*)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't lock nonexisting revision %s", + numrev.string + ); + else { + if ((r = addlock(target, false)) < 0 && breaklock(target)) + r = addlock(target, true); + if (0 <= r) { + if (r) + diagnose("%s locked\n", target->num); + return r; + } + } + } + return 0; +} + + + static int +domessages() +{ + struct hshentry *target; + struct Message *p; + int changed = false; + + for (p = messagelst; p; p = p->nextmessage) + if ( + expandsym(p->revno, &numrev) && + (target = genrevs( + numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas + )) + ) { + /* + * We can't check the old log -- it's much later in the file. + * We pessimistically assume that it changed. + */ + target->log = p->message; + changed = true; + } + return changed; +} + + + static int +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*)0, (char*)0, + (char*)0, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + rcserror("can't set state of nonexisting revision %s", + numrev.string + ); + else if (strcmp(target->state, status) != 0) { + target->state = status; + return true; + } + } + return false; +} + + + + + + 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 *cutname; + + fcut = 0; + cuttail->selector = false; + scanlogtext(deltas->first, false); + if ( cuthead ) { + cutname = maketemp(3); + if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) { + efaterror(cutname); + } + + while (deltas->first != cuthead) { + deltas = deltas->rest; + scanlogtext(deltas->first, true); + } + + snapshotedit(fcut); + Orewind(fcut); + aflush(fcut); + } + + while (deltas->first != cuttail) + scanlogtext((deltas = deltas->rest)->first, true); + finishedit((struct hshentry*)0, (FILE*)0, true); + Ozclose(&fcopy); + + if (fcut) { + char const *diffname = maketemp(0); + char const *diffv[6 + !!OPEN_O_BINARY]; + char const **diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp == "--binary"; +# endif + *++diffp = "-"; + *++diffp = resultname; + *++diffp = 0; + switch (runv(fileno(fcut), diffname, diffv)) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: rcsfaterror("diff failed"); + } + Ofclose(fcut); + return putdtext(cuttail,diffname,frewrite,true); + } else + return putdtext(cuttail,resultname,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 && !quietflag) { + if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) { + rcserror("No revision deleted"); + Delta = delstrt; + while( Delta) { + Delta->selector = true; + Delta = Delta->next; + } + return; + } + } + Head = cuttail; + } + return; +} + +#if RCS_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/gnu/usr.bin/rcs/src/rcsbase.h b/gnu/usr.bin/rcs/src/rcsbase.h new file mode 100644 index 00000000000..b3d8c43eec6 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsbase.h @@ -0,0 +1,761 @@ +/* RCS common definitions and data structures */ + +#define RCSBASE "$Id: rcsbase.h,v 1.1 1996/08/12 04:08:13 millert Exp $" + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsbase.h,v $ + * Revision 1.1 1996/08/12 04:08:13 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.20 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.19 1995/06/01 16:23:43 eggert + * (SIZEABLE_PATH): Don't depend on PATH_MAX: it's not worth configuring. + * (Ioffset_type,BINARY_EXPAND,MIN_UNEXPAND,MIN_UNCHANGED_EXPAND): New macros. + * (maps_memory): New macro; replaces many instances of `has_mmap'. + * (cacheptr): Renamed from cachetell. + * (struct RILE): New alternate name for RILE; the type is now recursive. + * (deallocate): New member for RILE, used for generic buffer deallocation. + * (cacheunget_): No longer take a failure arg; just call Ierror on failure. + * (struct rcslock): Renamed from struct lock, to avoid collisions with + * system headers on some hosts. All users changed. + * (basefilename): Renamed from basename, likewise. + * (dirtpname): Remove; no longer external. + * (dirlen, dateform): Remove; no longer used. + * (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New functions. + * (zonelenmax): Increase to 9 for full ISO 8601 format. + * (catchmmapints): Depend on has_NFS. + * + * Revision 5.18 1994/03/17 14:05:48 eggert + * Add primitives for reading backwards from a RILE; + * this is needed to go back and find the $Log prefix. + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.17 1993/11/09 17:40:15 eggert + * Move RCS-specific time handling into rcstime.c. + * printf_string now takes two arguments, alas. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Don't arbitrarily limit the number of joins. Remove `nil'. + * Add Name keyword. Don't discard ignored phrases. + * Add support for merge -A vs -E, and allow up to three labels. + * Improve quality of diagnostics and prototypes. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:22 eggert + * Add -T support. Work around NFS mmap SIGBUS problem. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * while (E) ; -> while (E) continue; + * + * 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 _POSIX_PATH_MAX +# define SIZEABLE_PATH _POSIX_PATH_MAX +#else +# define SIZEABLE_PATH 255 /* size of a large path; not a hard limit */ +#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 time2date */ +#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 + + +/* + * 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 + * cacheunget_(f,c,s) - read c backwards from cached f, executing s at BOF + * Ifileno, Ioffset_type, Irewind, Itell - analogs to stdio routines + * + * By conventions, macros whose names end in _ are statements, not expressions. + * Following such macros with `; else' results in a syntax error. + */ + +#define maps_memory (has_map_fd || has_mmap) + +#if large_memory + typedef unsigned char const *Iptr_type; + typedef struct RILE { + Iptr_type ptr, lim; + unsigned char *base; /* not Iptr_type for lint's sake */ + unsigned char *readlim; + int fd; +# if maps_memory + void (*deallocate) P((struct RILE *)); +# else + FILE *stream; +# endif + } RILE; +# if maps_memory +# 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 + int Igetmore P((RILE*)); +# 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 cacheunget_(f,c) (c)=(--ptr)[-1]; +# define Ioffset_type size_t +# define Itell(f) ((f)->ptr - (f)->base) +# define Irewind(f) ((f)->ptr = (f)->base) +# define cacheptr() ptr +# define Ifileno(f) ((f)->fd) +#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))==EOF){testIerror(f);if(feof(f))s}} +# define cachegeteof_(c,s) Igeteof_(ptr,c,s) +# define Iget_(f,c) { if (((c)=getc(f))==EOF) testIeof(f); } +# define cacheget_(c) Iget_(ptr,c) +# define cacheunget_(f,c) if(fseek(ptr,-2L,SEEK_CUR))Ierror();else cacheget_(c) +# define Ioffset_type long +# define Itell(f) ftell(f) +# define Ifileno(f) fileno(f) +#endif + +/* Print a char, but abort on write error. */ +#define aputc_(c,o) { if (putc(c,o)==EOF) testOerror(o); } + +/* 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)&(mode_t)~(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) */ + char const * name; /* name (if any) by which retrieved */ + struct cbuf log; /* log message requested at checkin */ + struct branchhead * branches; /* list of first revisions on branches*/ + struct cbuf ig; /* ignored phrases in admin part */ + struct cbuf igtext; /* ignored phrases in deltatext part */ + struct hshentry * next; /* next revision on same branch */ + struct hshentry * nexthsh; /* next revision with same hash value */ + long insertlns;/* lines inserted (computed by rlog) */ + 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 rcslock { + char const * login; + struct hshentry * delta; + struct rcslock * 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 RCS_lint +# define libId(name,rcsid) +# define mainProg(name,cmd,rcsid) int name mainArgs +#else +# define libId(name,rcsid) char const name[] = rcsid; +# define mainProg(n,c,i) char const Copyright[] = "Copyright 1982,1988,1989 Walter F. Tichy, Purdue CS\nCopyright 1990,1991,1992,1993,1994,1995 Paul Eggert", baseid[] = RCSBASE, cmdid[] = c; libId(n,i) int main P((int,char**)); 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 NAME "Name" +#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, Name, RCSfile, Revision, Source, State, LocalId }; + /* 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[]; +void exiterr P((void)) exiting; + +/* merge */ +int merge P((int,char const*,char const*const[3],char const*const[3])); + +/* rcsedit */ +#define ciklogsize 23 /* sizeof("checked in with -k by ") */ +extern FILE *fcopy; +extern char const *resultname; +extern char const ciklog[ciklogsize]; +extern int locker_expansion; +RILE *rcswriteopen P((struct buf*,struct stat*,int)); +char const *makedirtemp P((int)); +char const *getcaller P((void)); +int addlock P((struct hshentry*,int)); +int addsymbol P((char const*,char const*,int)); +int checkaccesslist P((void)); +int chnamemod P((FILE**,char const*,char const*,int,mode_t,time_t)); +int donerewrite P((int,time_t)); +int dorewrite P((int,int)); +int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int)); +int findlock P((int,struct hshentry**)); +int setmtime P((char const*,time_t)); +void ORCSclose P((void)); +void ORCSerror P((void)); +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) clear_buf(b) +#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0)) +extern FILE *workstdout; +extern char *workname; +extern char const *RCSname; +extern char const *suffixes; +extern int fdlock; +extern struct stat RCSstat; +RILE *rcsreadopen P((struct buf*,struct stat*,int)); +char *bufenlarge P((struct buf*,char const**)); +char const *basefilename P((char const*)); +char const *getfullRCSname P((void)); +char const *maketemp P((int)); +char const *rcssuffix P((char const*)); +int pairnames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); +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 putdtext P((struct hshentry const*,char const*,FILE*,int)); +int ttystdin P((void)); +int yesorno P((int,char const*,...)) printf_string(2,3); +struct cbuf cleanlogmsg P((char*,size_t)); +struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*)); +void putdesc P((int,char*)); +void putdftext P((struct hshentry const*,RILE*,FILE*,int)); + +/* rcskeep */ +extern int prevkeys; +extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate; +int getoldkeys P((RILE*)); + +/* rcskeys */ +extern char const *Keyword[]; +void setRCSlocalId(char const *); +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 long rcsline; +char const *getid P((void)); +void efaterror P((char const*)) exiting; +void enfaterror P((int,char const*)) exiting; +void fatcleanup P((int)) exiting; +void faterror P((char const*,...)) printf_string_exiting(1,2); +void fatserror P((char const*,...)) printf_string_exiting(1,2); +void rcsfaterror P((char const*,...)) printf_string_exiting(1,2); +void Ieof P((void)) exiting; +void Ierror P((void)) exiting; +void Oerror P((void)) exiting; +char *checkid P((char*,int)); +char *checksym 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 Orewind P((FILE*)); +void Ozclose P((FILE**)); +void aflush P((FILE*)); +void afputc P((int,FILE*)); +void aprintf P((FILE*,char const*,...)) printf_string(2,3); +void aputs P((char const*,FILE*)); +void checksid P((char*)); +void checkssym P((char*)); +void diagnose P((char const*,...)) printf_string(1,2); +void eerror P((char const*)); +void eflush P((void)); +void enerror P((int,char const*)); +void error P((char const*,...)) printf_string(1,2); +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 rcserror P((char const*,...)) printf_string(1,2); +void rcswarn P((char const*,...)) printf_string(1,2); +void testIerror P((FILE*)); +void testOerror P((FILE*)); +void warn P((char const*,...)) printf_string(1,2); +void warnignore P((void)); +void workerror P((char const*,...)) printf_string(1,2); +void workwarn P((char const*,...)) printf_string(1,2); +#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 large_memory && maps_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 enum tokens const ctab[]; + +/* rcsrev */ +char *partialno P((struct buf*,char const*,int)); +char const *namedrev P((char const*,struct hshentry*)); +char const *tiprev P((void)); +int cmpdate P((char const*,char const*)); +int cmpnum P((char const*,char const*)); +int cmpnumfld P((char const*,char const*,int)); +int compartial P((char const*,char const*,int)); +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**)); +int 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 */ +#define BINARY_EXPAND 5 /* -kb like -ko, but use binary mode I/O */ +#define MIN_UNEXPAND OLD_EXPAND /* min value for no logical expansion */ +#define MIN_UNCHANGED_EXPAND (OPEN_O_BINARY ? BINARY_EXPAND : OLD_EXPAND) + /* min value guaranteed to yield an identical file */ +struct diffcmd { + 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 cbuf Ignored; +extern struct rcslock *Locks; +extern struct hshentry * Head; +extern int Expand; +extern int StrictLocks; +extern int TotalDeltas; +extern char const *const expand_names[]; +extern char const + Kaccess[], Kauthor[], Kbranch[], Kcomment[], + Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[], + Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[]; +void unexpected_EOF P((void)) exiting; +int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*)); +int str2expmode P((char const*)); +void getadmin P((void)); +void getdesc P((int)); +void gettree P((void)); +void ignorephrases P((char const*)); +void initdiffcmd P((struct diffcmd*)); +void putadmin P((void)); +void putstring P((FILE*,int,struct cbuf,int)); +void puttree P((struct hshentry const*,FILE*)); + +/* rcstime */ +#define zonelenmax 9 /* maxiumum length of time zone string, e.g. "+12:34:56" */ +char const *date2str P((char const[datesize],char[datesize + zonelenmax])); +time_t date2time P((char const[datesize])); +void str2date P((char const*,char[datesize])); +void time2date P((time_t,char[datesize])); +void zone_set P((char const*)); + +/* rcsutil */ +extern int RCSversion; +FILE *fopenSafer P((char const*,char const*)); +char *cgetenv P((char const*)); +char *fstr_save P((char const*)); +char *str_save P((char const*)); +char const *getusername P((int)); +int fdSafer P((int)); +int getRCSINIT P((int,char**,char***)); +int run P((int,char const*,...)); +int runv P((int,char const*,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 RCS_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 +time_t now P((void)); +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_mmap && large_memory +# if has_NFS && mmap_signal + void catchmmapints P((void)); + void readAccessFilenameBuffer P((char const*,unsigned char const*)); +# else +# define catchmmapints() +# endif +#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 + +/* version */ +extern char const RCS_version_string[]; diff --git a/gnu/usr.bin/rcs/src/rcsclean.c b/gnu/usr.bin/rcs/src/rcsclean.c new file mode 100644 index 00000000000..f949c778818 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsclean.c @@ -0,0 +1,333 @@ +/* Clean up working files. */ + +/* Copyright 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 1.1 1996/08/12 04:08:14 millert Exp $") +{ + static char const usage[] = + "\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ..."; + + static struct buf revision; + + char *a, **newargv; + char const *rev, *p; + int dounlock, expmode, perform, unlocked, unlockflag, waslocked; + int Ttimeflag; + struct hshentries *deltas; + struct hshentry *delta; + struct stat workstat; + + setrid(); + + expmode = -1; + rev = 0; + suffixes = X_DEFAULT; + perform = true; + unlockflag = false; + Ttimeflag = false; + + argc = getRCSINIT(argc, argv, &newargv); + argv = newargv; + for (;;) { + if (--argc < 1) { +# if has_dirent + argc = get_directory(".", &newargv); + argv = newargv; + break; +# else + faterror("no pathnames specified"); +# endif + } + a = *++argv; + if (!*a || *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 'T': + if (*a) + goto unknown; + Ttimeflag = true; + break; + + case 'u': + unlockflag = true; + goto handle_revision; + + case 'V': + setRCSversion(*argv); + break; + + case 'x': + suffixes = a; + break; + + case 'z': + zone_set(a); + break; + + default: + unknown: + error("unknown option: %s%s", *argv, usage); + } + } + + dounlock = perform & unlockflag; + + if (nerror) + cleanup(); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + + ffree(); + + if (!( + 0 < pairnames( + argc, argv, + dounlock ? rcswriteopen : rcsreadopen, + true, true + ) && + (workptr = Iopen(workname, FOPEN_R_WORK, &workstat)) + )) + continue; + + if (same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + 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; + if (unlocked<waslocked && workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH)) + continue; + + if (unlocked && !checkaccesslist()) + continue; + + if (dorewrite(dounlock, unlocked) != 0) + 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, RCSname); + + if (perform & unlocked) { + if_advise_access(deltas->first != delta, finptr, MADV_SEQUENTIAL); + if (donerewrite(true, + Ttimeflag ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + continue; + } + + if (!quietflag) + aprintf(stdout, "rm -f %s\n", workname); + Izclose(&workptr); + if (perform && un_link(workname) != 0) + eerror(workname); + + } + + tempunlink(); + if (!quietflag) + Ofclose(stdout); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); + Izclose(&workptr); + Ozclose(&fcopy); + ORCSclose(); + dirtempunlink(); +} + +#if RCS_lint +# define exiterr rcscleanExit +#endif + void +exiterr() +{ + ORCSerror(); + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + static int +unlock(delta) + struct hshentry *delta; +{ + register struct rcslock **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 void_closedir +# define close_directory(d) (closedir(d), 0) +# else +# define close_directory(d) closedir(d) +# endif + if (errno || close_directory(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/gnu/usr.bin/rcs/src/rcsdiff.c b/gnu/usr.bin/rcs/src/rcsdiff.c new file mode 100644 index 00000000000..d3aca3d5ef4 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsdiff.c @@ -0,0 +1,484 @@ +/* Compare RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsdiff.c,v $ + * Revision 1.1 1996/08/12 04:08:15 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (main): Pass "--binary" if -kb and if --binary makes a difference. + * Don't treat + options specially. + * + * Revision 5.17 1994/03/17 14:05:48 eggert + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.16 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Add -z. Ignore -T. Pass -Vn to `co'. Add Name keyword. + * Put revision numbers in -c output. Improve quality of diagnostics. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Add -V. Use co -M for better dates with traditional diff -c. + * + * Revision 5.13 1992/02/17 23:02:23 eggert + * Output more readable context diff headers. + * Suppress needless checkout and comparison of identical revisions. + * + * Revision 5.12 1992/01/24 18:44:19 eggert + * Add GNU diff 1.15.2's new options. lint -> RCS_lint + * + * Revision 5.11 1992/01/06 02:42:34 eggert + * Update usage string. + * + * 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 1.1 1996/08/12 04:08:15 millert Exp $") +{ + static char const cmdusage[] = + "\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [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, *suffixarg, *versionarg, *zonearg; +#if DIFF_L + static struct buf labelbuf[2]; + int file_labels; + char const **diff_label1, **diff_label2; + char date2[datesize]; +#endif + char const *cov[10 + !DIFF_L]; + char const **diffv, **diffp, **diffpend; /* 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; + int no_diff_means_no_output; + register c; + + exitstatus = DIFF_SUCCESS; + + bufautobegin(&commarg); + bufautobegin(&numericrev); + revnums = 0; + rev1 = rev2 = xrev2 = 0; +#if DIFF_L + file_labels = 0; +#endif + expandarg = suffixarg = versionarg = zonearg = 0; + no_diff_means_no_output = true; + suffixes = X_DEFAULT; + + /* + * Room for runv extra + args [+ --binary] [+ 2 labels] + * + 1 file + 1 trailing null. + */ + diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2); + diffp = diffv + 1; + *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: error("too many revision numbers"); + } + goto option_handled; + case '-': case 'D': + no_diff_means_no_output = false; + /* fall into */ + case 'C': case 'F': case 'I': case 'L': case 'W': +#if DIFF_L + if (c == 'L' && ++file_labels == 2) + faterror("too many -L options"); +#endif + *dcp++ = c; + if (*a) + do *dcp++ = *a++; + while (*a); + else { + if (!--argc) + faterror("-%c needs following argument%s", + c, cmdusage + ); + *diffp++ = *argv++; + } + break; + case 'y': + no_diff_means_no_output = false; + /* fall into */ + case 'B': case 'H': + 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': + suffixarg = *argv; + suffixes = *argv + 2; + goto option_handled; + case 'z': + zonearg = *argv; + zone_set(*argv + 2); + goto option_handled; + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + goto option_handled; + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + goto option_handled; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + option_handled: + if (dcp != *argv+1) { + *dcp = 0; + *diffp++ = *argv; + } + } /* end of option processing */ + + for (pp = diffv+2, c = 0; pp<diffp; ) + c += strlen(*pp++) + 1; + diffvstr = a = tnalloc(char, c + 1); + for (pp = diffv+2; pp<diffp; ) { + p = *pp++; + *a++ = ' '; + while ((*a = *p++)) + a++; + } + *a = 0; + +#if DIFF_L + diff_label1 = diff_label2 = 0; + if (file_labels < 2) { + if (!file_labels) + diff_label1 = diffp++; + diff_label2 = diffp++; + } +#endif + diffpend = diffp; + + cov[1] = CO; + cov[2] = "-q"; +# if !DIFF_L + cov[3] = "-M"; +# endif + + /* Now handle all pathnames. */ + if (nerror) + cleanup(); + else if (argc < 1) + faterror("no input file%s", cmdusage); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); + + if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) + continue; + diagnose("===================================================================\nRCS file: %s\n",RCSname); + if (!rev2) { + /* Make sure work file is readable, and get its status. */ + if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { + eerror(workname); + continue; + } + } + + + gettree(); /* reads in the delta tree */ + + if (!Head) { + rcserror("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 *)0,(char *)0,(char *)0,&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 *)0,(char *)0,(char *)0,&gendeltas))) continue; + xrev2=target->num; + if (no_diff_means_no_output && xrev1 == xrev2) + continue; + } 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], (char*)0, date2); + } +#endif + + diagnose("retrieving revision %s\n", xrev1); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */ + + pp = &cov[3 + !DIFF_L]; + *pp++ = commarg.string; + if (lexpandarg) *pp++ = lexpandarg; + if (suffixarg) *pp++ = suffixarg; + if (versionarg) *pp++ = versionarg; + if (zonearg) *pp++ = zonearg; + *pp++ = RCSname; + *pp = 0; + + diffp = diffpend; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *diffp++ = "--binary"; +# endif + diffp[0] = maketemp(0); + if (runv(-1, diffp[0], cov)) { + rcserror("co failed"); + continue; + } + if (!rev2) { + diffp[1] = workname; + if (*workname == '-') { + char *dp = ftnalloc(char, strlen(workname)+3); + diffp[1] = dp; + *dp++ = '.'; + *dp++ = SLASH; + VOID strcpy(dp, workname); + } + } else { + diagnose("retrieving revision %s\n",xrev2); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */ + cov[3 + !DIFF_L] = commarg.string; + diffp[1] = maketemp(1); + if (runv(-1, diffp[1], cov)) { + rcserror("co failed"); + continue; + } + } + if (!rev2) + diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname); + else + diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); + + diffp[2] = 0; + switch (runv(-1, (char*)0, diffv)) { + case DIFF_SUCCESS: + break; + case DIFF_FAILURE: + if (exitstatus == DIFF_SUCCESS) + exitstatus = DIFF_FAILURE; + break; + default: + workerror("diff failed"); + } + } + + tempunlink(); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = DIFF_TROUBLE; + Izclose(&finptr); + Izclose(&workptr); +} + +#if RCS_lint +# define exiterr rdiffExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} + +#if DIFF_L + static char const * +setup_label(b, num, date) + struct buf *b; + char const *num; + char const date[datesize]; +{ + char *p; + char datestr[datesize + zonelenmax]; + VOID date2str(date, datestr); + bufalloc(b, + strlen(workname) + + sizeof datestr + 4 + + (num ? strlen(num) : 0) + ); + p = b->string; + if (num) + VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num); + else + VOID sprintf(p, "-L%s\t%s", workname, datestr); + return p; +} +#endif diff --git a/gnu/usr.bin/rcs/src/rcsedit.c b/gnu/usr.bin/rcs/src/rcsedit.c new file mode 100644 index 00000000000..a1b81918319 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsedit.c @@ -0,0 +1,1953 @@ +/* RCS stream editor */ + +/****************************************************************************** + * edits the input file according to a + * script from stdin, generated by diff -n + * performs keyword expansion + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsedit.c,v $ + * Revision 1.1 1996/08/12 04:08:16 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (dirtpname): No longer external. + * (do_link): Simplify logic. + * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. + * (fopen_update_truncate): Replace `#if' with `if'. + * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. + * + * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output + * at the end of incomplete lines. + * + * (keyreplace): Do not assume that seeking backwards + * at the start of a file will fail; on some systems it succeeds. + * Convert C- and Pascal-style comment starts to ` *' in comment leader. + * + * (rcswriteopen): Use fdSafer to get safer file descriptor. + * Open RCS file with FOPEN_RB. + * + * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. + * Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * (aflush): Move to rcslex.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. + * Escape white space, $, and \ in keyword string file names. + * Don't output 2 spaces between date and time after Log. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Some hosts have readlink but not ELOOP. Avoid `unsigned'. + * Preserve dates more systematically. Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:24 eggert + * Add -T support. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_chmod_close, bad_creat0. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Add setmode parameter to chnamemod. addsymbol now reports changes. + * while (E) ; -> while (E) continue; + * + * 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 1.1 1996/08/12 04:08:16 millert Exp $") + +static void editEndsPrematurely P((void)) exiting; +static void editLineNumberOverflow P((void)) exiting; +static void escape_string P((FILE*,char const*)); +static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); + +FILE *fcopy; /* result file descriptor */ +char const *resultname; /* result pathname */ +int locker_expansion; /* should the locker name be appended to Id val? */ +#if !large_memory + static RILE *fedit; /* edit file descriptor */ + static char const *editname; /* edit pathname */ +#endif +static 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 */ + +/* indexes into dirtpname */ +#define lockdirtp_index 0 +#define newRCSdirtp_index bad_creat0 +#define newworkdirtp_index (newRCSdirtp_index+1) +#define DIRTEMPNAMES (newworkdirtp_index + 1) + +enum maker {notmade, real, effective}; +static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ +static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ +#define lockname (dirtpname[lockdirtp_index].string) +#define newRCSname (dirtpname[newRCSdirtp_index].string) + + +#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 + if (unlink(s) == 0) + return 0; + else { + int e = errno; + /* + * Forge ahead even if errno == ENOENT; some completely + * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT + * even for existing unwritable files. + */ + 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 P((char const*,char const*)); + static int +do_link(s, t) + char const *s, *t; +/* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ +{ + int r = link(s, t); + + if (r != 0 && errno == EEXIST) { + struct stat sb, tb; + if ( + stat(s, &sb) == 0 && + stat(t, &tb) == 0 && + same_file(sb, tb, 0) + ) + r = 0; + errno = EEXIST; + } + return r; +} +# endif +#endif + + + static void +editEndsPrematurely() +{ + fatserror("edit script ends prematurely"); +} + + static 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 P((Iptr_type*,Iptr_type const*,long)); + static void +movelines(s1, s2, n) + register Iptr_type *s1; + register Iptr_type const *s2; + register long n; +{ + if (s1 < s2) + do { + *s1++ = *s2++; + } while (--n); + else { + s1 += n; + s2 += n; + do { + *--s1 = *--s2; + } while (--n); + } +} +#endif + +static void deletelines P((long,long)); +static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); +static void insertline P((long,Iptr_type)); +static void snapshotline P((FILE*,Iptr_type)); + +/* + * `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] hold 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 size_t gap, gapsize, linelim; + + static void +insertline(n, l) + 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) + long n, nlines; +/* Delete lines N through N+NLINES-1. N is 0-origin. */ +{ + 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; +{ + fin->ptr = l; + if (expandline(fin, fout, delta, true, (FILE*)0, true) < 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 = fin->ptr; + 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); + fin->ptr = here; + } + } +} + +/* Open a temporary NAME for output, truncating any previous contents. */ +# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) +#else /* !large_memory */ + static FILE * fopen_update_truncate P((char const*)); + static FILE * +fopen_update_truncate(name) + char const *name; +{ + if (bad_fopen_wplus && un_link(name) != 0) + efaterror(name); + return fopenSafer(name, FOPEN_WPLUS_WORK); +} +#endif + + + void +openfcopy(f) + FILE *f; +{ + if (!(fcopy = f)) { + if (!resultname) + resultname = maketemp(2); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); + } +} + + +#if !large_memory + + static void swapeditfiles P((FILE*)); + static void +swapeditfiles(outfile) + FILE *outfile; +/* Function: swaps resultname and editname, assigns fedit=fcopy, + * and rewinds fedit for reading. Set fcopy to outfile if nonnull; + * otherwise, set fcopy to be resultname opened for reading and writing. + */ +{ + char const *tmpptr; + + editline = 0; linecorr = 0; + Orewind(fcopy); + fedit = fcopy; + tmpptr=editname; editname=resultname; resultname=tmpptr; + openfcopy(outfile); +} + + void +snapshotedit(f) + FILE *f; +/* Copy the current state of the edits to F. */ +{ + finishedit((struct hshentry *)0, (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, 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) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) + ; + } else { + fastcopy(fe,fc); + } + Ifclose(fe); + } + if (!done) + swapeditfiles(outfile); +} +#endif + + + +#if large_memory +# define copylines(upto,delta) (editline = (upto)) +#else + static void copylines P((long,struct hshentry const*)); + static void +copylines(upto, delta) + register long upto; + struct hshentry const *delta; +/* + * Copy input lines editline+1..upto from fedit to fcopy. + * If delta, 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 *)0, (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,true) <= 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,true)) + continue; +} + + + 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 + editname = 0; + fedit = 0; + editline = linecorr = 0; + resultname = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); + copystring(); +#else + register int c; + declarecache; + register FILE *frew; + register 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 = cacheptr(); + 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, keyword expansion is performed simultaneously. + * If running out of lines in fedit, fedit and fcopy are swapped. + * editname 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. + * resultname 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; + long line_lim = LONG_MAX; + register RILE *fe; +# endif + register long i; + register RILE *fin; +# if large_memory + register 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 { + /* Copy lines without deleting any. */ + copylines(dc.line1, delta); + 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,true)){ + 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++, cacheptr()); +# endif + for (;;) { + GETC_(frew, c) + if (c==SDELIM) { + GETC_(frew, c) + if (c!=SDELIM) { + if (--i) + editEndsPrematurely(); + nextc = c; + uncache(fin); + return; + } + } +# if !large_memory + aputc_(c, f) +# endif + if (c == '\n') + break; + } + ++rcsline; + } while (--i); + uncache(fin); + } + } +} + + + +/* The rest is for keyword expansion */ + + + + int +expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) + RILE *infile; + FILE *outfile, *frewfile; + struct hshentry const *delta; + int delimstuffed, dolog; +/* + * Read a line from INFILE and write it to OUTFILE. + * Do keyword expansion with data from DELTA. + * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. + * If FREWFILE is set, copy the line unchanged to FREWFILE. + * DELIMSTUFFED must be true if FREWFILE is set. + * Append revision history to log only if DOLOG 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]) + 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 */ + uncache(infile); + keyreplace(matchresult, delta, ds, infile, out, dolog); + cache(infile); + e = 1; + break; + } + break; + } + } + + keystring_eof: + *tp = 0; + aputs(keyval.string, out); + uncache_exit: + uncache(infile); + return r + e; +} + + + static void +escape_string(out, s) + register FILE *out; + register char const *s; +/* Output to OUT the string S, escaping chars that would break `ci -k'. */ +{ + register char c; + for (;;) + switch ((c = *s++)) { + case 0: return; + case '\t': aputs("\\t", out); break; + case '\n': aputs("\\n", out); break; + case ' ': aputs("\\040", out); break; + case KDELIM: aputs("\\044", out); break; + case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} + /* fall into */ + default: aputc_(c, out) break; + } +} + +char const ciklog[ciklogsize] = "checked in with -k by "; + + static void +keyreplace(marker, delta, delimstuffed, infile, out, dolog) + enum markers marker; + register struct hshentry const *delta; + int delimstuffed; + RILE *infile; + register FILE *out; + int dolog; +/* function: outputs the keyword value(s) corresponding to marker. + * Attributes are derived from delta. + */ +{ + register char const *sp, *cp, *date; + register int c; + register size_t cs, cw, ls; + char const *sp1; + char datebuf[datesize + zonelenmax]; + int RCSv; + int exp; + + sp = Keyword[(int)marker]; + exp = Expand; + date = delta->date; + RCSv = RCSversion; + + if (exp != VAL_EXPAND) + aprintf(out, "%c%s", KDELIM, sp); + if (exp != KEY_EXPAND) { + + if (exp != VAL_EXPAND) + aprintf(out, "%c%c", 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 LocalId: + case Header: + escape_string(out, + marker==Id || RCSv<VERSION(4) + ? basefilename(RCSname) + : getfullRCSname() + ); + aprintf(out, " %s %s %s %s", + delta->num, + date2str(date, datebuf), + delta->author, + RCSv==VERSION(3) && delta->lockedby ? "Locked" + : delta->state + ); + if (delta->lockedby) + if (VERSION(5) <= RCSv) { + if (locker_expansion || exp==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 + || exp == KEYVALLOCK_EXPAND + || RCSv <= VERSION(4) + ) + aputs(delta->lockedby, out); + break; + case Log: + case RCSfile: + escape_string(out, basefilename(RCSname)); + break; + case Name: + if (delta->name) + aputs(delta->name, out); + break; + case Revision: + aputs(delta->num, out); + break; + case Source: + escape_string(out, getfullRCSname()); + break; + case State: + aputs(delta->state, out); + break; + default: + break; + } + if (exp != VAL_EXPAND) + afputc(' ', out); + } + if (exp != VAL_EXPAND) + afputc(KDELIM, out); + + if (marker == Log && dolog) { + struct buf leader; + + sp = delta->log.string; + ls = delta->log.size; + if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) + return; + bufautobegin(&leader); + if (RCSversion < VERSION(5)) { + cp = Comment.string; + cs = Comment.size; + } else { + int kdelim_found = 0; + Ioffset_type chars_read = Itell(infile); + declarecache; + setupcache(infile); cache(infile); + + c = 0; /* Pacify `gcc -Wall'. */ + + /* + * Back up to the start of the current input line, + * setting CS to the number of characters before `$Log'. + */ + cs = 0; + for (;;) { + if (!--chars_read) + goto done_backing_up; + cacheunget_(infile, c) + if (c == '\n') + break; + if (c == SDELIM && delimstuffed) { + if (!--chars_read) + break; + cacheunget_(infile, c) + if (c != SDELIM) { + cacheget_(c) + break; + } + } + cs += kdelim_found; + kdelim_found |= c==KDELIM; + } + cacheget_(c) + done_backing_up:; + + /* Copy characters before `$Log' into LEADER. */ + bufalloc(&leader, cs); + cp = leader.string; + for (cw = 0; cw < cs; cw++) { + leader.string[cw] = c; + if (c == SDELIM && delimstuffed) + cacheget_(c) + cacheget_(c) + } + + /* Convert traditional C or Pascal leader to ` *'. */ + for (cw = 0; cw < cs; cw++) + if (ctab[(unsigned char) cp[cw]] != SPACE) + break; + if ( + cw+1 < cs + && cp[cw+1] == '*' + && (cp[cw] == '/' || cp[cw] == '(') + ) { + size_t i = cw+1; + for (;;) + if (++i == cs) { + warn( + "`%c* $Log' is obsolescent; use ` * $Log'.", + cp[cw] + ); + leader.string[cw] = ' '; + break; + } else if (ctab[(unsigned char) cp[i]] != SPACE) + break; + } + + /* Skip `$Log ... $' string. */ + do { + cacheget_(c) + } while (c != KDELIM); + uncache(infile); + } + afputc('\n', out); + awrite(cp, cs, out); + sp1 = date2str(date, datebuf); + if (VERSION(5) <= RCSv) { + aprintf(out, "Revision %s %s %s", + delta->num, sp1, delta->author + ); + } else { + /* oddity: 2 spaces between date and time, not 1 as usual */ + sp1 = strchr(sp1, ' '); + 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. */ + cw = cs; + if (VERSION(5) <= RCSv) + for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) + continue; + 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'); + } + } + bufautoend(&leader); + } +} + +#if has_readlink + static int resolve_symlink P((struct buf*)); + 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; + int linkcount = MAXSYMLINKS; + + 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--) { +# ifndef ELOOP + /* + * Some pedantic Posix 1003.1-1990 hosts have readlink + * but not ELOOP. Approximate ELOOP with EMLINK. + */ +# define ELOOP EMLINK +# endif + errno = ELOOP; + return -1; + } else { + /* Splice symbolic link into L. */ + b[r] = '\0'; + L->string[ + ROOTPATH(b) ? 0 : basefilename(L->string) - L->string + ] = '\0'; + bufscat(L, b); + } + e = errno; + bufautoend(&bigbuf); + errno = e; + switch (e) { + case readlink_isreg_errno: 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 RCSBUF. + * Then try to open RCSBUF for reading and yield its RILE* 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 fdlock to the file descriptor of the RCS lockfile. + */ +{ + register char *tp; + register char const *sp, *RCSpath, *x; + RILE *f; + size_t l; + int e, exists, fdesc, fdescSafer, r, waslocked; + struct buf *dirt; + struct stat statbuf; + + waslocked = 0 <= fdlock; + exists = +# if has_readlink + resolve_symlink(RCSbuf); +# else + stat(RCSbuf->string, &statbuf) == 0 ? 1 + : errno==ENOENT ? 0 : -1; +# endif + if (exists < (mustread|waslocked)) + /* + * 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; + + RCSpath = RCSbuf->string; + sp = basefilename(RCSpath); + l = sp - RCSpath; + dirt = &dirtpname[waslocked]; + bufscpy(dirt, RCSpath); + tp = dirt->string + l; + x = rcssuffix(RCSpath); +# if has_readlink + if (!x) { + error("symbolic link to non RCS file `%s'", RCSpath); + errno = EINVAL; + return 0; + } +# endif + if (*sp == *x) { + error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); + errno = EINVAL; + return 0; + } + /* Create a lock filename that 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++)) + continue; + tp -= 2; + if (*tp == '_') { + error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); + errno = EINVAL; + return 0; + } + *tp = '_'; + } + + sp = dirt->string; + + f = 0; + + /* + * good news: + * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_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,OPEN_CREAT_READONLY) normally guarantees atomicity + * even with NFS. + * bad news: + * If you're root, (O_TRUNC,OPEN_CREAT_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. + * This can happen in the usual case of NFS over UDP. + * Suppose client A releases a lock by renaming ",f," to "f,v" at + * about the same time that client B obtains a lock by creating ",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 not only RCS, which uses open() and rename() + * to get and release locks; it also afflicts the traditional + * Unix method of using link() and unlink() to get and release locks, + * and the less traditional method of using mkdir() and rmdir(). + * There is no easy workaround. + * 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. + */ +# if !open_can_creat +# define create(f) creat(f, OPEN_CREAT_READONLY) +# else +# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_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); + fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ + e = errno; + setrid(); + + if (0 <= fdesc) + dirtpmaker[0] = effective; + + if (fdescSafer < 0) { + if (e == EACCES && stat(sp,&statbuf) == 0) + /* The RCS file is busy. */ + e = EEXIST; + } else { + e = ENOENT; + if (exists) { + f = Iopen(RCSpath, FOPEN_RB, status); + e = errno; + if (f && waslocked) { + /* Discard the previous lock in favor of this one. */ + ORCSclose(); + seteid(); + r = un_link(lockname); + e = errno; + setrid(); + if (r != 0) + enfaterror(e, lockname); + bufscpy(&dirtpname[lockdirtp_index], sp); + } + } + fdlock = fdescSafer; + } + + 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 (dirtpname[i].string == name) { + dirtpmaker[i] = notmade; + return; + } + faterror("keepdirtemp"); +} + + char const * +makedirtemp(isworkfile) + int isworkfile; +/* + * Create a unique pathname and store it into dirtpname. + * Because of storage in tpnames, dirtempunlink() can unlink the file later. + * Return a pointer to the pathname created. + * If ISWORKFILE is 1, put it into the working file's directory; + * if 0, put the unique file in RCSfile's directory. + */ +{ + register char *tp, *np; + register size_t dl; + register struct buf *bn; + register char const *name = isworkfile ? workname : RCSname; + + dl = basefilename(name) - name; + bn = &dirtpname[newRCSdirtp_index + isworkfile]; + bufalloc(bn, +# if has_mktemp + dl + 9 +# else + strlen(name) + 3 +# endif + ); + bufscpy(bn, name); + np = tp = bn->string; + tp += dl; + *tp++ = '_'; + *tp++ = '0'+isworkfile; + catchints(); +# if has_mktemp + VOID strcpy(tp, "XXXXXX"); + if (!mktemp(np) || !*np) + faterror("can't make temporary pathname `%.*s_%cXXXXXX'", + (int)dl, name, '0'+isworkfile + ); +# else + /* + * Posix 1003.1-1990 has no reliable way + * to create a unique file in a named directory. + * We fudge here. If the filename 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 + dirtpmaker[newRCSdirtp_index + isworkfile] = 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 = dirtpmaker[i]) != notmade) { + if (m == effective) + seteid(); + VOID un_link(dirtpname[i].string); + if (m == effective) + setrid(); + dirtpmaker[i] = notmade; + } +} + + + int +#if has_prototypes +chnamemod( + FILE **fromp, char const *from, char const *to, + int set_mode, mode_t mode, time_t mtime +) + /* The `#if has_prototypes' is needed because mode_t might promote to int. */ +#else + chnamemod(fromp, from, to, set_mode, mode, mtime) + FILE **fromp; char const *from,*to; + int set_mode; mode_t mode; time_t mtime; +#endif +/* + * Rename a file (with stream pointer *FROMP) from FROM to TO. + * FROM already exists. + * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. + * If MTIME is not -1, change its mtime to MTIME before renaming. + * Close and clear *FROMP before renaming it. + * Unlink TO if it already exists. + * Return -1 on error (setting errno), 0 otherwise. + */ +{ + mode_t mode_while_renaming = mode; + int fchmod_set_mode = 0; + +# if bad_a_rename || bad_NFS_rename + struct stat st; + if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { + if (fstat(fileno(*fromp), &st) != 0) + return -1; + if (bad_a_rename && set_mode <= 0) + mode = st.st_mode; + } +# endif + +# if bad_a_rename + /* + * There's a short window of inconsistency + * during which the lock file is writable. + */ + mode_while_renaming = mode|S_IWUSR; + if (mode != mode_while_renaming) + set_mode = 1; +# endif + +# if has_fchmod + if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) + fchmod_set_mode = set_mode; +# endif + /* If bad_chmod_close, we must close before chmod. */ + Ozclose(fromp); + if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0) + return -1; + + if (setmtime(from, mtime) != 0) + return -1; + +# if !has_rename || bad_b_rename + /* + * There's a short window of inconsistency + * during which TO does not exist. + */ + if (un_link(to) != 0 && errno != ENOENT) + return -1; +# endif + +# if has_rename + if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT)) + return -1; +# else + if (do_link(from,to) != 0 || un_link(from) != 0) + return -1; +# endif + +# if bad_NFS_rename + { + /* + * Check whether the rename falsely reported success. + * A race condition can occur between the rename and the stat. + */ + struct stat tostat; + if (stat(to, &tostat) != 0) + return -1; + if (! same_file(st, tostat, 0)) { + errno = EIO; + return -1; + } + } +# endif + +# if bad_a_rename + if (0 < set_mode && chmod(to, mode) != 0) + return -1; +# endif + + return 0; +} + + int +setmtime(file, mtime) + char const *file; + time_t mtime; +/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */ +{ + static struct utimbuf amtime; /* static so unused fields are zero */ + if (mtime == -1) + return 0; + amtime.actime = now(); + amtime.modtime = mtime; + return utime(file, &amtime); +} + + + + 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 rcslock *next, **trail, **found; + + found = 0; + for (trail = &Locks; (next = *trail); trail = &next->nextlock) + if (strcmp(getcaller(), next->login) == 0) { + if (found) { + rcserror("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 = 0; + *found = next->nextlock; + } + return 1; +} + + int +addlock(delta, verbose) + struct hshentry * delta; + int verbose; +/* + * Add a lock held by caller to DELTA and yield 1 if successful. + * Print an error message if verbose 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 rcslock *next; + + for (next = Locks; next; next = next->nextlock) + if (cmpnum(delta->num, next->delta->num) == 0) + if (strcmp(getcaller(), next->login) == 0) + return 0; + else { + if (verbose) + rcserror("Revision %s is already locked by %s.", + delta->num, next->login + ); + return -1; + } + next = ftalloc(struct rcslock); + 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 -1 if unsuccessful, 0 if no change, 1 if change. + */ +{ + register struct assoc *next; + + for (next = Symbols; next; next = next->nextassoc) + if (strcmp(name, next->symbol) == 0) + if (strcmp(next->num,num) == 0) + return 0; + else if (rebind) { + next->num = num; + return 1; + } else { + rcserror("symbolic name %s already bound to %s", + name, next->num + ); + return -1; + } + next = ftalloc(struct assoc); + next->symbol = name; + next->num = num; + next->nextassoc = Symbols; + Symbols = next; + return 1; +} + + + + 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)); + + rcserror("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 0 on success, -1 on failure. + */ +{ + int r = 0, e; + + if (lockflag) + if (changed) { + if (changed < 0) + return -1; + putadmin(); + puttree(Head, frewrite); + aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); + foutptr = frewrite; + } else { +# if bad_creat0 + int nr = !!frewrite, ne = 0; +# endif + ORCSclose(); + seteid(); + ignoreints(); +# if bad_creat0 + if (nr) { + nr = un_link(newRCSname); + ne = errno; + keepdirtemp(newRCSname); + } +# endif + r = un_link(lockname); + e = errno; + keepdirtemp(lockname); + restoreints(); + setrid(); + if (r != 0) + enerror(e, lockname); +# if bad_creat0 + if (nr != 0) { + enerror(ne, newRCSname); + r = -1; + } +# endif + } + return r; +} + + int +donerewrite(changed, newRCStime) + int changed; + time_t newRCStime; +/* + * Finish rewriting an RCS file if CHANGED is nonzero. + * Set its mode if CHANGED is positive. + * Set its modification time to NEWRCSTIME unless it is -1. + * Return 0 on success, -1 on failure. + */ +{ + int r = 0, e = 0; +# if bad_creat0 + int lr, le; +# endif + + if (changed && !nerror) { + if (finptr) { + fastcopy(finptr, frewrite); + Izclose(&finptr); + } + if (1 < RCSstat.st_nlink) + rcswarn("breaking hard link"); + aflush(frewrite); + seteid(); + ignoreints(); + r = chnamemod( + &frewrite, newRCSname, RCSname, changed, + RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), + newRCStime + ); + e = errno; + keepdirtemp(newRCSname); +# if bad_creat0 + lr = un_link(lockname); + le = errno; + keepdirtemp(lockname); +# endif + restoreints(); + setrid(); + if (r != 0) { + enerror(e, RCSname); + error("saved in %s", newRCSname); + } +# if bad_creat0 + if (lr != 0) { + enerror(le, lockname); + r = -1; + } +# endif + } + return r; +} + + void +ORCSclose() +{ + if (0 <= fdlock) { + if (close(fdlock) != 0) + efaterror(lockname); + fdlock = -1; + } + Ozclose(&frewrite); +} + + void +ORCSerror() +/* +* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. +* Do not report errors, since this may loop. This is needed only because +* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and +* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. +* This isn't a completely reliable away to work around brain-damaged hosts, +* because of the gap between actual file opening and setting frewrite etc., +* but it's better than nothing. +*/ +{ + if (0 <= fdlock) + VOID close(fdlock); + if (frewrite) + /* Avoid fclose, since stdio may not be reentrant. */ + VOID close(fileno(frewrite)); +} diff --git a/gnu/usr.bin/rcs/src/rcsfcmp.c b/gnu/usr.bin/rcs/src/rcsfcmp.c new file mode 100644 index 00000000000..1d1a366b8aa --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsfcmp.c @@ -0,0 +1,358 @@ +/* Compare working files, ignoring RCS keyword strings. */ + +/***************************************************************************** + * rcsfcmp() + * Testprogram: define FCMPTEST + ***************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + + +/* + * $Log: rcsfcmp.c,v $ + * Revision 1.1 1996/08/12 04:08:17 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.14 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.13 1995/06/01 16:23:43 eggert + * (rcsfcmp): Add -kb support. + * + * Revision 5.12 1994/03/17 14:05:48 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Calculate line numbers correctly even if the $Log prefix contains newlines. + * Remove lint. + * + * Revision 5.11 1993/11/03 17:42:27 eggert + * Fix yet another off-by-one error when comparing Log string expansions. + * + * Revision 5.10 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * 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 1.1 1996/08/12 04:08:17 millert Exp $") + + static int discardkeyval P((int,RILE*)); + 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, uname, delta) + register RILE *xfp; + struct stat const *xstatp; + char const *uname; + struct hshentry const *delta; +/* Compare the files xfp and uname. Return zero + * if xfp has the same contents as uname 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 uname, 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; + register size_t leaderlen; + int result; + enum markers match1; + struct stat ustat; + + if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) { + efaterror(uname); + } + xeof = ueof = false; + if (MIN_UNEXPAND <= Expand) { + if (!(result = xstatp->st_size!=ustat.st_size)) { +# if large_memory && maps_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. */ + leaderlen = 0; + 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 */ + int 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. Skip its header. + * The number of newlines to skip is + * 1 + (C+1)*(1+L+1), where C is the number of newlines + * in the comment leader, and L is the number of + * newlines in the log string. + */ + int c1 = 1; + for (ccnt=Comment.size; ccnt--; ) + c1 += Comment.string[ccnt] == '\n'; + lncnt = 2*c1 + 1; + while (ls--) if (*sp++=='\n') lncnt += c1; + 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....$) */ + ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen; + do { + Igeteof_(xfp, xc, goto returnresult;) + /* + * Read to the end of the comment leader or '\n', + * whatever comes first, because the leader's + * trailing white space was probably stripped. + */ + } while (ccnt-- && (xc!='\n' || --c1)); + } + } + } 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; + if (xc == '\n') + leaderlen = 0; + else + leaderlen++; + } + } + + 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/gnu/usr.bin/rcs/src/rcsfnms.c b/gnu/usr.bin/rcs/src/rcsfnms.c new file mode 100644 index 00000000000..89948c6bab7 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsfnms.c @@ -0,0 +1,1090 @@ +/* RCS filename and pathname handling */ + +/**************************************************************************** + * creation and deletion of /tmp temporaries + * pairing of RCS pathnames and working pathnames. + * Testprogram: define PAIRTEST + **************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* + * $Log: rcsfnms.c,v $ + * Revision 1.1 1996/08/12 04:08:18 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.16 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.15 1995/06/01 16:23:43 eggert + * (basefilename): Renamed from basename to avoid collisions. + * (dirlen): Remove (for similar reasons). + * (rcsreadopen): Open with FOPEN_RB. + * (SLASHSLASH_is_SLASH): Default is 0. + * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug. + * + * Revision 5.14 1994/03/17 14:05:48 eggert + * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Determine whether a file name is too long indirectly, + * by examining inode numbers, instead of trying to use operating system + * primitives like pathconf, which are not trustworthy in general. + * File names may now hold white space or $. + * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks. + * Add getabsname hook. Improve quality of diagnostics. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug. + * Check that $PWD is really ".". Be consistent about pathnames vs filenames. + * + * Revision 5.11 1992/02/17 23:02:25 eggert + * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'. + * + * Revision 5.10 1992/01/24 18:44:19 eggert + * Fix bug: Expand and Ignored weren't reinitialized. + * Avoid `char const c=ch;' compiler bug. + * Add support for bad_creat0. + * + * Revision 5.9 1992/01/06 02:42:34 eggert + * Shorten long (>31 chars) name. + * while (E) ; -> while (E) continue; + * + * Revision 5.8 1991/09/24 00:28:40 eggert + * Don't export bindex(). + * + * Revision 5.7 1991/08/19 03:13:55 eggert + * Fix messages when rcswriteopen fails. + * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune. + * + * Revision 5.6 1991/04/21 11:58:23 eggert + * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.5 1991/02/26 17:48:38 eggert + * Fix setuid bug. Support new link behavior. + * Define more portable getcwd(). + * + * Revision 5.4 1990/11/01 05:03:43 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/14 22:56:16 hammer + * added more filename extensions and their comment leaders + * + * Revision 5.2 1990/09/04 08:02:23 eggert + * Fix typo when !RCSSEP. + * + * Revision 5.1 1990/08/29 07:13:59 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:50 eggert + * Ignore signals when manipulating the semaphore file. + * Modernize list of filename extensions. + * Permit paths of arbitrary length. Beware filenames beginning with "-". + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. + * Don't use access(). Fix test for non-regular files. Tune. + * + * Revision 4.8 89/05/01 15:09:41 narten + * changed getwd to not stat empty directories. + * + * Revision 4.7 88/08/09 19:12:53 eggert + * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. + * + * Revision 4.6 87/12/18 11:40:23 narten + * additional file types added from 4.3 BSD version, and SPARC assembler + * comment character added. Also, more lint cleanups. (Guy Harris) + * + * Revision 4.5 87/10/18 10:34:16 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to verion 4.3 + * + * Revision 1.3 87/03/27 14:22:21 jenkins + * Port to suns + * + * Revision 1.2 85/06/26 07:34:28 svb + * Comment leader '% ' for '*.tex' files added. + * + * Revision 4.3 83/12/15 12:26:48 wft + * Added check for KDELIM in filenames to pairfilenames(). + * + * Revision 4.2 83/12/02 22:47:45 wft + * Added csh, red, and sl filename suffixes. + * + * Revision 4.1 83/05/11 16:23:39 wft + * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): + * 1. added copying of path from workfile to RCS file, if RCS file is omitted; + * 2. added getting the file status of RCS and working files; + * 3. added ignoring of directories. + * + * Revision 3.7 83/05/11 15:01:58 wft + * Added comtable[] which pairs filename suffixes with comment leaders; + * updated InitAdmin() accordingly. + * + * Revision 3.6 83/04/05 14:47:36 wft + * fixed Suffix in InitAdmin(). + * + * Revision 3.5 83/01/17 18:01:04 wft + * Added getwd() and rename(); these can be removed by defining + * V4_2BSD, since they are not needed in 4.2 bsd. + * Changed sys/param.h to sys/types.h. + * + * Revision 3.4 82/12/08 21:55:20 wft + * removed unused variable. + * + * Revision 3.3 82/11/28 20:31:37 wft + * Changed mktempfile() to store the generated filenames. + * Changed getfullRCSname() to store the file and pathname, and to + * delete leading "../" and "./". + * + * Revision 3.2 82/11/12 14:29:40 wft + * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), + * checksuffix(), checkfullpath(). Semaphore name generation updated. + * mktempfile() now checks for nil path; freefilename initialized properly. + * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. + * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. + * + * Revision 3.1 82/10/18 14:51:28 wft + * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). + * renamed checkpath() to checkfullpath(). + */ + + +#include "rcsbase.h" + +libId(fnmsId, "$Id: rcsfnms.c,v 1.1 1996/08/12 04:08:18 millert Exp $") + +static char const *bindex P((char const*,int)); +static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int suffix_matches P((char const*,char const*)); +static size_t dir_useful_len P((char const*)); +static size_t suffixlen P((char const*)); +static void InitAdmin P((void)); + +char const *RCSname; +char *workname; +int fdlock; +FILE *workstdout; +struct stat RCSstat; +char const *suffixes; + +static char const rcsdir[] = "RCS"; +#define rcslen (sizeof(rcsdir)-1) + +static struct buf RCSbuf, RCSb; +static int RCSerrno; + + +/* Temp names to be unlinked when done, if they are not 0. */ +#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ +static char *volatile tpnames[TEMPNAMES]; + + +struct compair { + char const *suffix, *comlead; +}; + +/* +* This table is present only for backwards compatibility. +* Normally we ignore this table, and use the prefix of the `$Log' line instead. +*/ +static struct compair const comtable[] = { + { "a" , "-- " }, /* Ada */ + { "ada" , "-- " }, + { "adb" , "-- " }, + { "ads" , "-- " }, + { "asm" , ";; " }, /* assembler (MS-DOS) */ + { "bat" , ":: " }, /* batch (MS-DOS) */ + { "body", "-- " }, /* Ada */ + { "c" , " * " }, /* C */ + { "c++" , "// " }, /* C++ in all its infinite guises */ + { "cc" , "// " }, + { "cpp" , "// " }, + { "cxx" , "// " }, + { "cl" , ";;; "}, /* Common Lisp */ + { "cmd" , ":: " }, /* command (OS/2) */ + { "cmf" , "c " }, /* CM Fortran */ + { "cs" , " * " }, /* C* */ + { "el" , "; " }, /* Emacs Lisp */ + { "f" , "c " }, /* Fortran */ + { "for" , "c " }, + { "h" , " * " }, /* C-header */ + { "hpp" , "// " }, /* C++ header */ + { "hxx" , "// " }, + { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */ + { "lisp", ";;; "}, /* Lucid Lisp */ + { "lsp" , ";; " }, /* Microsoft Lisp */ + { "m" , "// " }, /* Objective C */ + { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ + { "me" , ".\\\" "}, /* troff -me */ + { "ml" , "; " }, /* mocklisp */ + { "mm" , ".\\\" "}, /* troff -mm */ + { "ms" , ".\\\" "}, /* troff -ms */ + { "p" , " * " }, /* Pascal */ + { "pas" , " * " }, + { "ps" , "% " }, /* PostScript */ + { "spec", "-- " }, /* Ada */ + { "sty" , "% " }, /* LaTeX style */ + { "tex" , "% " }, /* TeX */ + { "y" , " * " }, /* yacc */ + { 0 , "# " } /* default for unknown suffix; must be last */ +}; + +#if has_mktemp + static char const *tmp P((void)); + static char const * +tmp() +/* Yield the name of the tmp directory. */ +{ + static char const *s; + if (!s + && !(s = cgetenv("TMPDIR")) /* Unix tradition */ + && !(s = cgetenv("TMP")) /* DOS tradition */ + && !(s = cgetenv("TEMP")) /* another DOS tradition */ + ) + s = TMPDIR; + return s; +} +#endif + + char const * +maketemp(n) + int n; +/* Create a unique pathname using n and the process id and store it + * into the nth slot in tpnames. + * Because of storage in tpnames, tempunlink() can unlink the file later. + * Return a pointer to the pathname created. + */ +{ + char *p; + char const *t = tpnames[n]; + + if (t) + return t; + + catchints(); + { +# if has_mktemp + char const *tp = tmp(); + size_t tplen = dir_useful_len(tp); + p = testalloc(tplen + 10); + VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); + if (!mktemp(p) || !*p) + faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", + (int)tplen, tp, SLASH, '0'+n + ); +# else + static char tpnamebuf[TEMPNAMES][L_tmpnam]; + p = tpnamebuf[n]; + if (!tmpnam(p) || !*p) +# ifdef P_tmpdir + faterror("can't make temporary pathname `%s...'",P_tmpdir); +# else + faterror("can't make temporary pathname"); +# endif +# endif + } + + tpnames[n] = p; + return p; +} + + void +tempunlink() +/* Clean up maketemp() files. May be invoked by signal handler. + */ +{ + register int i; + register char *p; + + for (i = TEMPNAMES; 0 <= --i; ) + if ((p = tpnames[i])) { + VOID unlink(p); + /* + * We would tfree(p) here, + * but this might dump core if we're handing a signal. + * We're about to exit anyway, so we won't bother. + */ + tpnames[i] = 0; + } +} + + + static char const * +bindex(sp, c) + register char const *sp; + register int c; +/* Function: Finds the last occurrence of character c in string sp + * and returns a pointer to the character just beyond it. If the + * character doesn't occur in the string, sp is returned. + */ +{ + register char const *r; + r = sp; + while (*sp) { + if (*sp++ == c) r=sp; + } + return r; +} + + + + static int +suffix_matches(suffix, pattern) + register char const *suffix, *pattern; +{ + register int c; + if (!pattern) + return true; + for (;;) + switch (*suffix++ - (c = *pattern++)) { + case 0: + if (!c) + return true; + break; + + case 'A'-'a': + if (ctab[c] == Letter) + break; + /* fall into */ + default: + return false; + } +} + + + static void +InitAdmin() +/* function: initializes an admin node */ +{ + register char const *Suffix; + register int i; + + Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; + StrictLocks=STRICT_LOCKING; + + /* guess the comment leader from the suffix*/ + Suffix = bindex(workname, '.'); + if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ + for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) + continue; + Comment.string = comtable[i].comlead; + Comment.size = strlen(comtable[i].comlead); + Expand = KEYVAL_EXPAND; + clear_buf(&Ignored); + Lexinit(); /* note: if !finptr, reads nothing; only initializes */ +} + + + + void +bufalloc(b, size) + register struct buf *b; + size_t size; +/* Ensure *B is a name buffer of at least SIZE bytes. + * *B's old contents can be freed; *B's new contents are undefined. + */ +{ + if (b->size < size) { + if (b->size) + tfree(b->string); + else + b->size = sizeof(malloc_type); + while (b->size < size) + b->size <<= 1; + b->string = tnalloc(char, b->size); + } +} + + void +bufrealloc(b, size) + register struct buf *b; + size_t size; +/* like bufalloc, except *B's old contents, if any, are preserved */ +{ + if (b->size < size) { + if (!b->size) + bufalloc(b, size); + else { + while ((b->size <<= 1) < size) + continue; + b->string = trealloc(char, b->string, b->size); + } + } +} + + void +bufautoend(b) + struct buf *b; +/* Free an auto buffer at block exit. */ +{ + if (b->size) + tfree(b->string); +} + + struct cbuf +bufremember(b, s) + struct buf *b; + size_t s; +/* + * Free the buffer B with used size S. + * Yield a cbuf with identical contents. + * The cbuf will be reclaimed when this input file is finished. + */ +{ + struct cbuf cb; + + if ((cb.size = s)) + cb.string = fremember(trealloc(char, b->string, s)); + else { + bufautoend(b); /* not really auto */ + cb.string = ""; + } + return cb; +} + + char * +bufenlarge(b, alim) + register struct buf *b; + char const **alim; +/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value + * of its old limit. + */ +{ + size_t s = b->size; + bufrealloc(b, s + 1); + *alim = b->string + b->size; + return b->string + s; +} + + void +bufscat(b, s) + struct buf *b; + char const *s; +/* Concatenate S to B's end. */ +{ + size_t blen = b->string ? strlen(b->string) : 0; + bufrealloc(b, blen+strlen(s)+1); + VOID strcpy(b->string+blen, s); +} + + void +bufscpy(b, s) + struct buf *b; + char const *s; +/* Copy S into B. */ +{ + bufalloc(b, strlen(s)+1); + VOID strcpy(b->string, s); +} + + + char const * +basefilename(p) + char const *p; +/* Yield the address of the base filename of the pathname P. */ +{ + register char const *b = p, *q = p; + for (;;) + switch (*q++) { + case SLASHes: b = q; break; + case 0: return b; + } +} + + + static size_t +suffixlen(x) + char const *x; +/* Yield the length of X, an RCS pathname suffix. */ +{ + register char const *p; + + p = x; + for (;;) + switch (*p) { + case 0: case SLASHes: + return p - x; + + default: + ++p; + continue; + } +} + + char const * +rcssuffix(name) + char const *name; +/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ +{ + char const *x, *p, *nz; + size_t nl, xl; + + nl = strlen(name); + nz = name + nl; + x = suffixes; + do { + if ((xl = suffixlen(x))) { + if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) + return p; + } else + for (p = name; p < nz - rcslen; p++) + if ( + isSLASH(p[rcslen]) + && (p==name || isSLASH(p[-1])) + && memcmp(p, rcsdir, rcslen) == 0 + ) + return nz; + x += xl; + } while (*x++); + return 0; +} + + /*ARGSUSED*/ RILE * +rcsreadopen(RCSpath, status, mustread) + struct buf *RCSpath; + struct stat *status; + int mustread; +/* Open RCSPATH for reading and yield its FILE* descriptor. + * If successful, set *STATUS to its status. + * Pass this routine to pairnames() for read-only access to the file. */ +{ + return Iopen(RCSpath->string, FOPEN_RB, status); +} + + static int +finopen(rcsopen, mustread) + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Set finptr to the result and yield true if successful. + * RCSb holds the file's name. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + int interesting, preferold; + + /* + * We prefer an old name to that of a nonexisting new RCS file, + * unless we tried locking the old name and failed. + */ + preferold = RCSbuf.string[0] && (mustread||0<=fdlock); + + finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); + interesting = finptr || errno!=ENOENT; + if (interesting || !preferold) { + /* Use the new name. */ + RCSerrno = errno; + bufscpy(&RCSbuf, RCSb.string); + } + return interesting; +} + + static int +fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) + char const *d, *base, *x; + size_t dlen, baselen, xlen; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * D is a directory name with length DLEN (including trailing slash). + * BASE is a filename with length BASELEN. + * X is an RCS pathname suffix with length XLEN. + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Yield true if successful. + * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. + * Put these potential names in RCSb. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + register char *p; + + bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); + + /* Try dRCS/basex. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, rcsdir, rcslen); + p += rcslen; + *p++ = SLASH; + VOID memcpy(p, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + if (xlen) { + if (finopen(rcsopen, mustread)) + return true; + + /* Try dbasex. */ + /* Start from scratch, because finopen() may have changed RCSb. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + } + return finopen(rcsopen, mustread); +} + + int +pairnames(argc, argv, rcsopen, mustread, quiet) + int argc; + char **argv; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread, quiet; +/* + * Pair the pathnames pointed to by argv; argc indicates + * how many there are. + * Place a pointer to the RCS pathname into RCSname, + * and a pointer to the pathname of the working file into workname. + * If both are given, and workstdout + * is set, a warning is printed. + * + * If the RCS file exists, places its status into RCSstat. + * + * If the RCS file exists, it is RCSOPENed for reading, the file pointer + * is placed into finptr, and the admin-node is read in; returns 1. + * If the RCS file does not exist and MUSTREAD, + * print an error unless QUIET and return 0. + * Otherwise, initialize the admin node and return -1. + * + * 0 is returned on all errors, e.g. files that are not regular files. + */ +{ + static struct buf tempbuf; + + register char *p, *arg, *RCS1; + char const *base, *RCSbase, *x; + int paired; + size_t arglen, dlen, baselen, xlen; + + fdlock = -1; + + if (!(arg = *argv)) return 0; /* already paired pathname */ + if (*arg == '-') { + error("%s option is ignored after pathnames", arg); + return 0; + } + + base = basefilename(arg); + paired = false; + + /* first check suffix to see whether it is an RCS file or not */ + if ((x = rcssuffix(arg))) + { + /* RCS pathname given */ + RCS1 = arg; + RCSbase = base; + baselen = x - base; + if ( + 1 < argc && + !rcssuffix(workname = p = argv[1]) && + baselen <= (arglen = strlen(p)) && + ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && + memcmp(base, p, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else { + bufscpy(&tempbuf, base); + workname = p = tempbuf.string; + p[baselen] = 0; + } + } else { + /* working file given; now try to find RCS file */ + workname = arg; + baselen = strlen(base); + /* Derive RCS pathname. */ + if ( + 1 < argc && + (x = rcssuffix(RCS1 = argv[1])) && + baselen <= x - RCS1 && + ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && + memcmp(base, RCSbase, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else + RCSbase = RCS1 = 0; + } + /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ + /* Second, try to find the right RCS file */ + if (RCSbase!=RCS1) { + /* a path for RCSfile is given; single RCS file to look for */ + bufscpy(&RCSbuf, RCS1); + finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); + RCSerrno = errno; + } else { + bufscpy(&RCSbuf, ""); + if (RCS1) + /* RCS filename was given without path. */ + VOID fin2open(arg, (size_t)0, RCSbase, baselen, + x, strlen(x), rcsopen, mustread + ); + else { + /* No RCS pathname was given. */ + /* Try each suffix in turn. */ + dlen = base-arg; + x = suffixes; + while (! fin2open(arg, dlen, base, baselen, + x, xlen=suffixlen(x), rcsopen, mustread + )) { + x += xlen; + if (!*x++) + break; + } + } + } + RCSname = p = RCSbuf.string; + if (finptr) { + if (!S_ISREG(RCSstat.st_mode)) { + error("%s isn't a regular file -- ignored", p); + return 0; + } + Lexinit(); getadmin(); + } else { + if (RCSerrno!=ENOENT || mustread || fdlock<0) { + if (RCSerrno == EEXIST) + error("RCS file %s is in use", p); + else if (!quiet || RCSerrno!=ENOENT) + enerror(RCSerrno, p); + return 0; + } + InitAdmin(); + }; + + if (paired && workstdout) + workwarn("Working file ignored due to -p option"); + + prevkeys = false; + return finptr ? 1 : -1; +} + + + char const * +getfullRCSname() +/* + * Return a pointer to the full pathname of the RCS file. + * Remove leading `./'. + */ +{ + if (ROOTPATH(RCSname)) { + return RCSname; + } else { + static struct buf rcsbuf; +# if needs_getabsname + bufalloc(&rcsbuf, SIZEABLE_PATH + 1); + while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0) + if (errno == ERANGE) + bufalloc(&rcsbuf, rcsbuf.size<<1); + else + efaterror("getabsname"); +# else + static char const *wdptr; + static struct buf wdbuf; + static size_t wdlen; + + register char const *r; + register size_t dlen; + register char *d; + register char const *wd; + + if (!(wd = wdptr)) { + /* Get working directory for the first time. */ + char *PWD = cgetenv("PWD"); + struct stat PWDstat, dotstat; + if (! ( + (d = PWD) && + ROOTPATH(PWD) && + stat(PWD, &PWDstat) == 0 && + stat(".", &dotstat) == 0 && + same_file(PWDstat, dotstat, 1) + )) { + bufalloc(&wdbuf, SIZEABLE_PATH + 1); +# if has_getcwd || !has_getwd + while (!(d = getcwd(wdbuf.string, wdbuf.size))) + if (errno == ERANGE) + bufalloc(&wdbuf, wdbuf.size<<1); + else if ((d = PWD)) + break; + else + efaterror("getcwd"); +# else + d = getwd(wdbuf.string); + if (!d && !(d = PWD)) + efaterror("getwd"); +# endif + } + wdlen = dir_useful_len(d); + d[wdlen] = 0; + wdptr = wd = d; + } + /* + * Remove leading `./'s from RCSname. + * Do not try to handle `../', since removing it may yield + * the wrong answer in the presence of symbolic links. + */ + for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2) + /* `.////' is equivalent to `./'. */ + while (isSLASH(r[2])) + r++; + /* Build full pathname. */ + dlen = wdlen; + bufalloc(&rcsbuf, dlen + strlen(r) + 2); + d = rcsbuf.string; + VOID memcpy(d, wd, dlen); + d += dlen; + *d++ = SLASH; + VOID strcpy(d, r); +# endif + return rcsbuf.string; + } +} + + static size_t +dir_useful_len(d) + char const *d; +/* +* D names a directory; yield the number of characters of D's useful part. +* To create a file in D, append a SLASH and a file name to D's useful part. +* Ignore trailing slashes if possible; not only are they ugly, +* but some non-Posix systems misbehave unless the slashes are omitted. +*/ +{ +# ifndef SLASHSLASH_is_SLASH +# define SLASHSLASH_is_SLASH 0 +# endif + size_t dlen = strlen(d); + if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) + --dlen; + else + while (dlen && isSLASH(d[dlen-1])) + --dlen; + return dlen; +} + +#ifndef isSLASH + int +isSLASH(c) + int c; +{ + switch (c) { + case SLASHes: + return true; + default: + return false; + } +} +#endif + + +#if !has_getcwd && !has_getwd + + char * +getcwd(path, size) + char *path; + size_t size; +{ + static char const usrbinpwd[] = "/usr/bin/pwd"; +# define binpwd (usrbinpwd+4) + + register FILE *fp; + register int c; + register char *p, *lim; + int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; + pid_t child; + + if (!size) { + errno = EINVAL; + return 0; + } + if (pipe(fd) != 0) + return 0; +# if bad_wait_if_SIGCHLD_ignored +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); +# endif + if (!(child = vfork())) { + if ( + close(fd[0]) == 0 && + (fd[1] == STDOUT_FILENO || +# ifdef F_DUPFD + (VOID close(STDOUT_FILENO), + fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) +# else + dup2(fd[1], STDOUT_FILENO) +# endif + == STDOUT_FILENO && + close(fd[1]) == 0 + ) + ) { + VOID close(STDERR_FILENO); + VOID execl(binpwd, binpwd, (char *)0); + VOID execl(usrbinpwd, usrbinpwd, (char *)0); + } + _exit(EXIT_FAILURE); + } + e = errno; + closeerror = close(fd[1]); + closeerrno = errno; + fp = 0; + readerror = toolong = wstatus = 0; + p = path; + if (0 <= child) { + fp = fdopen(fd[0], "r"); + e = errno; + if (fp) { + lim = p + size; + for (p = path; ; *p++ = c) { + if ((c=getc(fp)) < 0) { + if (feof(fp)) + break; + if (ferror(fp)) { + readerror = 1; + e = errno; + break; + } + } + if (p == lim) { + toolong = 1; + break; + } + } + } +# if has_waitpid + if (waitpid(child, &wstatus, 0) < 0) + wstatus = 1; +# else + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) { + wstatus = 1; + break; + } + } while (w != child); + } +# endif + } + if (!fp) { + VOID close(fd[0]); + errno = e; + return 0; + } + if (fclose(fp) != 0) + return 0; + if (readerror) { + errno = e; + return 0; + } + if (closeerror) { + errno = closeerrno; + return 0; + } + if (toolong) { + errno = ERANGE; + return 0; + } + if (wstatus || p == path || *--p != '\n') { + errno = EACCES; + return 0; + } + *p = '\0'; + return path; +} +#endif + + +#ifdef PAIRTEST +/* test program for pairnames() and getfullRCSname() */ + +char const cmdid[] = "pair"; + +main(argc, argv) +int argc; char *argv[]; +{ + int result; + int initflag; + quietflag = initflag = false; + + while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'p': workstdout = stdout; + break; + case 'i': initflag=true; + break; + case 'q': quietflag=true; + break; + default: error("unknown option: %s", *argv); + break; + } + } + + do { + RCSname = workname = 0; + result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); + if (result!=0) { + diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", + RCSname, workname, getfullRCSname() + ); + } + switch (result) { + case 0: continue; /* already paired file */ + + case 1: if (initflag) { + rcserror("already exists"); + } else { + diagnose("RCS file %s exists\n", RCSname); + } + Ifclose(finptr); + break; + + case -1:diagnose("RCS file doesn't exist\n"); + break; + } + + } while (++argv, --argc>=1); + +} + + void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} +#endif diff --git a/gnu/usr.bin/rcs/src/rcsfreeze.sh b/gnu/usr.bin/rcs/src/rcsfreeze.sh new file mode 100644 index 00000000000..0f0cedc89a1 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsfreeze.sh @@ -0,0 +1,99 @@ +#! /bin/sh + +# rcsfreeze - assign a symbolic revision number to a configuration of RCS files + +# $Id: rcsfreeze.sh,v 1.1 1996/08/12 04:08:19 millert 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 test -d RCS +then RCSDIR=RCS/ EXT= +else RCSDIR= EXT=,v +fi + +# Version number stuff, log message file +VERSIONFILE=${RCSDIR}.rcsfreeze.ver +LOGFILE=${RCSDIR}.rcsfreeze.log +# Initialize, rcsfreeze never run before in the current directory +test -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 && + +# Now the real work begins by assigning a symbolic revision number +# to each rcs file. Take the most recent version on the default branch. + +# If there are any .*,v files, throw them in too. +# But ignore RCS/.* files that do not end in ,v. +DOTFILES= +for DOTFILE in ${RCSDIR}.*,v +do + if test -f "$DOTFILE" + then + DOTFILES="${RCSDIR}.*,v" + break + fi +done + +exec rcs -q -n$SYMREVNAME: ${RCSDIR}*$EXT $DOTFILES diff --git a/gnu/usr.bin/rcs/src/rcsgen.c b/gnu/usr.bin/rcs/src/rcsgen.c new file mode 100644 index 00000000000..99d080d0257 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsgen.c @@ -0,0 +1,685 @@ +/* Generate RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsgen.c,v $ + * Revision 1.1 1996/08/12 04:08:19 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.16 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.15 1995/06/01 16:23:43 eggert + * (putadmin): Open RCS file with FOPEN_WB. + * + * Revision 5.14 1994/03/17 14:05:48 eggert + * Work around SVR4 stdio performance bug. + * Flush stderr after prompt. Remove lint. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * Be consistent about pathnames vs filenames. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Move put routines here from rcssyn.c. + * Add support for bad_creat0. + * + * 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 1.1 1996/08/12 04:08:19 millert 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 putdelta P((struct hshentry const*,FILE*)); +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 0 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 (resultname and + * editname). 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 resultname; + } + } 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*)0, outfile, false); + } + scandeltatext(target, expandflag?edit_expand:edit, true); + finishedit( + expandflag ? target : (struct hshentry*)0, + outfile, true + ); + if (outfile) + return 0; + Ozclose(&fcopy); + return resultname; + } +} + + + + 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. + * Similarly, if needlog, delta->igtext is set to the ignored phrases. + * 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); + nextlex(); + delta->igtext = getphrases(Ktext); + } else {readstring(); + ignorephrases(Ktext); + } + 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 *)0); 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 == EOF) { + 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 textfile, the text is read from that + * file, or from stdin, if !textfile. + * A textfile with a leading '-' is treated as a string, not a pathname. + * 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 = fopenSafer(textfile, "r"))) + efaterror(textfile); + bufalloc(&desc, 1); + p = desc.string; + plim = p + desc.size; + for (;;) { + if ((c=getc(txt)) == EOF) { + 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 + ); + eflush(); + } else if (feof(stdin)) + rcsfaterror("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); + eflush(); + } + return cleanlogmsg(p, i); +} + + + void +putadmin() +/* Output the admin node. */ +{ + register FILE *fout; + struct assoc const *curassoc; + struct rcslock const *curlock; + struct access const *curaccess; + + if (!(fout = frewrite)) { +# if bad_creat0 + ORCSclose(); + fout = fopenSafer(makedirtemp(0), FOPEN_WB); +# else + int fo = fdlock; + fdlock = -1; + fout = fdopen(fo, FOPEN_WB); +# endif + + if (!(frewrite = fout)) + efaterror(RCSname); + } + + /* + * Output the first character with putc, not printf. + * Otherwise, an SVR4 stdio bug buffers output inefficiently. + */ + aputc_(*Khead, fout) + aprintf(fout, "%s\t%s;\n", Khead + 1, 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; +/* Output the delta NODE to FOUT. */ +{ + struct branchhead const *nextbranch; + + if (!node) 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; +/* Output the delta tree with base ROOT in preorder to FOUT. */ +{ + struct branchhead const *nextbranch; + + if (!root) return; + + if (root->selector) + putdelta(root, fout); + + puttree(root->next, fout); + + nextbranch = root->branches; + while (nextbranch) { + puttree(nextbranch->hsh, fout); + nextbranch = nextbranch->nextbranch; + } +} + + + int +putdtext(delta, srcname, fout, diffmt) + struct hshentry const *delta; + char const *srcname; + FILE *fout; + int diffmt; +/* + * Output a deltatext node with delta number DELTA->num, log message DELTA->log, + * ignored phrases DELTA->igtext and text SRCNAME to FOUT. + * Double up all SDELIMs in both the log and the text. + * Make sure the log message ends in \n. + * Return false on error. + * If DIFFMT, also check that the text is valid diff -n output. + */ +{ + RILE *fin; + if (!(fin = Iopen(srcname, "r", (struct stat*)0))) { + eerror(srcname); + return false; + } + putdftext(delta, fin, fout, diffmt); + Ifclose(fin); + return true; +} + + 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) +} + + void +putdftext(delta, finfile, foutfile, diffmt) + struct hshentry const *delta; + 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, delta->num, Klog); + + /* put log */ + putstring(fout, true, delta->log, true); + aputc_('\n', fout) + + /* put ignored phrases */ + awrite(delta->igtext.string, delta->igtext.size, fout); + + /* put text */ + aprintf(fout, "%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); +} diff --git a/gnu/usr.bin/rcs/src/rcskeep.c b/gnu/usr.bin/rcs/src/rcskeep.c new file mode 100644 index 00000000000..17749365118 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcskeep.c @@ -0,0 +1,456 @@ +/* Extract RCS keyword string values from working files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcskeep.c,v $ + * Revision 1.1 1996/08/12 04:08:20 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.10 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.9 1995/06/01 16:23:43 eggert + * (getoldkeys): Don't panic if a Name: is empty. + * + * Revision 5.8 1994/03/17 14:05:48 eggert + * Remove lint. + * + * Revision 5.7 1993/11/09 17:40:15 eggert + * Use simpler timezone parsing strategy now that we're using ISO 8601 format. + * + * Revision 5.6 1993/11/03 17:42:27 eggert + * Scan for Name keyword. Improve quality of diagnostics. + * + * Revision 5.5 1992/07/28 16:12:44 eggert + * Statement macro names now end in _. + * + * 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. + * + */ + +#include "rcsbase.h" + +libId(keepId, "$Id: rcskeep.c,v 1.1 1996/08/12 04:08:20 millert Exp $") + +static int badly_terminated P((void)); +static int checknum P((char const*)); +static int get0val P((int,RILE*,struct buf*,int)); +static int getval P((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, prevname, 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 fp is null, workname is opened and closed instead of using fp. + * The results are placed into + * prevauthor, prevdate, prevname, 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; + int prevname_found; + + if (prevkeys) + return true; + + needs_closing = false; + if (!fp) { + if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { + eerror(workname); + return false; + } + needs_closing = true; + } + + /* initialize to empty */ + bufscpy(&prevauthor, ""); + bufscpy(&prevdate, ""); + bufscpy(&prevname, ""); prevname_found = 0; + 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: + case LocalId: + if (!( + getval(fp, (struct buf*)0, 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*)0, true) && + getval(fp, (struct buf*)0, true)) + c = 0; + else if (nerror) + return false; + else + c = KDELIM; + break; + case Locker: + (void) getval(fp, (struct buf*)0, false); + c = 0; + break; + case Log: + case RCSfile: + case Source: + if (!getval(fp, (struct buf*)0, false)) + return false; + c = 0; + break; + case Name: + if (getval(fp, &prevname, false)) { + if (*prevname.string) + checkssym(prevname.string); + prevname_found = 1; + } + 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) { + workerror("closing %c missing on keyword", KDELIM); + return false; + } + if (prevname_found && + *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() +{ + workerror("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 0. + * 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 + } + 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; + register int c; + + c = 0; + bufautobegin(&prevday); + if (getval(fp,&prevday,false)) { + bufautobegin(&prevtime); + if (getval(fp,&prevtime,false)) { + Igeteof_(fp, c, c=0;) + if (c) { + register char const *d = prevday.string, *t = prevtime.string; + bufalloc(&prevdate, strlen(d) + strlen(t) + 9); + VOID sprintf(prevdate.string, "%s%s %s%s", + /* Parse dates put out by old versions of RCS. */ + isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) + ? "19" : "", + d, t, + strchr(t,'-') || strchr(t,'+') ? "" : "+0000" + ); + } + } + 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 !nerror; +} + + static int +keeprev(fp) + RILE *fp; +/* Get previous revision from FP into prevrev. */ +{ + return getval(fp,&prevrev,false) && checknum(prevrev.string); +} + + + static int +checknum(s) + char const *s; +{ + register char const *sp; + register int dotcount = 0; + for (sp=s; ; sp++) { + switch (*sp) { + case 0: + if (dotcount & 1) + return true; + else + break; + + case '.': + dotcount++; + continue; + + default: + if (isdigit(*sp)) + continue; + break; + } + break; + } + workerror("%s is not a revision number", s); + return false; +} + + + +#ifdef KEEPTEST + +/* Print the keyword values found. */ + +char const cmdid[] ="keeptest"; + + int +main(argc, argv) +int argc; char *argv[]; +{ + while (*(++argv)) { + workname = *argv; + getoldkeys((RILE*)0); + VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n", + *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string); + } + exitmain(EXIT_SUCCESS); +} +#endif diff --git a/gnu/usr.bin/rcs/src/rcskeys.c b/gnu/usr.bin/rcs/src/rcskeys.c new file mode 100644 index 00000000000..704fd77cef8 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcskeys.c @@ -0,0 +1,121 @@ +/* RCS keyword table and match operation */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcskeys.c,v $ + * Revision 1.1 1996/08/12 04:08:22 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.4 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.3 1993/11/03 17:42:27 eggert + * Add Name keyword. + * + * 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 1.1 1996/08/12 04:08:22 millert Exp $") + + +char const *Keyword[] = { + /* This must be in the same order as rcsbase.h's enum markers type. */ + 0, + AUTHOR, DATE, HEADER, IDH, + LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, NULL +}; + + + 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); +} + + + void +setRCSlocalId(string) + char const *string; +/* function: sets local RCS id and RCSLOCALID envariable */ +{ + static char local_id[keylength+1]; + + if (strlen(string) > keylength) + error("LocalId is too long"); + VOID strcpy(local_id, string); + Keyword[LocalId] = local_id; +} diff --git a/gnu/usr.bin/rcs/src/rcslex.c b/gnu/usr.bin/rcs/src/rcslex.c new file mode 100644 index 00000000000..1eed5642869 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcslex.c @@ -0,0 +1,1572 @@ +/* lexical analysis of RCS files */ + +/****************************************************************************** + * Lexical Analysis. + * hashtable, Lexinit, nextlex, getlex, getkey, + * getid, getnum, readstring, printstring, savestring, + * checkid, fatserror, error, faterror, warn, diagnose + * Testprogram: define LEXDB + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* + * $Log: rcslex.c,v $ + * Revision 1.1 1996/08/12 04:08:22 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate): + * New functions. + * (Iclose): If large_memory and maps_memory, use them to deallocate mapping. + * (fd2RILE): Use map_fd if available. + * If one mapping method fails, try the next instead of giving up; + * if they all fail, fall back on ordinary read. + * Work around bug: root mmap over NFS succeeds, but accessing dumps core. + * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. + * (advise_access): Use madvise only if this instance used mmap. + * (Iopen): Use fdSafer to get safer file descriptor. + * (aflush): Moved here from rcsedit.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Don't worry if madvise fails. Add Orewind. Remove lint. + * + * Revision 5.16 1993/11/09 17:55:29 eggert + * Fix `label: }' typo. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Improve quality of diagnostics by putting file names in them more often. + * Don't discard ignored phrases. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Identifiers may now start with a digit and (unless they are symbolic names) + * may contain `.'. Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.13 1992/02/17 23:02:27 eggert + * Work around NFS mmap SIGBUS problem. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Use OPEN_O_BINARY if mode contains 'b'. + * + * 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 1.1 1996/08/12 04:08:22 millert Exp $") + +static char *checkidentifier P((char*,int,int)); +static void errsay P((char const*)); +static void fatsay P((char const*)); +static void lookup P((char const*)); +static void startsay P((const char*,const char*)); +static void warnsay P((char const*)); + +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 */ + +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) { + ignored_phrases = true; + rcswarn("Unknown phrases like `%s ...;' are present.", 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 = 0; +# 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] = 0; + } + + 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 IDCHAR: + case LETTER: + case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: + sp = tokbuf.string; + limit = sp + tokbuf.size; + *sp++ = c; + for (;;) { + GETC_(frew, c) + switch (ctab[c]) { + case IDCHAR: + case LETTER: + case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: + *sp++ = c; + if (limit <= sp) + sp = bufenlarge(&tokbuf, &limit); + continue; + + default: + break; + } + break; + } + *sp = 0; + if (d == DIGIT || d == PERIOD) { + d = NUM; + if (hshenter) { + lookup(tokbuf.string); + break; + } + } + NextString = fstr_save(tokbuf.string); + 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 0. + * Treats keywords as identifiers. + */ +{ + register char const *name; + if (nexttok==ID) { + name = NextString; + nextlex(); + return name; + } else + return 0; +} + + +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 0. + * Doesn't work if hshenter is false. + */ +{ + register struct hshentry * num; + if (nexttok==NUM) { + num=nexthsh; + nextlex(); + return num; + } else + return 0; +} + + 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. Copy input to foutptr if it is set. Unlike ignorephrases(), +* this routine assumes nextlex() has already been invoked before we start. +*/ +{ + declarecache; + register int c; + register char const *kn; + struct cbuf r; + register RILE *fin; + register FILE *frew; +# if large_memory +# define savech_(c) ; +# else + register char *p; + char const *limit; + struct buf b; +# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);} +# endif + + if (nexttok!=ID || strcmp(NextString,key) == 0) + clear_buf(&r); + else { + warnignore(); + fin = finptr; + frew = foutptr; + setupcache(fin); cache(fin); +# if large_memory + r.string = (char const*)cacheptr() - strlen(NextString) - 1; +# else + bufautobegin(&b); + bufscpy(&b, NextString); + p = b.string + strlen(b.string); + limit = b.string + b.size; +# endif + ffree1(NextString); + c = nextc; + for (;;) { + for (;;) { + savech_(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: + GETC_(frew, c) + continue; + case SBEGIN: /* long string */ + for (;;) { + for (;;) { + GETC_(frew, c) + savech_(c) + switch (c) { + case '\n': + ++rcsline; + /* fall into */ + default: + continue; + + case SDELIM: + break; + } + break; + } + GETC_(frew, c) + if (c != SDELIM) + break; + savech_(c) + } + continue; + case SEMI: + cacheget_(c) + if (ctab[c] == NEWLN) { + if (frew) + aputc_(c, frew) + ++rcsline; + savech_(c) + cacheget_(c) + } +# if large_memory + r.size = (char const*)cacheptr() - 1 - r.string; +# endif + for (;;) { + switch (ctab[c]) { + case NEWLN: + ++rcsline; + /* fall into */ + case SPACE: + cacheget_(c) + continue; + + default: break; + } + break; + } + if (frew) + aputc_(c, frew) + break; + } + break; + } + if (ctab[c] == Letter) { + for (kn = key; c && *kn==c; kn++) + GETC_(frew, c) + if (!*kn) + switch (ctab[c]) { + case DIGIT: case LETTER: case Letter: + case IDCHAR: case PERIOD: + break; + default: + nextc = c; + NextString = fstr_save(key); + nexttok = ID; + uncache(fin); + goto returnit; + } +# if !large_memory + { + register char const *ki; + for (ki=key; ki<kn; ) + savech_(*ki++) + } +# endif + } else { + nextc = c; + uncache(fin); + nextlex(); + break; + } + } + returnit:; +# if !large_memory + return bufremember(&b, (size_t)(p - b.string)); +# endif + } + return r; +} + + + 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; + } +} + + + static char * +checkidentifier(id, delimiter, dotok) + register char *id; + int delimiter; + register int dotok; +/* 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. */ +/* Allow '.' in identifier only if DOTOK is set. */ +{ + register char *temp; + register char c; + register char delim = delimiter; + int isid = false; + + temp = id; + for (;; id++) { + switch (ctab[(unsigned char)(c = *id)]) { + case IDCHAR: + case LETTER: + case Letter: + isid = true; + continue; + + case DIGIT: + continue; + + case PERIOD: + if (dotok) + continue; + break; + + default: + break; + } + break; + } + if ( ! isid + || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n'))) + ) { + /* append \0 to end of id before error message */ + while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) + id++; + *id = '\0'; + faterror("invalid %s `%s'", + dotok ? "identifier" : "symbol", temp + ); + } + return id; +} + + char * +checkid(id, delimiter) + char *id; + int delimiter; +{ + return checkidentifier(id, delimiter, true); +} + + char * +checksym(sym, delimiter) + char *sym; + int delimiter; +{ + return checkidentifier(sym, delimiter, false); +} + + void +checksid(id) + char *id; +/* Check whether the string ID is an identifier. */ +{ + VOID checkid(id, 0); +} + + void +checkssym(sym) + char *sym; +{ + VOID checksym(sym, 0); +} + + +#if !large_memory +# define Iclose(f) fclose(f) +#else +# if !maps_memory + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + tfree(f->base); + f->base = 0; + return fclose(f->stream); + } +# else + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + (* f->deallocate) (f); + f->base = 0; + return close(f->fd); + } + +# if has_map_fd + static void map_fd_deallocate P((RILE *)); + static void + map_fd_deallocate(f) + register RILE *f; + { + if (vm_deallocate( + task_self(), + (vm_address_t) f->base, + (vm_size_t) (f->lim - f->base) + ) != KERN_SUCCESS) + efaterror("vm_deallocate"); + } +# endif +# if has_mmap + static void mmap_deallocate P((RILE *)); + static void + mmap_deallocate(f) + register RILE *f; + { + if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0) + efaterror("munmap"); + } +# endif + static void read_deallocate P((RILE *)); + static void + read_deallocate(f) + RILE *f; + { + tfree(f->base); + } + + static void nothing_to_deallocate P((RILE *)); + static void + nothing_to_deallocate(f) + RILE *f; + { + } +# endif +#endif + + +#if large_memory && maps_memory + static RILE *fd2_RILE P((int,char const*,struct stat*)); + static RILE * +fd2_RILE(fd, name, status) +#else + static RILE *fd2RILE P((int,char const*,char const*,struct stat*)); + static RILE * +fd2RILE(fd, name, type, status) + char const *type; +#endif + int fd; + char const *name; + register struct stat *status; +{ + struct stat st; + + if (!status) + status = &st; + if (fstat(fd, status) != 0) + efaterror(name); + if (!S_ISREG(status->st_mode)) { + error("`%s' is not a regular file", name); + VOID close(fd); + errno = EINVAL; + return 0; + } else { + +# if !(large_memory && maps_memory) + FILE *stream; + if (!(stream = fdopen(fd, type))) + efaterror(name); +# 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: too large", name); + for (f = rilebuf; f->base; f++) + if (f == rilebuf+RILES) + faterror("too many RILEs"); +# if maps_memory + f->deallocate = nothing_to_deallocate; +# endif + if (!s) { + static unsigned char nothing; + f->base = ¬hing; /* Any nonzero address will do. */ + } else { + f->base = 0; +# if has_map_fd + map_fd( + fd, (vm_offset_t)0, (vm_address_t*) &f->base, + TRUE, (vm_size_t)s + ); + f->deallocate = map_fd_deallocate; +# endif +# if has_mmap + if (!f->base) { + catchmmapints(); + f->base = (unsigned char *) mmap( + (char *)0, s, PROT_READ, MAP_SHARED, + fd, (off_t)0 + ); +# ifndef MAP_FAILED +# define MAP_FAILED (-1) +# endif + if (f->base == (unsigned char *) MAP_FAILED) + f->base = 0; + else { +# if has_NFS && mmap_signal + /* + * On many hosts, the superuser + * can mmap an NFS file it can't read. + * So access the first page now, and print + * a nice message if a bus error occurs. + */ + readAccessFilenameBuffer(name, f->base); +# endif + } + f->deallocate = mmap_deallocate; + } +# endif + if (!f->base) { + f->base = tnalloc(unsigned char, s); +# if maps_memory + { + /* + * We can't map the file into memory for some reason. + * Read it into main memory all at once; this is + * the simplest substitute for memory mapping. + */ + char *bufptr = (char *) f->base; + size_t bufsiz = s; + do { + ssize_t r = read(fd, bufptr, bufsiz); + switch (r) { + case -1: + efaterror(name); + + case 0: + /* The file must have shrunk! */ + status->st_size = s -= bufsiz; + bufsiz = 0; + break; + + default: + bufptr += r; + bufsiz -= r; + break; + } + } while (bufsiz); + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + efaterror(name); + f->deallocate = read_deallocate; + } +# endif + } + } + f->ptr = f->base; + f->lim = f->base + s; + f->fd = fd; +# if !maps_memory + f->readlim = f->base; + f->stream = stream; +# endif + if_advise_access(s, f, MADV_SEQUENTIAL); + return f; + } +# endif + } +} + +#if !maps_memory && 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 (f->deallocate == mmap_deallocate) + VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice); + /* Don't worry if madvise fails; it's only advisory. */ +} +#endif + + RILE * +#if large_memory && maps_memory +I_open(name, status) +#else +Iopen(name, type, status) + char const *type; +#endif + char const *name; + struct stat *status; +/* Open NAME for reading, yield its descriptor, and set *STATUS. */ +{ + int fd = fdSafer(open(name, O_RDONLY +# if OPEN_O_BINARY + | (strchr(type,'b') ? OPEN_O_BINARY : 0) +# endif + )); + + if (fd < 0) + return 0; +# if large_memory && maps_memory + return fd2_RILE(fd, name, status); +# else + return fd2RILE(fd, name, type, status); +# endif +} + + +static int Oerrloop; + + void +Oerror() +{ + if (Oerrloop) + exiterr(); + Oerrloop = true; + efaterror("output error"); +} + +void Ieof() { fatserror("unexpected end of file"); } +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 Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); } + +void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); } +void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); } +void oflush() +{ + if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) + Oerror(); +} + + void +fatcleanup(already_newline) + int already_newline; +{ + VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); + exiterr(); +} + + static void +startsay(s, t) + const char *s, *t; +{ + oflush(); + if (s) + aprintf(stderr, "%s: %s: %s", cmdid, s, t); + else + aprintf(stderr, "%s: %s", cmdid, t); +} + + static void +fatsay(s) + char const *s; +{ + startsay(s, ""); +} + + static void +errsay(s) + char const *s; +{ + fatsay(s); + nerror++; +} + + static void +warnsay(s) + char const *s; +{ + startsay(s, "warning: "); +} + +void eerror(s) char const *s; { enerror(errno,s); } + + void +enerror(e,s) + int e; + char const *s; +{ + errsay((char const*)0); + errno = e; + perror(s); + eflush(); +} + +void efaterror(s) char const *s; { enfaterror(errno,s); } + + void +enfaterror(e,s) + int e; + char const *s; +{ + fatsay((char const*)0); + 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((char const*)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +rcserror(char const *format,...) +#else + /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal RCS file error */ +{ + va_list args; + errsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +workerror(char const *format,...) +#else + /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal working file error */ +{ + va_list args; + errsay(workname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +fatserror(char const *format,...) +#else + /*VARARGS1*/ void + fatserror(format, va_alist) char const *format; va_dcl +#endif +/* fatal RCS file syntax error */ +{ + va_list args; + oflush(); + VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +faterror(char const *format,...) +#else + /*VARARGS1*/ void faterror(format, va_alist) + char const *format; va_dcl +#endif +/* fatal error, terminates program after cleanup */ +{ + va_list args; + fatsay((char const*)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +rcsfaterror(char const *format,...) +#else + /*VARARGS1*/ void rcsfaterror(format, va_alist) + char const *format; va_dcl +#endif +/* fatal RCS file error, terminates program after cleanup */ +{ + va_list args; + fatsay(RCSname); + 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 +/* warning */ +{ + va_list args; + if (!quietflag) { + warnsay((char *)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +rcswarn(char const *format,...) +#else + /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl +#endif +/* RCS file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +workwarn(char const *format,...) +#else + /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl +#endif +/* working file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(workname); + 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) +/* 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) + Oerror(); +#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)) + Oerror(); +#endif +} + +#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); +} + +void exiterr() { _exit(EXIT_FAILURE); } + + +#endif diff --git a/gnu/usr.bin/rcs/src/rcsmap.c b/gnu/usr.bin/rcs/src/rcsmap.c new file mode 100644 index 00000000000..8f292237859 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsmap.c @@ -0,0 +1,69 @@ +/* RCS map of character types */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mapId, "$Id: rcsmap.c,v 1.1 1996/08/12 04:08:24 millert 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/gnu/usr.bin/rcs/src/rcsmerge.c b/gnu/usr.bin/rcs/src/rcsmerge.c new file mode 100644 index 00000000000..e4d2da08a12 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsmerge.c @@ -0,0 +1,290 @@ +/* Merge RCS revisions. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsmerge.c,v $ + * Revision 1.1 1996/08/12 04:08:24 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.15 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.14 1995/06/01 16:23:43 eggert + * (main): Report an error if -kb, so don't worry about binary stdout. + * Punctuate messages properly. Rewrite to avoid `goto end'. + * + * Revision 5.13 1994/03/17 14:05:48 eggert + * Specify subprocess input via file descriptor, not file name. Remove lint. + * + * Revision 5.12 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.11 1993/11/03 17:42:27 eggert + * Add -A, -E, -e, -z. Ignore -T. Allow up to three file labels. + * Pass -Vn to `co'. Pass unexpanded revision name to `co', so that Name works. + * + * Revision 5.10 1992/07/28 16:12:44 eggert + * Add -V. + * + * Revision 5.9 1992/01/24 18:44:19 eggert + * lint -> RCS_lint + * + * Revision 5.8 1992/01/06 02:42:34 eggert + * Update usage string. + * + * 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 1.1 1996/08/12 04:08:24 millert Exp $") +{ + static char const cmdusage[] = + "\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] -ksubst -{pq}[rev] -Vn -xsuff -zzone file"; + static char const quietarg[] = "-q"; + + register int i; + char *a, **newargv; + char const *arg[3]; + char const *rev[3], *xrev[3]; /*revision numbers*/ + char const *edarg, *expandarg, *suffixarg, *versionarg, *zonearg; + 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); + edarg = rev[1] = rev[2] = 0; + status = 0; /* Keep lint happy. */ + tostdout = false; + expandarg = suffixarg = versionarg = zonearg = quietarg; /* 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[1]) + rev[1] = a; + else if (!rev[2]) + rev[2] = a; + else + error("too many revision numbers"); + break; + + case 'A': case 'E': case 'e': + if (*a) + goto unknown; + edarg = *argv; + break; + + case 'x': + suffixarg = *argv; + suffixes = a; + break; + case 'z': + zonearg = *argv; + zone_set(a); + break; + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + break; + + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + break; + /* fall into */ + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + }; + } /* end of option processing */ + + if (!rev[1]) faterror("no base revision number given"); + + /* Now handle all pathnames. */ + + if (!nerror) { + if (argc < 1) + faterror("no input file%s", cmdusage); + if (0 < pairnames(argc, argv, rcsreadopen, true, false)) { + + if (argc>2 || (argc==2 && argv[1])) + warn("excess arguments ignored"); + if (Expand == BINARY_EXPAND) + workerror("merging binary files"); + diagnose("RCS file: %s\n", RCSname); + if (!(workptr = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) + efaterror(workname); + + gettree(); /* reads in the delta tree */ + + if (!Head) rcsfaterror("no revisions present"); + + if (!*rev[1]) + rev[1] = Dbranch ? Dbranch : Head->num; + if (fexpandsym(rev[1], &numericrev, workptr) + && (target=genrevs(numericrev.string, (char *)0, (char *)0, (char*)0, &gendeltas)) + ) { + xrev[1] = target->num; + if (!rev[2] || !*rev[2]) + rev[2] = Dbranch ? Dbranch : Head->num; + if (fexpandsym(rev[2], &numericrev, workptr) + && (target=genrevs(numericrev.string, (char *)0, (char *)0, (char *)0, &gendeltas)) + ) { + xrev[2] = target->num; + + if (strcmp(xrev[1],xrev[2]) == 0) { + if (tostdout) { + fastcopy(workptr, stdout); + Ofclose(stdout); + } + } else { + Izclose(&workptr); + + for (i=1; i<=2; i++) { + diagnose("retrieving revision %s\n", xrev[i]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev[i]); /* not xrev[i], for $Name's sake */ + if (run( + -1, + /* Do not collide with merger.c maketemp(). */ + arg[i] = maketemp(i+2), + co, quietarg, commarg.string, + expandarg, suffixarg, versionarg, zonearg, + RCSname, (char*)0 + )) + rcsfaterror("co failed"); + } + diagnose("Merging differences between %s and %s into %s%s\n", + xrev[1], xrev[2], workname, + tostdout?"; result to stdout":""); + + arg[0] = xrev[0] = workname; + status = merge(tostdout, edarg, xrev, arg); + } + } + } + + Izclose(&workptr); + } + } + tempunlink(); + exitmain(nerror ? DIFF_TROUBLE : status); +} + +#if RCS_lint +# define exiterr rmergeExit +#endif + void +exiterr() +{ + tempunlink(); + _exit(DIFF_TROUBLE); +} diff --git a/gnu/usr.bin/rcs/src/rcsrev.c b/gnu/usr.bin/rcs/src/rcsrev.c new file mode 100644 index 00000000000..5ce4ea11745 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsrev.c @@ -0,0 +1,915 @@ +/* Handle RCS revision numbers. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcsrev.c,v $ + * Revision 1.1 1996/08/12 04:08:25 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.10 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.9 1995/06/01 16:23:43 eggert + * (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility. + * (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug. + * (genrevs, genbranch): cmpnum -> cmpdate + * + * Revision 5.8 1994/03/17 14:05:48 eggert + * Remove lint. + * + * Revision 5.7 1993/11/09 17:40:15 eggert + * Fix format string typos. + * + * Revision 5.6 1993/11/03 17:42:27 eggert + * Revision number `.N' now stands for `D.N', where D is the default branch. + * Add -z. Improve quality of diagnostics. Add `namedrev' for Name support. + * + * Revision 5.5 1992/07/28 16:12:44 eggert + * Identifiers may now start with a digit. Avoid `unsigned'. + * + * Revision 5.4 1992/01/06 02:42:34 eggert + * while (E) ; -> while (E) continue; + * + * 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. + */ + +#include "rcsbase.h" + +libId(revId, "$Id: rcsrev.c,v 1.1 1996/08/12 04:08:25 millert Exp $") + +static char const *branchtip P((char const*)); +static char const *lookupsym P((char const*)); +static char const *normalizeyear P((char const*,char[5])); +static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**)); +static void absent P((char const*,int)); +static void cantfindbranch P((char const*,char const[datesize],char const*,char const*)); +static void store1 P((struct hshentries***,struct hshentry*)); + + + + int +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 int count; + if (!(sp=s) || !*sp) + return 0; + count = 1; + do { + if (*sp++ == '.') count++; + } while (*sp); + return(count); +} + + void +getbranchno(revno,branchno) + char const *revno; + struct buf *branchno; +/* Given a 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 int numflds; + register char *tp; + + bufscpy(branchno, revno); + numflds=countnumflds(revno); + if (!(numflds & 1)) { + tp = branchno->string; + while (--numflds) + while (*tp++ != '.') + continue; + *(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 ? num1 : ""; + s2 = num2 ? 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; + while (*s2=='0') ++s2; + for (d1=0; isdigit(*(s1+d1)); d1++) continue; + for (d2=0; isdigit(*(s2+d2)); d2++) continue; + + /* 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; + int 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++ != '.') + continue; + while (*s2++ != '.') + continue; + } + /* Now s1 and s2 point to the beginning of the respective fields */ + while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue; + while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue; + + return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1; +} + + + int +cmpdate(d1, d2) + char const *d1, *d2; +/* +* Compare the two dates. This is just like cmpnum, +* except that for compatibility with old versions of RCS, +* 1900 is added to dates with two-digit years. +*/ +{ + char year1[5], year2[5]; + int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1); + + if (r) + return r; + else { + while (isdigit(*d1)) d1++; d1 += *d1=='.'; + while (isdigit(*d2)) d2++; d2 += *d2=='.'; + return cmpnum(d1, d2); + } +} + + static char const * +normalizeyear(date, year) + char const *date; + char year[5]; +{ + if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) { + year[0] = '1'; + year[1] = '9'; + year[2] = date[0]; + year[3] = date[1]; + year[4] = 0; + return year; + } else + return date; +} + + + static void +cantfindbranch(revno, date, author, state) + char const *revno, date[datesize], *author, *state; +{ + char datebuf[datesize + zonelenmax]; + + rcserror("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; + int field; +{ + struct buf t; + bufautobegin(&t); + rcserror("%s %s absent", field&1?"revision":"branch", + partialno(&t,revno,field) + ); + bufautoend(&t); +} + + + int +compartial(num1, num2, length) + char const *num1, *num2; + int 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++) continue; + while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue; + + if (d1 != d2) + return d1<d2 ? -1 : 1; + if ((r = memcmp(s1, s2, d1))) + return r; + if (!--length) + return 0; + + s1 += d1; + s2 += d1; + + if (*s1 == '.') s1++; + if (*s2 == '.') s2++; + } +} + + +char * partialno(rev1,rev2,length) + struct buf *rev1; + char const *rev2; + register int 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, 0 is returned. + */ +{ + int length; + register struct hshentry * next; + int result; + char const *branchnum; + struct buf t; + char datebuf[datesize + zonelenmax]; + + bufautobegin(&t); + + if (!(next = Head)) { + rcserror("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) { + rcserror("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 && + cmpnumfld(branchnum,next->num,1) == 0 && + ( + (date && cmpdate(date,next->date) < 0) || + (author && strcmp(author,next->author) != 0) || + (state && strcmp(state,next->state) != 0) + ) + ) + { + store1(&store, next); + next=next->next; + } + if (!next || + (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { + cantfindbranch( + length ? revno : partialno(&t,branchnum,1), + date, author, state + ); + goto norev; + } else { + store1(&store, next); + } + *store = 0; + 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 || cmpnumfld(revno,next->num,1) != 0) { + rcserror("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 && cmpdate(date,next->date)<0) { + rcserror("Revision %s has date %s.", + next->num, + date2str(next->date, datebuf) + ); + return 0; + } + if (author && strcmp(author,next->author)!=0) { + rcserror("Revision %s has author %s.", + next->num, next->author + ); + return 0; + } + if (state && strcmp(state,next->state)!=0) { + rcserror("Revision %s has state %s.", + next->num, + next->state ? next->state : "<empty>" + ); + return 0; + } + *store = 0; + return next; + } + + norev: + bufautoend(&t); + return 0; +} + + + + + static struct hshentry * +genbranch(bpoint, revno, length, date, author, state, store) + struct hshentry const *bpoint; + char const *revno; + int 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 0 on error. + */ +{ + int field; + register struct hshentry * next, * trail; + register struct branchhead const *bhead; + int result; + struct buf t; + char datebuf[datesize + zonelenmax]; + + field = 3; + bhead = bpoint->branches; + + do { + if (!bhead) { + bufautobegin(&t); + rcserror("no side branches present for %s", + partialno(&t,revno,field-1) + ); + bufautoend(&t); + return 0; + } + + /*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); + rcserror("branch number %s too high", + partialno(&t,revno,field) + ); + bufautoend(&t); + return 0; + } + } + + if (result<0) { + absent(revno, field); + return 0; + } + + next = bhead->hsh; + if (length==field) { + /* pick latest one on that branch */ + trail = 0; + do { if ((!date || cmpdate(date,next->date)>=0) && + (!author || strcmp(author,next->author)==0) && + (!state || strcmp(state,next->state)==0) + ) trail = next; + next=next->next; + } while (next); + + if (!trail) { + cantfindbranch(revno, date, author, state); + return 0; + } else { /* print up to last one suitable */ + next = bhead->hsh; + while (next!=trail) { + store1(&store, next); + next=next->next; + } + store1(&store, next); + } + *store = 0; + return next; + } + + /* length > field */ + /* find revision */ + /* check low */ + if (cmpnumfld(revno,next->num,field+1)<0) { + bufautobegin(&t); + rcserror("revision number %s too low", + partialno(&t,revno,field+1) + ); + bufautoend(&t); + return 0; + } + do { + store1(&store, next); + trail = next; + next = next->next; + } while (next && 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 0; + } + if (length == field+1) { + if (date && cmpdate(date,trail->date)<0) { + rcserror("Revision %s has date %s.", + trail->num, + date2str(trail->date, datebuf) + ); + return 0; + } + if (author && strcmp(author,trail->author)!=0) { + rcserror("Revision %s has author %s.", + trail->num, trail->author + ); + return 0; + } + if (state && strcmp(state,trail->state)!=0) { + rcserror("Revision %s has state %s.", + trail->num, + trail->state ? trail->state : "<empty>" + ); + return 0; + } + } + bhead = trail->branches; + + } while ((field+=2) <= length); + *store = 0; + 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. Return 0 if not present. + */ +{ + register struct assoc const *next; + for (next = Symbols; next; next = next->nextassoc) + if (strcmp(id, next->symbol)==0) + return next->num; + return 0; +} + +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; + int dots; + + sp = source; + bufalloc(target, 1); + tp = target->string; + if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ + *tp='\0'; + return true; + } + if (sp[0] == KDELIM && !sp[1]) { + if (!getoldkeys(fp)) + return false; + if (!*prevrev.string) { + workerror("working file lacks revision number"); + return false; + } + bufscpy(target, prevrev.string); + return true; + } + tlim = tp + target->size; + dots = 0; + + for (;;) { + register char *p = tp; + size_t s = tp - target->string; + int id = false; + for (;;) { + switch (ctab[(unsigned char)*sp]) { + case IDCHAR: + case LETTER: + case Letter: + id = true; + /* fall into */ + case DIGIT: + if (tlim <= p) + p = bufenlarge(target, &tlim); + *p++ = *sp++; + continue; + + default: + break; + } + break; + } + if (tlim <= p) + p = bufenlarge(target, &tlim); + *p = 0; + tp = target->string + s; + + if (id) { + bp = lookupsym(tp); + if (!bp) { + rcserror("Symbolic name `%s' is undefined.",tp); + return false; + } + } else { + /* skip leading zeros */ + for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++) + continue; + + if (!*bp) + if (s || *sp!='.') + break; + else { + /* Insert default branch before initial `.'. */ + char const *b; + if (Dbranch) + b = Dbranch; + else if (Head) + b = Head->num; + else + break; + getbranchno(b, target); + bp = tp = target->string; + tlim = tp + target->size; + } + } + + while ((*tp++ = *bp++)) + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + + switch (*sp++) { + case '\0': + return true; + + case '.': + if (!*sp) { + if (dots & 1) + break; + if (!(bp = branchtip(target->string))) + return false; + bufscpy(target, bp); + return true; + } + ++dots; + tp[-1] = '.'; + continue; + } + break; + } + + rcserror("improper revision number: %s", source); + return false; +} + + char const * +namedrev(name, delta) + char const *name; + struct hshentry *delta; +/* Yield NAME if it names DELTA, 0 otherwise. */ +{ + if (name) { + char const *id = 0, *p, *val; + for (p = name; ; p++) + switch (ctab[(unsigned char)*p]) { + case IDCHAR: + case LETTER: + case Letter: + id = name; + break; + + case DIGIT: + break; + + case UNKN: + if (!*p && id && + (val = lookupsym(id)) && + strcmp(val, delta->num) == 0 + ) + return id; + /* fall into */ + default: + return 0; + } + } + return 0; +} + + 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 + +/* +* Test the routines that generate a sequence of delta numbers +* needed to regenerate a given delta. +*/ + +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 (!fgets(symrevno, sizeof(symrevno), stdin)) break; + if (*symrevno == '.') break; + aprintf(stderr,"%s;\n",symrevno); + expandsym(symrevno,&numricrevno); + aprintf(stderr,"expanded number: %s; ",numricrevno.string); + aprintf(stderr,"Date: "); + fgets(date, sizeof(date), stdin); aprintf(stderr,"%s; ",date); + aprintf(stderr,"Author: "); + fgets(author, sizeof author, stdin); aprintf(stderr,"%s; ",author); + aprintf(stderr,"State: "); + fgets(state, state, stdin); aprintf(stderr, "%s;\n", state); + target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0, + *state?state:(char*)0, &gendeltas); + if (target) { + while (gendeltas) { + aprintf(stderr,"%s\n",gendeltas->first->num); + gendeltas = gendeltas->next; + } + } + } while (true); + aprintf(stderr,"done\n"); + exitmain(EXIT_SUCCESS); +} + +void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/gnu/usr.bin/rcs/src/rcssyn.c b/gnu/usr.bin/rcs/src/rcssyn.c new file mode 100644 index 00000000000..56070423262 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcssyn.c @@ -0,0 +1,685 @@ +/* RCS file syntactic analysis */ + +/****************************************************************************** + * Syntax Analysis. + * Keyword table + * Testprogram: define SYNTEST + * Compatibility with Release 2: define COMPAT2=1 + ****************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rcssyn.c,v $ + * Revision 1.1 1996/08/12 04:08:26 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.15 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.14 1995/06/01 16:23:43 eggert + * (expand_names): Add "b" for -kb. + * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate. + * + * Revision 5.13 1994/03/20 04:52:58 eggert + * Remove lint. + * + * Revision 5.12 1993/11/03 17:42:27 eggert + * Parse MKS RCS dates; ignore \r in diff control lines. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.11 1992/07/28 16:12:44 eggert + * Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.10 1992/01/24 18:44:19 eggert + * Move put routines to rcsgen.c. + * + * Revision 5.9 1992/01/06 02:42:34 eggert + * ULONG_MAX/10 -> ULONG_MAX_OVER_10 + * while (E) ; -> while (E) continue; + * + * 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. + */ + +#include "rcsbase.h" + +libId(synId, "$Id: rcssyn.c,v 1.1 1996/08/12 04:08:26 millert Exp $") + +static char const *getkeyval P((char const*,enum tokens,int)); +static int getdelta P((void)); +static int strn2expmode P((char const*,size_t)); +static struct hshentry *getdnum P((void)); +static void badDiffOutput P((char const*)) exiting; +static void diffLineNumberTooLarge P((char const*)) exiting; +static void getsemi P((char const*)); + +/* keyword table */ + +char const + Kaccess[] = "access", + Kauthor[] = "author", + Kbranch[] = "branch", + Kcomment[] = "comment", + Kdate[] = "date", + Kdesc[] = "desc", + Kexpand[] = "expand", + Khead[] = "head", + Klocks[] = "locks", + Klog[] = "log", + Knext[] = "next", + Kstate[] = "state", + Kstrict[] = "strict", + Ksymbols[] = "symbols", + Ktext[] = "text"; + +static char const +#if COMPAT2 + Ksuffix[] = "suffix", +#endif + K_branches[]= "branches"; + +static struct buf Commleader; +struct cbuf Comment; +struct cbuf Ignored; +struct access * AccessList; +struct assoc * Symbols; +struct rcslock *Locks; +int Expand; +int StrictLocks; +struct hshentry * Head; +char const * Dbranch; +int 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 rcslock *newlock; + struct hshentry * delta; + struct access **LastAccess; + struct assoc **LastSymbol; + struct rcslock **LastLock; + struct buf b; + struct cbuf cb; + + TotalDeltas=0; + + getkey(Khead); + Head = getdnum(); + getsemi(Khead); + + Dbranch = 0; + 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 = 0; + 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 = 0; + 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 rcslock); + newlock->login=id; + newlock->delta=delta; + *LastLock = newlock; + LastLock = &newlock->nextlock; + } + } + *LastLock = 0; + getsemi(Klocks); + + if ((StrictLocks = getkeyopt(Kstrict))) + getsemi(Kstrict); + + clear_buf(&Comment); + 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", "b", + 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 +ignorephrases(key) + const char *key; +/* +* Ignore a series of phrases that do not start with KEY. +* Stop when the next phrase starts with a token that is not an identifier, +* or is KEY. +*/ +{ + for (;;) { + nextlex(); + if (nexttok != ID || strcmp(NextString,key) == 0) + break; + warnignore(); + hshenter=false; + for (;; nextlex()) { + switch (nexttok) { + case SEMI: hshenter=true; break; + case ID: + case NUM: ffree1(NextString); continue; + case STRING: readstring(); continue; + default: continue; + } + break; + } + } +} + + + 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 = 0; + getsemi(K_branches); + + getkey(Knext); + Delta->next = num = getdnum(); + getsemi(Knext); + Delta->lockedby = 0; + 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 rcslock const *currlock; + + while (getdelta()) + continue; + 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 = 0; + + getkey(keyword); + if (nexttok==token) { + val = NextString; + nextlex(); + } else { + if (!optional) + fatserror("missing %s", keyword); + } + getsemi(keyword); + return(val); +} + + + void +unexpected_EOF() +{ + rcsfaterror("unexpected EOF in diff output"); +} + + void +initdiffcmd(dc) + register struct diffcmd *dc; +/* Initialize *dc suitably for getdiffcmd(). */ +{ + dc->adprev = 0; + dc->dafter = 0; +} + + static void +badDiffOutput(buf) + char const *buf; +{ + rcsfaterror("bad diff output line: %s", buf); +} + + static void +diffLineNumberTooLarge(buf) + char const *buf; +{ + rcsfaterror("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; + 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) { + rcsfaterror("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++) == ' '; ) + continue; + line1 = 0; + while (isdigit(c)) { + if ( + LONG_MAX/10 < line1 || + (t = line1 * 10, (line1 = t + (c - '0')) < t) + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + while (c == ' ') + c = *p++; + nlines = 0; + while (isdigit(c)) { + if ( + LONG_MAX/10 < nlines || + (t = nlines * 10, (nlines = t + (c - '0')) < t) + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + if (c == '\r') + c = *p++; + if (c || !nlines) { + badDiffOutput(buf); + } + if (line1+nlines < line1) + diffLineNumberTooLarge(buf); + switch (buf[0]) { + case 'a': + if (line1 < dc->adprev) { + rcsfaterror("backward insertion in diff output: %s", buf); + } + dc->adprev = line1 + 1; + break; + case 'd': + if (line1 < dc->adprev || line1 < dc->dafter) { + rcsfaterror("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 + +/* Input an RCS file and print its internal data structures. */ + +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(); + fdlock = STDOUT_FILENO; + putadmin(); + + gettree(); + + getdesc(true); + + nextlex(); + + if (!eoflex()) { + fatserror("expecting EOF"); + } + exitmain(EXIT_SUCCESS); +} + +void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/gnu/usr.bin/rcs/src/rcstest b/gnu/usr.bin/rcs/src/rcstest new file mode 100644 index 00000000000..d8a7ca098c6 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcstest @@ -0,0 +1,454 @@ +#! /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 1.1 1996/08/12 04:08:28 millert Exp $ + + +# Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + +# The Makefile overrides the following defaults. +: ${ALL_CFLAGS=-Dhas_conf_h} +: ${CC=cc} +: ${DIFF=diff} +# : ${LDFLAGS=} ${LIBS=} tickles old shell bug + +CL="$CC $ALL_CFLAGS $LDFLAGS -o a.out" +L=$LIBS + +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 + +if test -d RCS +then rmdir=: +else rmdir=rmdir; mkdir RCS || exit +fi + +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 && +test -r $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 && +test -r $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; } + +rm -f a.c && +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; } + +rm -f a.c && +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; } + +rm -f a.c && +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; } + +rm -f a.c && +co -l $q a.c && +echo 1.5 >a.c && +ci -u -mm $q a.c && +test -r 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 + + +# Get the date of the previous revision in UTC. +date=`rlog -r a.c | sed -n '/^date: /{ s///; s/;.*//; p; q; }'` || exit +case $date in +[0-9][0-9][0-9]*[0-9]/[0-1][0-9]/[0-3][0-9]\ [0-2][0-9]:[0-5][0-9]:[0-6][0-9]);; +*) echo >&2 "$0: $date: bad rlog date output"; exit 1 +esac +PWD=`pwd` && export PWD && +rm -f a.c && +co -l $q a.c && +sed 's/@/$/g' >a.kv <<EOF +@Author: w @ +@Date: $date @ +@Header: $PWD$SLASH$RCSfile 2.1 $date w s @ +@Id: a.c 2.1 $date w s @ +@Locker: @ + * @Log: a.c @ + * Revision 2.1 $date w + * m + * +@Name: Oz @ +@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 s/Oz//g a.kv >a.e && +sed s/Oz/N/g a.kv >a.N && +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" -nOz -ss -ww -u2.1 -mm $q a.c && +$diff a.kv a.c || { echo "#keyword expansion failed"; exit 1; } +co -pOz -ko $q a.c >a.oo && +$diff a.o a.oo || { echo "#co -p -ko failed"; exit 1; } +cp a.kv a.o && cp a.o a.b || exit 2 +rcs -oOz $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 -n 's/^[^$]*\$/$/p' 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; } + +rm -f a.c && +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; } + +rm -f a.c && +rcs -NN:2.1 $q a.c && +co -rN $q a.c && +$diff a.N a.c || { echo "#rcs -N failed"; exit 1; } + +rm -f a.c && +co -l $q a.c && +echo ':::$''Log$' >a.c && +ci -u -mm $q a.c && +test " `sed '$!d' a.c`" = ' :::' || { echo "#comment leader failed"; exit 1; } + +rm -f a.c && +rcs -o2.2: $q a.c && +co $q a.c && +$diff a.e a.c || { echo "#rcs -o failed"; exit 1; } + +rcsdiff -r1.1 -rOz $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 b k kv kvl o v +do + rm -f a.c && + cp a.$i a.c && + rcsdiff -k$i -rOz $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 && +rcsclean $q a.c && +rcsclean -u $q a.c || { echo "#rcsclean botched a nonexistent file"; exit 1; } + +rm -f a.c && +co $q a.c && +rcsclean -n $q a.c && +rcsclean -n -u $q a.c && +test -f a.c || { echo "#rcsclean -n removed a file"; exit 1; } + +rm -f a.c && +co $q a.c && +rcsclean $q a.c && +test ! -f a.c || { echo "#rcsclean missed an unlocked file"; exit 1; } + +rm -f a.c && +co -l $q a.c && +rcsclean $q a.c && +test -f a.c || { echo "#rcsclean removed a locked file"; exit 1; } +rcsclean -u $q a.c && +test ! -f a.c || { + echo "#rcsclean -u missed an unchanged locked file"; exit 1; +} + +rm -f a.c && +co -l $q a.c && +echo change >>a.c && +rcsclean $q a.c && +rcsclean $q -u a.c && +test -f a.c || { echo "#rcsclean removed a changed file"; 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 -E -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 + +# Avoid `tr' if possible; it's not portable, and it can't handle null bytes. +# Our substitute exclusive-ORs with '\n'; +# this ensures null bytes on output, which is even better than `tr', +# since some diffs think a file is binary only if it contains null bytes. +cat >a.c <<'EOF' +#include <stdio.h> +int main() { + int c; + while ((c=getchar()) != EOF) + putchar(c ^ '\n'); + return 0; +} +EOF +tr=tr +if (rm -f a.exe a.out && $CL a.c $L >&2) >/dev/null 2>&1 +then + if test -s a.out + then tr=./a.out + elif test -s a.exe + then tr=./a.exe + fi +fi +{ + co -p $q a.c | $tr '\012' '\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' '\012' >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 + Oz: 2.1 + n: 1.8 +keyword substitution: kv +total revisions: 13; selected revisions: 1 +description: +1.1 +---------------------------- +revision 2.1 +date: $date; author: w; state: s; lines: +14 -1 +============================================================================= +EOF +test $? = 0 || { echo "#rlog failed"; exit 1; } + + +test ! -f $lockfile || { echo "#lock file not removed"; exit 1; } + +rm -f a.* $RCSfile $RCS_alt +$rmdir RCS diff --git a/gnu/usr.bin/rcs/src/rcstime.c b/gnu/usr.bin/rcs/src/rcstime.c new file mode 100644 index 00000000000..6dcc6291cb7 --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcstime.c @@ -0,0 +1,191 @@ +/* Convert between RCS time format and Posix and/or C formats. */ + +/* Copyright 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" +#include "partime.h" +#include "maketime.h" + +libId(rcstimeId, "$Id: rcstime.c,v 1.1 1996/08/12 04:08:29 millert Exp $") + +static long zone_offset; /* seconds east of UTC, or TM_LOCAL_ZONE */ +static int use_zone_offset; /* if zero, use UTC without zone indication */ + +/* +* Convert Unix time to RCS format. +* For compatibility with older versions of RCS, +* dates from 1900 through 1999 are stored without the leading "19". +*/ + void +time2date(unixtime,date) + time_t unixtime; + char date[datesize]; +{ + register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5)); + VOID sprintf(date, +# if has_printf_dot + "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d", +# else + "%02d.%02d.%02d.%02d.%02d.%02d", +# endif + tm->tm_year + ((unsigned)tm->tm_year < 100 ? 0 : 1900), + tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec + ); +} + +/* Like str2time, except die if an error was found. */ +static time_t str2time_checked P((char const*,time_t,long)); + static time_t +str2time_checked(source, default_time, default_zone) + char const *source; + time_t default_time; + long default_zone; +{ + time_t t = str2time(source, default_time, default_zone); + if (t == -1) + faterror("unknown date/time: %s", source); + return t; +} + +/* +* Parse a free-format date in SOURCE, convert it +* into RCS internal format, and store the result into TARGET. +*/ + void +str2date(source, target) + char const *source; + char target[datesize]; +{ + time2date( + str2time_checked(source, now(), + use_zone_offset ? zone_offset + : RCSversion<VERSION(5) ? TM_LOCAL_ZONE + : 0 + ), + target + ); +} + +/* Convert an RCS internal format date to time_t. */ + time_t +date2time(source) + char const source[datesize]; +{ + char s[datesize + zonelenmax]; + return str2time_checked(date2str(source, s), (time_t)0, 0); +} + + +/* Set the time zone for date2str output. */ + void +zone_set(s) + char const *s; +{ + if ((use_zone_offset = *s)) { + long zone; + char const *zonetail = parzone(s, &zone); + if (!zonetail || *zonetail) + error("%s: not a known time zone", s); + else + zone_offset = zone; + } +} + + +/* +* Format a user-readable form of the RCS format DATE into the buffer DATEBUF. +* Yield DATEBUF. +*/ + char const * +date2str(date, datebuf) + char const date[datesize]; + char datebuf[datesize + zonelenmax]; +{ + register char const *p = date; + + while (*p++ != '.') + continue; + if (!use_zone_offset) + 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 + ); + else { + struct tm t; + struct tm const *z; + int non_hour; + long zone; + char c; + + t.tm_year = atoi(date) - (date[2]=='.' ? 0 : 1900); + t.tm_mon = atoi(p) - 1; + t.tm_mday = atoi(p+3); + t.tm_hour = atoi(p+6); + t.tm_min = atoi(p+9); + t.tm_sec = atoi(p+12); + t.tm_wday = -1; + zone = zone_offset; + if (zone == TM_LOCAL_ZONE) { + time_t u = tm2time(&t, 0), d; + z = localtime(&u); + d = difftm(z, &t); + zone = (time_t)-1 < 0 || d < -d ? d : -(long)-d; + } else { + adjzone(&t, zone); + z = &t; + } + c = '+'; + if (zone < 0) { + zone = -zone; + c = '-'; + } + VOID sprintf(datebuf, +# if has_printf_dot + "%.2d-%.2d-%.2d %.2d:%.2d:%.2d%c%.2d", +# else + "%02d-%02d-%02d %02d:%02d:%02d%c%02d", +# endif + z->tm_year + 1900, + z->tm_mon + 1, z->tm_mday, z->tm_hour, z->tm_min, z->tm_sec, + c, (int) (zone / (60*60)) + ); + if ((non_hour = zone % (60*60))) { +# if has_printf_dot + static char const fmt[] = ":%.2d"; +# else + static char const fmt[] = ":%02d"; +# endif + VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour / 60); + if ((non_hour %= 60)) + VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour); + } + } + return datebuf; +} diff --git a/gnu/usr.bin/rcs/src/rcsutil.c b/gnu/usr.bin/rcs/src/rcsutil.c new file mode 100644 index 00000000000..644bc05c79a --- /dev/null +++ b/gnu/usr.bin/rcs/src/rcsutil.c @@ -0,0 +1,1398 @@ +/* RCS utility functions */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* + * $Log: rcsutil.c,v $ + * Revision 1.1 1996/08/12 04:08:29 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.20 1995/06/16 06:19:24 eggert + * (catchsig): Remove `return'. + * Update FSF address. + * + * Revision 5.19 1995/06/02 18:19:00 eggert + * (catchsigaction): New name for `catchsig', for sa_sigaction signature. + * Use nRCS even if !has_psiginfo, to remove unused variable warning. + * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. + * Use ENOTSUP only if defined. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, + * to determine whether to use SA_SIGINFO feature, + * but also check at runtime whether the feature works. + * (catchsig): If an mmap_signal occurs, report the affected file name. + * (unsupported_SA_SIGINFO, accessName): New variables. + * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. + * If SA_SIGINFO fails, fall back on sa_handler method. + * + * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. + * (concatenate): Remove. + * + * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. + * Remove reference to OPEN_O_WORK. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Specify subprocess input via file descriptor, not file name. + * Avoid messing with I/O buffers in the child process. + * Define dup in terms of F_DUPFD if it exists. + * Move setmtime to rcsedit.c. Remove lint. + * + * Revision 5.16 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Use psiginfo and setreuid if available. Move date2str to maketime.c. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. + * Add mmap_signal, which minimizes signal handling for non-mmap hosts. + * + * Revision 5.13 1992/02/17 23:02:28 eggert + * Work around NFS mmap SIGBUS problem. Add -T support. + * + * Revision 5.12 1992/01/24 18:44:19 eggert + * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint + * + * Revision 5.11 1992/01/06 02:42:34 eggert + * O_BINARY -> OPEN_O_WORK + * while (E) ; -> while (E) continue; + * + * 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 1.1 1996/08/12 04:08:29 millert 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 RCS_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)); + 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 = 0; +} + + 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 setenv 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; +#ifdef SA_SIGINFO + static int unsupported_SA_SIGINFO; + static siginfo_t bufsiginfo; + static siginfo_t *volatile heldsiginfo; +#endif + + +#if has_NFS && has_mmap && large_memory && mmap_signal + static char const *accessName; + + void + readAccessFilenameBuffer(filename, p) + char const *filename; + unsigned char const *p; + { + unsigned char volatile t; + accessName = filename; + t = *p; + accessName = 0; + } +#else +# define accessName ((char const *) 0) +#endif + + +#if !has_psignal + +# define psignal my_psignal + static void my_psignal P((int,char const*)); + static void +my_psignal(sig, s) + int sig; + char const *s; +{ + char const *sname = "Unknown signal"; +# if has_sys_siglist && defined(NSIG) + if ((unsigned)sig < NSIG) + sname = sys_siglist[sig]; +# else + switch (sig) { +# 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 +# if has_mmap && large_memory +# if defined(SIGBUS) && mmap_signal==SIGBUS + case SIGBUS: sname = "Bus error"; break; +# endif +# if defined(SIGSEGV) && mmap_signal==SIGSEGV + case SIGSEGV: sname = "Segmentation fault"; break; +# endif +# endif + } +# endif + + /* Avoid calling sprintf etc., in case they're not reentrant. */ + { + char const *p; + char buf[BUFSIZ], *b = buf; + for (p = s; *p; *b++ = *p++) + continue; + *b++ = ':'; + *b++ = ' '; + for (p = sname; *p; *b++ = *p++) + continue; + *b++ = '\n'; + VOID write(STDERR_FILENO, buf, b - buf); + } +} +#endif + +static signal_type catchsig P((int)); +#ifdef SA_SIGINFO + static signal_type catchsigaction P((int,siginfo_t*,void*)); +#endif + + static signal_type +catchsig(s) + int s; +#ifdef SA_SIGINFO +{ + catchsigaction(s, (siginfo_t *)0, (void *)0); +} + static signal_type +catchsigaction(s, i, c) + int s; + siginfo_t *i; + void *c; +#endif +{ +# if sig_zaps_handler + /* If a signal arrives before we reset the handler, we lose. */ + VOID signal(s, SIG_IGN); +# endif + +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) + i = 0; +# endif + + if (holdlevel) { + heldsignal = s; +# ifdef SA_SIGINFO + if (i) { + bufsiginfo = *i; + heldsiginfo = &bufsiginfo; + } +# endif + return; + } + + ignoreints(); + setrid(); + if (!quietflag) { + /* Avoid calling sprintf etc., in case they're not reentrant. */ + char const *p; + char buf[BUFSIZ], *b = buf; + + if ( ! ( +# if has_mmap && large_memory && mmap_signal + /* Check whether this signal was planned. */ + s == mmap_signal && accessName +# else + 0 +# endif + )) { + char const *nRCS = "\nRCS"; +# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal + if (s == mmap_signal && i && i->si_errno) { + errno = i->si_errno; + perror(nRCS++); + } +# endif +# if defined(SA_SIGINFO) && has_psiginfo + if (i) + psiginfo(i, nRCS); + else + psignal(s, nRCS); +# else + psignal(s, nRCS); +# endif + } + + for (p = "RCS: "; *p; *b++ = *p++) + continue; +# if has_mmap && large_memory && mmap_signal + if (s == mmap_signal) { + p = accessName; + if (!p) + p = "Was a file changed by some other process? "; + else { + char const *p1; + for (p1 = p; *p1; p1++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + VOID write(STDERR_FILENO, p, p1 - p); + b = buf; + p = ": Permission denied. "; + } + while (*p) + *b++ = *p++; + } +# endif + for (p = "Cleaning up.\n"; *p; *b++ = *p++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + } + exiterr(); +} + + void +ignoreints() +{ + ++holdlevel; +} + + void +restoreints() +{ + if (!--holdlevel && heldsignal) +# ifdef SA_SIGINFO + VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); +# else + VOID catchsig(heldsignal); +# endif +} + + +static void setup_catchsig P((int const*,int)); + +#if has_sigaction + + static void check_sig P((int)); + static void + check_sig(r) + int r; + { + if (r != 0) + efaterror("signal handling"); + } + + static void + setup_catchsig(sig, sigs) + int const *sig; + int sigs; + { + register int i, j; + struct sigaction act; + + for (i=sigs; 0<=--i; ) { + check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); + if (act.sa_handler != SIG_IGN) { + act.sa_handler = catchsig; +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) { +# if has_sa_sigaction + act.sa_sigaction = catchsigaction; +# else + act.sa_handler = catchsigaction; +# endif + act.sa_flags |= SA_SIGINFO; + } +# endif + for (j=sigs; 0<=--j; ) + check_sig(sigaddset(&act.sa_mask, sig[j])); + if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { +# if defined(SA_SIGINFO) && defined(ENOTSUP) + if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { + /* Turn off use of SA_SIGINFO and try again. */ + unsupported_SA_SIGINFO = 1; + i++; + continue; + } +# endif + check_sig(-1); + } + } + } + } + +#else +#if has_sigblock + + static void + setup_catchsig(sig, sigs) + int const *sig; + int sigs; + { + 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(sig, sigs) + int const *sig; + int sigs; + { + 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 + + +static int const regsigs[] = { +# 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 +}; + + void +catchints() +{ + static int catching_ints; + if (!catching_ints) { + catching_ints = true; + setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); + } +} + +#if has_mmap && large_memory && mmap_signal + + /* + * If you mmap an NFS file, and someone on another client removes the last + * link to that file, and you later reference an uncached part of that file, + * you'll get a SIGBUS or SIGSEGV (depending on the operating system). + * Catch the signal and report the problem to the user. + * Unfortunately, there's no portable way to differentiate between this + * problem and actual bugs in the program. + * This NFS problem is rare, thank goodness. + * + * This can also occur if someone truncates the file, even without NFS. + */ + + static int const mmapsigs[] = { mmap_signal }; + + void + catchmmapints() + { + static int catching_mmap_ints; + if (!catching_mmap_ints) { + catching_mmap_ints = true; + setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); + } + } +#endif + +#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 maps_memory + 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(); +} + +/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ + static int dupSafer P((int)); + static int +dupSafer(fd) + int fd; +{ +# ifdef F_DUPFD + return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); +# else + int e, f, i, used = 0; + while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) + used |= 1<<f; + e = errno; + for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) + if (used & (1<<i)) + VOID close(i); + errno = e; + return f; +# endif +} + +/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */ + int +fdSafer(fd) + int fd; +{ + if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { + int f = dupSafer(fd); + int e = errno; + VOID close(fd); + errno = e; + fd = f; + } + return fd; +} + +/* Like fopen, except the result is never stdin, stdout, or stderr. */ + FILE * +fopenSafer(filename, type) + char const *filename; + char const *type; +{ + FILE *stream = fopen(filename, type); + if (stream) { + int fd = fileno(stream); + if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { + int f = dupSafer(fd); + if (f < 0) { + int e = errno; + VOID fclose(stream); + errno = e; + return 0; + } + if (fclose(stream) != 0) { + int e = errno; + VOID close(f); + errno = e; + return 0; + } + stream = fdopen(f, type); + } + } + return stream; +} + + +#ifdef F_DUPFD +# undef dup +# define dup(fd) fcntl(fd, F_DUPFD, 0) +#endif + + +#if has_fork || has_spawn + + static int movefd P((int,int)); + 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 P((int,char const*,int)); + 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 redirect P((int,int)); + static void +redirect(old, new) + int old, new; +/* +* Move file descriptor OLD to NEW. +* If OLD is -1, do nothing. +* If OLD is -2, just close NEW. +*/ +{ + if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) + efaterror("spawn I/O redirection"); +} +#endif + + +#else /* !has_fork && !has_spawn */ + + static void bufargcat P((struct buf*,int,char const*)); + 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 + +#if !has_spawn && has_fork +/* +* Output the string S to stderr, without touching any I/O buffers. +* This is useful if you are a child process, whose buffers are usually wrong. +* Exit immediately if the write does not completely succeed. +*/ +static void write_stderr P((char const *)); + static void +write_stderr(s) + char const *s; +{ + size_t slen = strlen(s); + if (write(STDERR_FILENO, s, slen) != slen) + _exit(EXIT_TROUBLE); +} +#endif + +/* +* Run a command. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* args[1..] form the command to be run; args[0] might be modified. +*/ + int +runv(infd, outname, args) + int infd; + char const *outname, **args; +{ + int wstatus; + +#if bad_wait_if_SIGCHLD_ignored + static int fixed_SIGCHLD; + if (!fixed_SIGCHLD) { + fixed_SIGCHLD = true; +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); + } +#endif + + oflush(); + eflush(); + { +#if has_spawn + int in, out; + char const *file; + + in = -1; + if (infd != -1 && infd != STDIN_FILENO) { + if ((in = dup(STDIN_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn input setup"); + in = -2; + } else { +# ifdef F_DUPFD + if (close(STDIN_FILENO) != 0) + efaterror("spawn input close"); +# endif + } + if ( +# ifdef F_DUPFD + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + ) + efaterror("spawn input redirection"); + } + + out = -1; + if (outname) { + if ((out = dup(STDOUT_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn output setup"); + out = -2; + } + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) + efaterror(outname); + } + + wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); +# ifdef RCS_SHELL + if (wstatus == -1 && errno == ENOEXEC) { + args[0] = RCS_SHELL; + wstatus = spawnv(0, args[0], (char**)args); + } +# endif + redirect(in, STDIN_FILENO); + redirect(out, STDOUT_FILENO); +#else +#if has_fork + pid_t pid; + if (!(pid = vfork())) { + char const *notfound; + if (infd != -1 && infd != STDIN_FILENO && ( +# ifdef F_DUPFD + (VOID close(STDIN_FILENO), + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + )) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": I/O redirection failed\n"); + _exit(EXIT_TROUBLE); + } + + if (outname) + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": "); + write_stderr(outname); + write_stderr(": cannot create\n"); + _exit(EXIT_TROUBLE); + } + VOID exec_RCS(args[1], (char**)(args + 1)); + notfound = args[1]; +# ifdef RCS_SHELL + if (errno == ENOEXEC) { + args[0] = notfound = RCS_SHELL; + VOID execv(args[0], (char**)args); + } +# endif + + /* Avoid perror since it may misuse buffers. */ + write_stderr(notfound); + write_stderr(": not found\n"); + _exit(EXIT_TROUBLE); + } + if (pid < 0) + efaterror("fork"); +# if has_waitpid + if (waitpid(pid, &wstatus, 0) < 0) + efaterror("waitpid"); +# else + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) + efaterror("wait"); + } while (w != pid); + } +# endif +#else + static struct buf b; + char const *p; + + /* Use system(). On many hosts system() discards signals. Yuck! */ + p = args + 1; + bufscpy(&b, *p); + while (*++p) + bufargcat(&b, ' ', *p); + if (infd != -1 && infd != STDIN_FILENO) { + char redirection[32]; + VOID sprintf(redirection, "<&%d", infd); + bufscat(&b, redirection); + } + if (outname) + bufargcat(&b, '>', outname); + wstatus = system(b.string); +#endif +#endif + } + if (!WIFEXITED(wstatus)) { + if (WIFSIGNALED(wstatus)) { + psignal(WTERMSIG(wstatus), args[1]); + fatcleanup(1); + } + faterror("%s failed for unknown reason", args[1]); + } + return WEXITSTATUS(wstatus); +} + +#define CARGSMAX 20 +/* +* Run a command. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* The remaining arguments specify the command and its arguments. +*/ + int +#if has_prototypes +run(int infd, char const *outname, ...) +#else + /*VARARGS2*/ +run(infd, outname, va_alist) + int infd; + char const *outname; + va_dcl +#endif +{ + va_list ap; + char const *rgargs[CARGSMAX]; + register int i; + vararg_start(ap, outname); + for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) + if (CARGSMAX <= i) + faterror("too many command arguments"); + va_end(ap); + return runv(infd, outname, rgargs); +} + + +int RCSversion; + + void +setRCSversion(str) + char const *str; +{ + static int oldversion; + + register char const *s = str + 2; + + if (*s) { + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = true; + v = 0; + while (isdigit(*s)) + v = 10*v + *s++ - '0'; + if (*s) + error("%s isn't a number", str); + else if (v < VERSION_min || VERSION_max < v) + error("%s out of range %d..%d", + str, VERSION_min, VERSION_max + ); + + RCSversion = VERSION(v); + } else { + printf("RCS version %s\n", RCS_version_string); + exit(0); + } +} + + int +getRCSINIT(argc, argv, newargv) + int argc; + char **argv, ***newargv; +{ + register char *p, *q, **pp; + size_t n; + + if ((q = cgetenv("RCSLOCALID"))) + setRCSlocalId(q); + + 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++)) + continue; + } + 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 +#if has_prototypes +set_uid_to(uid_t u) +#else + set_uid_to(u) uid_t u; +#endif +/* Become user u. */ +{ + static int looping; + + if (euid() == ruid()) + return; +#if (has_fork||has_spawn) && DIFF_ABSOLUTE +# if has_setreuid + if (setreuid(u==euid() ? ruid() : euid(), u) != 0) + efaterror("setuid"); +# else + if (seteuid(u) != 0) + efaterror("setuid"); +# endif +#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 + + time_t +now() +{ + static time_t t; + if (!t && time(&t) == -1) + efaterror("time"); + return t; +} diff --git a/gnu/usr.bin/rcs/src/rlog.c b/gnu/usr.bin/rcs/src/rlog.c new file mode 100644 index 00000000000..873c408dc9e --- /dev/null +++ b/gnu/usr.bin/rcs/src/rlog.c @@ -0,0 +1,1279 @@ +/* Print log messages and other information about RCS files. */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 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, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * $Log: rlog.c,v $ + * Revision 1.1 1996/08/12 04:08:31 millert + * rcs 5.7 + OpenBSD changes + * + * Revision 5.18 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.17 1995/06/01 16:23:43 eggert + * (struct rcslockers): Renamed from `struct lockers'. + * (getnumericrev): Return error indication instead of ignoring errors. + * (main): Check it. Don't use dateform. + * (recentdate, extdate): cmpnum -> cmpdate + * + * Revision 5.16 1994/04/13 16:30:34 eggert + * Fix bug; `rlog -lxxx' inverted the sense of -l. + * + * Revision 5.15 1994/03/17 14:05:48 eggert + * -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it. + * Emulate -V4's white space generation more precisely. + * Work around SVR4 stdio performance bug. Remove lint. + * + * Revision 5.14 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Add -N, -z. Ignore -T. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Don't miss B.0 when handling branch B. Diagnose missing `,' in -r. + * Add -V. Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Don't duplicate unexpected_EOF's function. lint -> RCS_lint + * + * Revision 5.10 1992/01/06 02:42:34 eggert + * Update usage string. + * while (E) ; -> while (E) continue; + * + * 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 rcslockers { /* lockers in locker option; stored */ + char const * login; /* lockerlist */ + struct rcslockers * 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 */ + int numfld; /* option; stored in revlist */ + char const * strtrev; + char const * endrev; + struct Revpairs * rnext; + } ; + +struct Datepairs{ /* date range in -d option; stored in */ + struct Datepairs *dnext; + char strtdate[datesize]; /* duelst and datelist */ + char enddate[datesize]; + char ne_date; /* datelist only; distinguishes < from <= */ + }; + +static char extractdelta P((struct hshentry const*)); +static int checkrevpair P((char const*,char const*)); +static int extdate P((struct hshentry*)); +static int getnumericrev P((void)); +static struct hshentry const *readdeltalog P((void)); +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 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 rcslockers *lockerlist; +static struct stateattri *statelist; + + +mainProg(rlogId, "rlog", "$Id: rlog.c,v 1.1 1996/08/12 04:08:31 millert Exp $") +{ + static char const cmdusage[] = + "\nrlog usage: rlog -{bhLNRt} -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ..."; + + register FILE *out; + char *a, **newargv; + struct Datepairs *currdate; + char const *accessListString, *accessFormat; + char const *headFormat, *symbolFormat; + struct access const *curaccess; + struct assoc const *curassoc; + struct hshentry const *delta; + struct rcslock const *currlock; + int descflag, selectflag; + int onlylockflag; /* print only files with locks */ + int onlyRCSflag; /* print only RCS pathname */ + int pre5; + int shownames; + int revno; + + descflag = selectflag = shownames = 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 'N': + shownames = false; + 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 'z': + zone_set(a); + break; + + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + + case 'V': + setRCSversion(*argv); + break; + + default: + unknown: + error("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + if (! (descflag|selectflag)) { + warn("-t overrides -h."); + descflag = true; + } + + pre5 = RCSversion < VERSION(5); + if (pre5) { + accessListString = "\naccess list: "; + accessFormat = " %s"; + headFormat = "RCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; + insDelFormat = " lines added/del: %ld/%ld"; + symbolFormat = " %s: %s;"; + } else { + accessListString = "\naccess list:"; + accessFormat = "\n\t%s"; + headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; + insDelFormat = " lines: +%ld -%ld"; + symbolFormat = "\n\t%s: %s"; + } + + /* Now handle all pathnames. */ + if (nerror) + cleanup(); + else if (argc < 1) + faterror("no input file%s", cmdusage); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); + + if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) + continue; + + /* + * RCSname contains the name of the RCS file, + * and finptr the file descriptor; + * workname 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", RCSname); + continue; + } + + gettree(); + + if (!getnumericrev()) + continue; + + /* + * Output the first character with putc, not printf. + * Otherwise, an SVR4 stdio bug buffers output inefficiently. + */ + aputc_('\n', out) + + /* print RCS pathname, working pathname and optional + administrative information */ + /* could use getfullRCSname() here, but that is very slow */ + aprintf(out, headFormat, RCSname, workname, + 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 && pre5) + aputs(" ; strict" + (Locks?3:0), out); + + aputs(accessListString, out); /* print access list */ + curaccess = AccessList; + while(curaccess) { + aprintf(out, accessFormat, curaccess->login); + curaccess = curaccess->nextaccess; + } + + if (shownames) { + aputs("\nsymbolic names:", out); /* print symbolic names */ + for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) + aprintf(out, symbolFormat, curassoc->symbol, curassoc->num); + } + if (pre5) { + aputs("\ncomment leader: \"", out); + awrite(Comment.string, Comment.size, out); + afputc('\"', out); + } + if (!pre5 || Expand != KEYVAL_EXPAND) + aprintf(out, "\nkeyword substitution: %s", + expand_names[Expand] + ); + + aprintf(out, "\ntotal revisions: %d", TotalDeltas); + + revno = 0; + + if (Head && selectflag & descflag) { + + exttree(Head); + + /* get most recently date of the dates pointed by duelst */ + currdate = duelst; + while( currdate) { + VOID strcpy(currdate->strtdate, "0.0.0.0.0.0"); + recentdate(Head, currdate); + currdate = currdate->dnext; + } + + revno = extdate(Head); + + aprintf(out, ";\tselected revisions: %d", revno); + } + + afputc('\n',out); + if (descflag) { + aputs("description:\n", out); + getdesc(true); + } + if (revno) { + while (! (delta = readdeltalog())->selector || --revno) + continue; + if (delta->next && countnumflds(delta->num)==2) + /* Read through delta->next to get its insertlns. */ + while (readdeltalog() != delta->next) + continue; + putrunk(); + putree(Head); + } + aputs("=============================================================================\n",out); + } + Ofclose(out); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + Izclose(&finptr); +} + +#if RCS_lint +# define exiterr rlogExit +#endif + 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) 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) return; + + putforest(branchroot->nextbranch); + + putabranch(branchroot->hsh); + putree(branchroot->hsh); +} + + + + + static void +putabranch(root) + struct hshentry const *root; +/* function : print one branch */ + +{ + if (!root) 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 + zonelenmax]; + int pre5 = RCSversion < VERSION(5); + + if (!node->selector) + return; + + out = stdout; + aprintf(out, + "----------------------------\nrevision %s%s", + node->num, pre5 ? " " : "" + ); + if ( node->lockedby ) + aprintf(out, pre5+"\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); + + ignorephrases(Ktext); + 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 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) + unexpected_EOF(); + 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) return; + + root->selector = extractdelta(root); + root->log.string = 0; + 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 rcslockers *newlocker; + argv--; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + if ( c == '\0') { + lockerlist = 0; + return; + } + + while( c != '\0' ) { + newlocker = talloc(struct rcslockers); + newlocker->lockerlink = lockerlist; + newlocker->login = argv; + lockerlist = newlocker; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + *argv = '\0'; + if ( c == '\0' ) return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + 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==';') + continue; + if ( c == '\0' ) { + authorlist = talloc(struct authors); + authorlist->login = getusername(false); + authorlist->nextauthor = 0; + return; + } + + while( c != '\0' ) { + newauthor = talloc(struct authors); + newauthor->nextauthor = authorlist; + newauthor->login = argv; + authorlist = newauthor; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + * argv = '\0'; + if ( c == '\0') return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + + 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==';') + continue; + if ( c == '\0'){ + error("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!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; + *argv = '\0'; + if ( c == '\0' ) return; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; + } +} + + + + 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 rcslockers const *plocker; + struct rcslock *p, **pp; + + if (!lockerlist) return; + + /* shorten Locks to those contained in lockerlist */ + for (pp = &Locks; (p = *pp); ) + for (plocker = lockerlist; ; ) + if (strcmp(plocker->login, p->login) == 0) { + pp = &p->nextlock; + break; + } else if (!(plocker = plocker->lockerlink)) { + *pp = p->nextlock; + break; + } +} + + + + 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) return; + if (root->selector) { + if ( cmpdate(root->date, pd->strtdate) >= 0 && + cmpdate(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 int +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; + int revno, ne; + + if (!root) + return 0; + + if ( datelist || duelst) { + pdate = datelist; + while( pdate ) { + ne = pdate->ne_date; + if ( + (!pdate->strtdate[0] + || ne <= cmpdate(root->date, pdate->strtdate)) + && + (!pdate->enddate[0] + || ne <= cmpdate(pdate->enddate, root->date)) + ) + break; + pdate = pdate->dnext; + } + if (!pdate) { + pdate = duelst; + for (;;) { + if (!pdate) { + root->selector = false; + break; + } + if (cmpdate(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 rcslock const *plock; + struct stateattri const *pstate; + struct authors const *pauthor; + struct Revpairs const *prevision; + int 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==';') + continue; + if ( c == '\0' ) { + error("missing date/time after -d"); + return; + } + + while( c != '\0' ) { + switchflag = false; + nextdate = talloc(struct Datepairs); + if ( c == '<' ) { /* case: -d <date */ + c = *++argv; + if (!(nextdate->ne_date = c!='=')) + c = *++argv; + (nextdate->strtdate)[0] = '\0'; + } else if (c == '>') { /* case: -d'>date' */ + c = *++argv; + if (!(nextdate->ne_date = c!='=')) + 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 */ + int eq = argv[1]=='='; + nextdate->ne_date = !eq; + argv += eq; + while ((c = *++argv) == ' ' || c=='\t' || c=='\n') + continue; + 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 (RCSversion < VERSION(5)) + nextdate->ne_date = 0; + if ( c == '\0') return; + while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n') + continue; + } +} + + + + static int +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; + int n; + struct buf s, e; + char const *lrev; + struct buf const *rstart, *rend; + + Revlst = 0; + ptr = revlist; + bufautobegin(&s); + bufautobegin(&e); + while( ptr ) { + n = 0; + rstart = &s; + rend = &e; + + switch (ptr->numfld) { + + case 1: /* -rREV */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + rend = &s; + n = countnumflds(s.string); + if (!n && (lrev = tiprev())) { + bufscpy(&s, lrev); + n = countnumflds(lrev); + } + break; + + case 2: /* -rREV: */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + 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)) + goto freebufs; + if ((n = countnumflds(e.string)) < 2) + bufscpy(&s, ".0"); + else { + bufscpy(&s, e.string); + VOID strcpy(strrchr(s.string,'.'), ".0"); + } + break; + + default: /* -rREV1:REV2 */ + if (!( + expandsym(ptr->strtrev, &s) + && expandsym(ptr->endrev, &e) + && checkrevpair(s.string, e.string) + )) + goto freebufs; + 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); + } + + freebufs: + bufautoend(&s); + bufautoend(&e); + return !ptr; +} + + + + 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) */ + +{ + int length = countnumflds(num1); + + if ( + countnumflds(num2) != length + || (2 < length && compartial(num1, num2, length-1) != 0) + ) { + rcserror("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') + continue; + 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) + break; + continue; + } + 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; + else if (c==',' || c==';') + c = *++argv; + else + error("missing `,' near `%c%s'", c, argv+1); + } +} diff --git a/gnu/usr.bin/rcs/src/version.c b/gnu/usr.bin/rcs/src/version.c new file mode 100644 index 00000000000..81f5585b9d1 --- /dev/null +++ b/gnu/usr.bin/rcs/src/version.c @@ -0,0 +1,2 @@ +#include "rcsbase.h" +char const RCS_version_string[] = "5.7"; |