summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/cvs
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>1996-01-01 11:01:14 +0000
committercvs2svn <cvs2svn@FreeBSD.org>1996-01-01 11:01:14 +0000
commit9963660cb5783bc50ba922ca985ddd2f255f897f (patch)
tree488d5b4f781673604736d64f0f4a1871755d2df8 /gnu/usr.bin/cvs
parent15a04a0c6d870853ad0e05b26774f0a5b9df47ac (diff)
Notes
Diffstat (limited to 'gnu/usr.bin/cvs')
-rw-r--r--gnu/usr.bin/cvs/BUGS248
-rw-r--r--gnu/usr.bin/cvs/INSTALL356
-rw-r--r--gnu/usr.bin/cvs/MINOR-BUGS60
-rw-r--r--gnu/usr.bin/cvs/NEWS863
-rw-r--r--gnu/usr.bin/cvs/PROJECTS59
-rw-r--r--gnu/usr.bin/cvs/contrib/Makefile37
-rw-r--r--gnu/usr.bin/cvs/contrib/ccvs-rsh.pl97
-rw-r--r--gnu/usr.bin/cvs/contrib/clmerge.pl152
-rw-r--r--gnu/usr.bin/cvs/contrib/cvscheck.sh84
-rw-r--r--gnu/usr.bin/cvs/contrib/descend.sh116
-rw-r--r--gnu/usr.bin/cvs/contrib/dirfns.shar481
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/compile.sh2
-rw-r--r--gnu/usr.bin/cvs/contrib/rcs-to-cvs.sh185
-rw-r--r--gnu/usr.bin/cvs/contrib/rcs2log.sh592
-rw-r--r--gnu/usr.bin/cvs/contrib/rcs2sccs.sh143
-rw-r--r--gnu/usr.bin/cvs/contrib/sccs2rcs.csh277
-rw-r--r--gnu/usr.bin/cvs/cvs/ChangeLog3205
-rw-r--r--gnu/usr.bin/cvs/cvs/README-rm-add48
-rw-r--r--gnu/usr.bin/cvs/cvs/client.c3542
-rw-r--r--gnu/usr.bin/cvs/cvs/client.h163
-rw-r--r--gnu/usr.bin/cvs/cvs/expand_path.c139
-rw-r--r--gnu/usr.bin/cvs/cvs/login.c287
-rw-r--r--gnu/usr.bin/cvs/cvs/rcscmds.c102
-rw-r--r--gnu/usr.bin/cvs/cvs/server.c3992
-rw-r--r--gnu/usr.bin/cvs/cvs/server.h136
-rw-r--r--gnu/usr.bin/cvs/cvs/update.h9
-rw-r--r--gnu/usr.bin/cvs/cvs/wrapper.c371
-rw-r--r--gnu/usr.bin/cvs/cvsbug/Makefile25
-rw-r--r--gnu/usr.bin/cvs/cvsbug/cvsbug.8269
-rw-r--r--gnu/usr.bin/cvs/cvsbug/cvsbug.sh528
-rw-r--r--gnu/usr.bin/cvs/cvsinit/cvsinit.8142
-rw-r--r--gnu/usr.bin/cvs/doc/cvsclient.texi673
-rw-r--r--gnu/usr.bin/cvs/examples/checkoutlist20
-rw-r--r--gnu/usr.bin/cvs/examples/cvswrappers29
-rw-r--r--gnu/usr.bin/cvs/examples/rcstemplate7
-rw-r--r--gnu/usr.bin/cvs/examples/taginfo25
-rw-r--r--gnu/usr.bin/cvs/examples/unwrap21
-rw-r--r--gnu/usr.bin/cvs/examples/wrap21
-rw-r--r--gnu/usr.bin/cvs/lib/ChangeLog335
-rw-r--r--gnu/usr.bin/cvs/lib/error.h47
-rw-r--r--gnu/usr.bin/cvs/lib/filesubr.c640
-rw-r--r--gnu/usr.bin/cvs/lib/getline.c126
-rw-r--r--gnu/usr.bin/cvs/lib/getline.h15
-rw-r--r--gnu/usr.bin/cvs/lib/run.c533
-rw-r--r--gnu/usr.bin/cvs/lib/save-cwd.c141
-rw-r--r--gnu/usr.bin/cvs/lib/save-cwd.h20
-rw-r--r--gnu/usr.bin/cvs/lib/xgetwd.c79
47 files changed, 19442 insertions, 0 deletions
diff --git a/gnu/usr.bin/cvs/BUGS b/gnu/usr.bin/cvs/BUGS
new file mode 100644
index 000000000000..3ad13a82828d
--- /dev/null
+++ b/gnu/usr.bin/cvs/BUGS
@@ -0,0 +1,248 @@
+* `cvs checkout -d nested/dir/path <module>' just doesn't work. The
+ simpler version -- `cvs checkout -d single-dir <module>' works,
+ however.
+
+
+* CVS leaves .#mumble files around when a conflict occurs. (Note:
+ this is intentional and is documented in doc/cvs.texinfo. Of course
+ whether it is a good idea is a separate question).
+
+
+* pcl-cvs doesn't like it when you try to check in a file which isn't
+ up-to-date. The messages produced by the server perhaps don't match
+ what pcl-cvs is looking for.
+
+
+* From: Roland McGrath <roland@gnu.ai.mit.edu>
+ To: Cyclic CVS Hackers <cyclic-cvs@cyclic.com>
+ Subject: weird bug
+ Date: Sat, 25 Mar 1995 16:41:41 -0500
+ X-Windows: Even your dog won't like it.
+
+ I just noticed some droppings on my disk from what must be a pretty weird
+ bug in remote CVS.
+
+ In my home directory on a repository machine I use, I find:
+
+ drwxr-xr-x 4 roland staff 512 Mar 7 14:08 cvs-serv28962
+ drwxr-xr-x 4 roland staff 512 Mar 7 14:11 cvs-serv28978
+ drwxr-xr-x 4 roland staff 512 Mar 7 15:13 cvs-serv29141
+
+ OK, so these are leftover cruft from some cvs run that got aborted.
+ Well, it should clean up after itself, but so what.
+
+ The last one is pretty dull; the real weirdness is the contents of the
+ first two directories.
+
+ duality 77 # ls -RF cvs-serv28978/
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978:
+ arpa/
+
+ cvs-serv28978/cvs-serv28978/arpa:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978:
+ assert/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978:
+ bare/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978:
+ conf/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978:
+ crypt/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978:
+ csu/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu:
+ CVS/ cvs-serv28978/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/CVS:
+ Entries Repository
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978:
+ ctype/
+
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978/ctype:
+ CVS/ cvs-serv28978/
+
+ [...]
+
+ ls: cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978/ctype/cvs-serv28978/dirent/cvs-serv28978/elf/cvs-serv28978/gnu/cvs-serv28978/gnulib/cvs-serv28978/grp/cvs-serv28978/hurd/cvs-serv28978/hurd/hurd/cvs-serv28978/inet/cvs-serv28978/inet/arpa/cvs-serv28978/inet/netinet[...]/cvs-serv28978/posix/cvs-serv28978/posix/glob/cvs-serv28978/posix/gnu/cvs-serv28978/posix/sys/cvs-serv28978/protocols/cvs-serv28978/pwd/cvs-serv28978/resolv/cvs-serv28978/resolv/arpa/cvs-serv28978/resolv/sys/cvs-serv28978/resource/cvs-serv28978/resource/sys/cvs-serv28978/rpc/cvs-serv28978/setjmp/cvs-serv28978/signal/cvs-serv28978/signal/sys/cvs-serv28978/socket/cvs-serv28978/socket: File name too long
+ cvs-serv28978/cvs-serv28978/arpa/cvs-serv28978/assert/cvs-serv28978/bare/cvs-serv28978/conf/cvs-serv28978/crypt/cvs-serv28978/csu/cvs-serv28978/ctype/cvs-serv28978/dirent/cvs-serv28978/elf/cvs-serv28978/gnu/cvs-serv28978/gnulib/cvs-serv28978/grp/cvs-serv28978/hurd/cvs-serv28978/hurd/hurd/cvs-serv28978/inet/cvs-serv28978/inet/arpa/cvs-serv28978/inet/netinet[...]/cvs-serv28978/posix/glob/cvs-serv28978/posix/gnu/cvs-serv28978/posix/sys/cvs-serv28978/protocols/cvs-serv28978/pwd/cvs-serv28978/resolv/cvs-serv28978/resolv/arpa/cvs-serv28978/resolv/sys/cvs-serv28978/resource/cvs-serv28978/resource/sys/cvs-serv28978/rpc/cvs-serv28978/setjmp/cvs-serv28978/signal/cvs-serv28978/signal/sys/cvs-serv28978/socket/cvs-serv28978:
+
+
+* From: billr@mpd.tandem.com (Bill Robertson)
+ Subject: Problem with rtag and the -D option
+ Date: Fri, 17 Mar 1995 10:53:29 -0600 (CST)
+
+ I have been trying to use the -D option to specify a date for tagging, but
+ rtag does not recognize the -D option. It is documented to do so and I've
+ tested the use of -D with cvs update and cvs diff and it works fine there.
+
+
+* We need some version numbers really badly. Are there some
+ (and Charles Hannum is just not including them in his reports), or do
+ we simply have no reliable way to distinguish between the various
+ versions of rCVS people on the list are running?
+
+ Now that I think of it, version numbers present a problem when
+ people can update their sources anytime and rebuild. I think the
+ solution is to increment a minor version number *every* time a bug is
+ fixed, so we can identify uniquely what someone is running when they
+ submit a report. This implies recording the version increments in the
+ ChangeLog; that way we can just look to see where a particular version
+ lies in relation to the flow of changing code.
+
+ Should we be doing same with Guppy? I guess not -- it's only
+ important when you have people who are updating directly from your
+ development tree, which is the case with the remote-cvs folks.
+
+ Thoughts?
+
+
+* (Charles Hannum <mycroft@ai.mit.edu>) has these bugs:
+
+ I just tossed remote CVS at a fairly large source tree that I already
+ had, and noticed a few problems:
+
+ 1) server.c assumes that /usr/tmp is a valid default for the place to
+ store files uploaded from the client. There are a number of systems
+ that now use /var/tmp. These should probably be detected by autoconf.
+
+ 2) The server deals rather ungracefully with the tmp directory
+ becoming full.
+
+ 3) There's some oddness with relative paths in Repository files that
+ causes the directory prefix to be added twice; e.g. if I have CVSROOT
+ set to `machine:/this/dir', and I try to update in a directory whose
+ Repository file says `src/bin', the server looks in
+ `/this/dir/machine:/this/dir/src/bin'.
+
+* From: "Charles M. Hannum" <mycroft@ai.mit.edu>
+ To: jimb@duality.gnu.ai.mit.edu, roland@duality.gnu.ai.mit.edu
+ Subject: Serious flaw in remote CVS
+ Date: Wed, 22 Feb 1995 20:54:36 -0500
+
+ I just found a major flaw in the current implementation. Because the
+ sockets are not changed to non-blocking mode, write(2)s can hang. In
+ some cases, this happens on both sides at the same time, with the
+ socket buffers full in both directions. This causes a deadlock,
+ because both processes are stuck in I/O wait and thus never drain
+ their input queues.
+
+ Until this is fixed, I can't use it. I'll look at the problem myself
+ at some point, but I don't know when.
+
+
+ From: "Charles M. Hannum" <mycroft@ai.mit.edu>
+ To: remote-cvs@cyclic.com
+ Cc: jimb@totoro.bio.indiana.edu
+ Subject: Re: forwarded message from Charles M. Hannum
+ Date: Wed, 22 Feb 1995 22:07:07 -0500
+
+ FYI, this happened because the tmp directory on the server became
+ full. Somehow the server started interpreting the files the client
+ was sending as commands, and started spewing tons of errors.
+ Apparently the errors are sent with blocking I/O, or something, and
+ thus allowed the deadlock to happen.
+
+
+* From: "Charles M. Hannum" <mycroft@ai.mit.edu>
+ To: remote-cvs@cyclic.com
+ Subject: Regarding that relative path problem
+ Date: Thu, 23 Feb 1995 02:41:51 -0500
+
+ This is actually more serious. If you have `bar.com:/foo' as your CVS
+ root directory, then:
+
+ 1) When you check things out, the Repository files will contain
+ `/foo/...' (i.e. without the machine name), which makes little sense.
+
+ 2) If you instead have a relative path, when the Repository file is
+ read, `bar.com:/foo' is prepended. This is sent to the server, but
+ confuses it, because it's not expecting the machine name to be
+ prepended.
+
+ A slightly klugy fix would be to have the client prepend the machine
+ name when writing a new Repository file, and strip it off before
+ sending one to the server. This would be backward-compatible with the
+ current arrangement.
+
+
+* From: "Charles M. Hannum" <mycroft@ai.mit.edu>
+ To: remote-cvs@cyclic.com
+ Subject: Still one more bug
+ Date: Sat, 25 Feb 1995 17:01:15 -0500
+
+ mycroft@duality [1]; cd /usr/src/lib/libc
+ mycroft@duality [1]; cvs diff -c2 '-D1 day ago' -Dnow
+ cvs server: Diffing .
+ cvs server: Diffing DB
+ cvs [server aborted]: could not chdir to DB: No such file or directory
+ mycroft@duality [1];
+
+ `DB' is an old directory, which no longer has files in it, and is
+ removed automatically when I use the `-P' option to checkout.
+
+ This error doesn't occur when run locally.
+
+ P.S. Is anyone working on fixing these bugs?
+
+
+* From: Roland McGrath <roland@gnu.ai.mit.edu>
+ To: Cyclic CVS Hackers <cyclic-cvs@cyclic.com>
+ Subject: bizarre failure mode
+ Date: Tue, 7 Mar 95 14:17:28 -0500
+
+ This is pretty weird:
+
+ CVS_SERVER='TMPDIR=. /usr/local/bin/cvs' ../cvs-build/src/cvs update -q
+ cvs [server aborted]: could not get working directory: Result too large
+ [Exit 1]
+ asylum 29 % grep 'Result too large' /usr/include/sys/errno.h
+ #define ERANGE 34 /* Result too large */
+
+ Now, getcwd fails with ERANGE when the buffer is too small. But I don't
+ know why that would be the case; I don't think there are exceptionally long
+ directory names involved. It would be robust to notice ERANGE and use a
+ bigger buffer. But I suspect something weirder is going on.
+
+ The repository in question in duality.gnu.ai.mit.edu:/gd4/gnu/cvsroot/libc.
+
+ Send me a PGP-signed message if you want the password to use the machine
+ where the problem showed up.
diff --git a/gnu/usr.bin/cvs/INSTALL b/gnu/usr.bin/cvs/INSTALL
new file mode 100644
index 000000000000..898af66c1c1e
--- /dev/null
+++ b/gnu/usr.bin/cvs/INSTALL
@@ -0,0 +1,356 @@
+#ident "$CVSid$"
+
+First, read the README file. If you're still happy...
+
+CVS has been tested on the following platforms. The most recent
+version of CVS reported to have been tested is indicated, but more
+recent versions of CVS probably will work too. Please send updates to
+this list to info-cvs@prep.ai.mit.edu.
+
+Alpha:
+ DEC Alpha running OSF/1 version 1.3 using cc (about 1.4A2)
+ DEC Alpha running OSF/1 version 2.0 (1.4.90)
+ DEC Alpha running OSF/1 version 2.1 (about 1.4A2)
+ DEC Alpha running OSF/1 version 3.0 (1.5.95) (footnote 7)
+HPPA:
+ HP 9000/710 running HP-UX 8.07A using gcc (about 1.4A2)
+ HP 9000/715 running HP-UX 9.01 (1.6)
+ HPPA 1.1 running HP-UX A.09.03 (1.5.95) (footnote 8)
+ NextSTEP 3.3 (1.4.92, a few tweaks needed)
+i386 family:
+ Gateway P5-66 (pentium) running Solaris 2.4 using gcc (about 1.4A2)
+ PC Clone running UnixWare v1.1.1 using gcc (about 1.4A2)
+ PC Clone running ISC 4.0.1 (1.5.94)
+ PC Clone running Fintronic Linux 1.2.5 (1.5)
+ PC Clone running BSDI 2.0 (1.4.93) (footnote 5)
+ PC Clone running Windows NT 3.51 (1.6.2 client-only)
+ FreeBSD 2.0.5, i486, gcc (1.5.95)
+ NextSTEP 3.3 (1.4.92, a few tweaks needed)
+ SCO Unix 3.2.4.2 (1.4.93) (footnote 4)
+ SCO OpenServer 5.0.0, "CC='cc -b elf' configure"
+m68k:
+ Sun 3 running SunOS 4.1.1_U1 w/ bundled K&R /usr/5bin/cc (1.6)
+ NextSTEP 3.3 (1.4.92, a few tweaks needed)
+m88k:
+ Data General AViiON running dgux 5.4R2.10 (1.5)
+ Harris Nighthawk 5800 running CX/UX 7.1 (1.5) (footnote 6)
+MIPS:
+ DECstation running Ultrix 4.2a (1.4.90)
+ DECstation running Ultrix 4.3 (1.5)
+ SGI running Irix 4.0.5H using gcc and cc (about 1.4A2) (footnote 2)
+ SGI running Irix 5.3 (1.4.93)
+ SGI running Irix-6 (about 1.4.90) (footnote 3)
+ Siemens-Nixdorf RM600 running SINIX-Y (1.6)
+PowerPC or RS/6000:
+ IBM RS/6000 running AIX 3.2.5 (cc=xlc, CVS 1.5)
+ IBM RS/6000 running AIX 4.1 using gcc and cc (about 1.4A2) (footnote 1)
+SPARC:
+ Sun SPARC running SunOS 4.1.4 w/ bundled K&R /usr/5bin/cc (1.6)
+ Sun SPARC running SunOS 4.1.3, 4.1.2, and 4.1.1 (1.5)
+ Sun SPARC running SunOS 4.1.3, w/ bundled K&R cc (1.5.94)
+ Sun SPARCstation 10 running Solaris 2.3 using gcc and cc (about 1.4A2)
+ Sun SPARCstation running Solaris 2.4 using gcc and cc (about 1.5.91)
+ Sun SPARC running Solaris 2.5 (2.5 beta?) (1.6.2)
+ NextSTEP 3.3 (1.4.92, a few tweaks needed)
+
+(footnote 1)
+ AIX 4.1 systems fail to run "configure" due to bugs in their
+ "/bin/sh" implementation. You might want to try feeding the
+ configure script to "bash" ported to AIX 4.1. (about 1.4A2).
+
+(footnote 2)
+ Some Irix 4.0 systems may core dump in malloc while running
+ CVS. We believe this is a bug in the Irix malloc. You can
+ workaround this bug by linking with "-lmalloc" if necessary.
+ (about 1.4A2).
+
+(footnote 3)
+ There are some warnings about pointer casts which can safely be
+ ignored. (about 1.4.90).
+
+(footnote 4) Comment out the include of sys/time.h in src/server.c. (1.4.93)
+ You also may have to make sure TIME_WITH_SYS_TIME is undef'ed.
+
+(footnote 5) Change /usr/tmp to /var/tmp in src/server.c (2 places) (1.4.93).
+
+(footnote 6) Build in ucb universe with COFF compiler tools. Put
+ /usr/local/bin first in PATH while doing a configure, make
+ and install of GNU diffutils-2.7, rcs-5.7, then cvs-1.5.
+
+(footnote 7) Manoj Srivastava <srivasta@pilgrim.umass.edu> reports
+ success with this configure command:
+ CC=cc CFLAGS='-O2 -Olimit 2000 -std1' ./configure --verbose alpha-dec-osf
+
+(footnote 8) Manoj Srivastava <srivasta@pilgrim.umass.edu> reports
+ success with this configure command:
+ CC=cc CFLAGS='+O2 -Aa -D_HPUX_SOURCE' ./configure --verbose hppa1.1-hp-hpux
+
+-------------------------------------------------------------------------------
+
+Installation under Unix:
+
+1) Run "configure":
+
+ $ ./configure
+
+ You can specify an alternate destination to override the default with
+ the --prefix option:
+
+ $ ./configure --prefix=/usr/local/gnu
+
+ or some path that is more appropriate for your site. The default prefix
+ value is "/usr/local", with binaries in sub-directory "bin", manual
+ pages in sub-directory "man", and libraries in sub-directory "lib".
+
+ This release of CVS also requires RCS commands to be installed in
+ the user's PATH (or a path you have configured in src/options.h).
+ If you don't have RCS, you will need to get it from GNU as well. It
+ is best to get the version 5.7 (or later) version of RCS, available
+ from prep.ai.mit.edu in the file pub/gnu/rcs-5.7.tar.gz. It is best
+ (although not essential) to avoid RCS versions 5.6.[5-7] beta
+ because the rcsmerge therein defaults to -A instead of -E which
+ affects the way CVS handles conflicts (this is fixed in RCS 5.6.8
+ and RCS 5.7).
+
+ Along with RCS, you will want to run GNU diffutils. This will allow
+ revision control of files with binary data (a real nice feature).
+ You will need at least version 1.15 of GNU diff for this to work.
+ The current version of GNU diffutils is 2.7, and it is also
+ available from prep.ai.mit.edu in the file pub/gnu/diffutils-2.7.tar.gz.
+
+ WARNING: Be sure that you (have) configure(d) RCS to work correctly
+ with GNU diff to avoid other configuration problems.
+
+ Configure will attempt to discern the location of your most capable
+ version of diff, and tries to find the GNU Diffutils version first.
+ You can explicitly tell configure to use the diffutils that's
+ installed in the same place you intend to install CVS:
+
+ $ ./configure --with-diffutils
+
+ Or, if you've installed it somewhere else, you can give configure
+ the full pathname:
+
+ $ ./configure --with-diffutils=/usr/gnu/bin/diff
+
+ Configure will also try to find a version of grep that supports the
+ '-s' option, and tries to find the GNU Grep version first. You can
+ similarly tell it where to find GNU Grep:
+
+ $ ./configure --with-gnugrep
+ $ ./configure --with-gnugrep=/usr/gnu/bin/grep
+
+ If you are using the remote client, you will need a version of patch
+ which understands unidiffs (such as any recent version of GNU
+ patch). Configure does not yet check to see if you've got this, so
+ be careful!
+
+ NOTE: The configure program will cache the results of the previous
+ configure execution. If you need to re-run configure from scratch, you
+ may need to run "make distclean" first to remove the cached
+ configuration information.
+
+ Try './configure --help' for further information on its usage.
+
+ NOTE ON CVS's USE OF NDBM:
+
+ By default, CVS uses some built-in ndbm emulation code to allow
+ CVS to work in a heterogeneous environment. However, if you have
+ a very large modules database, this may not work well. You will
+ need to edit src/options.h to turn off the MY_NDBM #define and
+ re-run configure. If you do this, the following comments apply.
+ If not, you may safely skip these comments.
+
+ If you configure CVS to use the real ndbm(3) libraries and
+ you do not have them installed in a "normal" place, you will
+ probably want to get the GNU version of ndbm (gdbm) and install
+ that before running the CVS configure script. Be aware that the
+ GDBM 1.5 release does NOT install the <ndbm.h> header file included
+ with the release automatically. You may have to install it by hand.
+
+ If you configure CVS to use the ndbm(3) libraries, you cannot
+ compile CVS with GNU cc (gcc) on Sun-4 SPARC systems. However, gcc
+ 2.0 may have fixed this limitation if -fpcc-struct-return is
+ defined. When using gcc on other systems to compile CVS, you *may*
+ need to specify the -fpcc-struct-return option to gcc (you will
+ *know* you have to if "cvs checkout" core dumps in some ndbm
+ function). You can do this as follows:
+
+ $ CC='gcc -fpcc-struct-return' ./configure
+
+ for sh, bash, and ksh users and:
+
+ % setenv CC 'gcc -fpcc-struct-return'
+ % ./configure
+
+ for csh and tcsh users.
+
+ END OF NOTE FOR NDBM GUNK.
+
+2) Edit src/options.h. Appropriate things to look at may be the
+ invocation locations of programs like DIFF, GREP, RM, and SORT.
+ Also glance at the default values for the environment variables
+ that CVS uses, in particular, the RCSBIN variable, which holds the
+ path to where the RCS programs live on your system. The
+ likelihood is that you don't have to change anything here, except
+ perhaps adding the -a option to DIFF if you are using GNU diff.
+
+3) Try to build it:
+
+ $ make
+
+ This will (hopefully) make the needed CVS binaries within the "src"
+ directory. If something fails for your system, using the "cvsbug"
+ script submit your "config.status" file together with your host
+ type, operating system and compiler information, make output, and
+ anything else you think will be helpful.
+
+ You may also wish to validate the correctness of the new binary by
+ running the regression tests:
+
+ $ make check
+
+ Note that if your /bin/sh doesn't support shell functions, you'll
+ have to try something like this, where "/bin/sh5" is replaced by the
+ pathname of a shell which handles normal shell functions:
+
+ $ make SHELL=/bin/sh5 check
+
+ WARNING: This test can take quite a while to run, esp. if your
+ disks are slow or over-loaded.
+
+ If you receive any un-expected output from the regression tests,
+ using the "cvsbug" script please submit your "config.status" file,
+ together with your host type, operating system and compiler
+ information, the contents of /tmp/cvs-sanity/check.log, and any
+ "make check" output.
+
+4) Install the binaries/documentation:
+
+ $ make install
+
+ Depending on your installation's configuration, you may need to be
+ root to do this.
+
+5) Take a look at the CVS documentation.
+
+ $ man cvs
+
+ and
+
+ $ info cvs
+
+ See what it can do for you, and if it fits your environment (or can
+ possibly be made to fit your environment). If things look good,
+ continue on...
+
+6) Setup the master source repository. Choose a directory with ample disk
+ space available for source files. This is where the RCS ",v" files
+ will be stored. Note that this should be some shared directory for your
+ site. It should probably be auto-mounted, if you're running NFS.
+
+ Say you choose "/src/master" as the root of your source repository.
+ Run the "cvsinit" script to help you set it up. It will ask you to
+ enter the path to your CVSROOT area. You would enter /src/master in
+ this example.
+
+ $ ./cvsinit
+
+ The cvsinit script will setup a reasonable CVSROOT area to start with.
+ It is also valuable to folks who already have a CVSROOT area setup from
+ using earlier releases of CVS. It assumes that you have installed CVS
+ already (step 4) and that the RCS programs (co and ci) are in your
+ PATH. There are many ways to customize CVS for your site. Read the
+ cvs(5) manual page when you get the chance.
+
+7) Have all users of the CVS system set the CVSROOT environment
+ variable appropriately to reflect the placement of your source
+ repository. If the above example is used, the following commands
+ can be placed in user's ~/.profile, ~/.bash_profile file; or in the
+ site-wide /etc/profile:
+
+ CVSROOT=/src/master; export CVSROOT
+
+ for sh/bash/ksh users, or place the following commands in the user's
+ ~/.cshrc, ~/.login, or /etc/chsrc file:
+
+ setenv CVSROOT /src/master
+
+ for csh/tcsh users. If these environment variables are not already set
+ in your current shell, set them now (or source the login script you
+ just edited). You will need to have the CVSROOT environment variable
+ set to continue on to the next step.
+
+8) It might be a good idea to jump right in and put the CVS distribution
+ directly under CVS control. From within the top-level directory of the
+ CVS distribution (the one that contains this README file) do the
+ following commands:
+
+ $ make distclean
+ $ cvs import -m 'CVS 1.6 distribution' cvs CVS CVS-1_6
+
+9) Having done step 8, one should be able to checkout a fresh copy of the
+ CVS distribution and hack away at the sources with the following command:
+
+ $ cd
+ $ cvs checkout cvs
+
+ This will make the directory "cvs" in your current directory and
+ populate it with the appropriate CVS files and directories.
+
+10) Remember to edit the modules file manually when sources are checked in
+ with "cvs import" or "cvs add". A copy of the modules file for editing
+ can usually be retrieved with the "cvs checkout modules" command, and
+ definitely with the "cvs checkout CVSROOT" command. See cvs(5).
+
+11) Read the NEWS file to see what's new.
+
+12) Hack away.
+
+-------------------------------------------------------------------------------
+
+Detailed information about your interaction with "configure":
+
+The "configure" script and its interaction with its options and the
+environment is described here. For more detailed documentation about
+"configure", please refer to the GNU Autoconf documentation.
+
+Supported options are:
+
+ --srcdir=DIR Useful for compiling on many different
+ machines sharing one source tree.
+ --prefix=DIR The root of where to install the
+ various pieces of CVS (/usr/local).
+ --exec_prefix=DIR If you want executables in a
+ host-dependent place and shared
+ things in a host-independent place.
+ --with-diffutils[=PATH] Assume use of GNU diffutils is possible.
+ --with-gnugrep[=PATH] Assume use of GNU grep is possible.
+
+The following environment variables override configure's default
+behaviour:
+
+ CC If not set, tries to use gcc first,
+ then cc. Also tries to use "-g -O"
+ as options, backing down to -g
+ alone if that doesn't work.
+ INSTALL If not set, tries to use "install", then
+ "./install-sh" as a final choice.
+ RANLIB If not set, tries to determine if "ranlib"
+ is available, choosing "echo" if it doesn't
+ appear to be.
+ YACC If not set, tries to determine if "bison"
+ is available, choosing "yacc" if it doesn't
+ appear to be.
+
+-------------------------------------------------------------------------------
+Installation under Windows NT:
+
+You may find interesting information in windows-NT/README.
+
+1) Using Microsoft Visual C++ version 2.1, open the project `cvsnt.mak',
+ in the top directory of the CVS distribution.
+2) Choose "Build cvs.exe" from the "Project" menu.
+3) MSVC will place the executable file cvs.exe in WinDebug, or whatever
+ your target directory is.
+-------------------------------------------------------------------------------
diff --git a/gnu/usr.bin/cvs/MINOR-BUGS b/gnu/usr.bin/cvs/MINOR-BUGS
new file mode 100644
index 000000000000..7b857193f86b
--- /dev/null
+++ b/gnu/usr.bin/cvs/MINOR-BUGS
@@ -0,0 +1,60 @@
+Low-priority bugs go here. We don't have many yet -- everything is
+high-priority at the moment. :-)
+
+
+* From: Jeff Johnson <jbj@brewster.JBJ.ORG>
+ To: cyclic-cvs@cyclic.com
+ Subject: Named_Root assumes . on server
+ Date: Wed, 17 May 1995 11:04:53 -0400 (EDT)
+
+ Problem:
+ On server, Name_Root() attempts (aggressively) to set CVSADM_Root.
+ If ~/CVS/Root exists (wrto rsh login), then CVSADM_Root will be
+ initialized from that file. The sanity check between the root
+ repository and the invocation will fail if the two values are not
+ coincidentally the same.
+
+ Workaround:
+ There's a zillion ways to fix this bugture/featurelet. My current
+ workaround is to remove ~/CVS/Root on the server. I shall attempt
+ a better fix as soon as I can determine what appears politically
+ correct. IMHO, the CVS/Root stuff (and getenv("CVSROOT") also) is
+ a bit fragile and tedious in an rcmd() driven CCVS environment.
+
+
+* (Jeff Johnson <jbj@jbj.org>)
+ I tried a "cvs status -v" and received the following:
+
+ ? CVS
+ ? programs/CVS
+ ? tests/CVS
+ cvs server: Examining .
+ ===================================================================
+ File: Install.dec Status: Up-to-date
+ ...
+
+ I claim that CVS dirs should be ignored.
+
+
+* I sometimes get this message:
+
+ Could not look up address for your host. Permission denied.
+ cvs [update aborted]: premature end of file from server
+
+ The client's response should be cleaned up.
+
+* In the gb-grep module, update-ChangeLog (and therefore, I assume,
+ rcs2log) truncates file names --- I get entries for things called
+ ring/lenstring.h instead of lenstring/lenstring.h.
+
+* On remote checkout, files don't have the right time/date stamps in
+ the CVS/Entries files. Doesn't look like the C/S protocol has any
+ way to send this information along (according to cvsclient.texi).
+ Perhaps we can spiff it up a bit by using the conflict field for the
+ stamp on the checkout/update command. Please note that this really
+ doesn't do very much for us even if we get it done.
+
+* Does the function that lists the available modules in the repository
+ belong under the "checkout" function? Perhaps it is more logically
+ grouped with the "history" function or we should create a new "info"
+ function?
diff --git a/gnu/usr.bin/cvs/NEWS b/gnu/usr.bin/cvs/NEWS
new file mode 100644
index 000000000000..89658192e2b0
--- /dev/null
+++ b/gnu/usr.bin/cvs/NEWS
@@ -0,0 +1,863 @@
+Changes since 1.6:
+
+* RCS keyword "Name" supported for "cvs update -r <tag>" and "cvs
+checkout -r <tag>".
+
+* If there is a group whose name matches a compiled in value which
+defaults to "cvsadmin", only members of that group can use "cvs
+admin".
+
+* CVS now sets the modes of files in the repository based on the
+CVSUMASK environment variable or a compiled in value defaulting to
+002. This way other developers will be able to access the files in
+the repository regardless of the umask of the developer creating them.
+
+* The command name .cvsrc now matches the official name of the
+command, not the one (possibly an alias) by which it was invoked. If
+you had previously relied on "cvs di" and "cvs diff" using different
+options, instead use a shell function or alias (for example "alias
+cvsdi='cvs diff -u'").
+
+Changes from 1.5 to 1.6:
+
+* Del updated the man page to include all of the new features
+of CVS 1.6.
+
+* "cvs tag" now supports a "-r | -D" option for tagging an already
+tagged revision / specific revision of a file.
+
+* There is a "taginfo" file in CVSROOT that supports filtering and
+recording of tag operations.
+
+* Long options support added, including --help and --version options.
+
+* "cvs release" no longer cares whether or not the directory being
+released has an entry in the `modules' file.
+
+* The modules file now takes a -e option which is used instead of -o
+for "cvs export". If your modules file has a -o option which you want
+to be used for "cvs export", change it to specify -e as well as -o.
+
+* "cvs export" now takes a -k option to set RCS keyword expansion.
+This way you can export binary files. If you want the old behavior,
+you need to specify -kv.
+
+* "cvs update", "cvs rdiff", "cvs checkout", "cvs import", "cvs
+release", "cvs rtag", and "cvs tag" used to take -q and -Q options
+after the command name (e.g. "cvs update -q"). This was confusing
+because other commands, such as "cvs ci", did not. So the options
+after the command name have been removed and you must now specify, for
+example, "cvs -q update", which has been supported since CVS 1.3.
+
+* New "wrappers" feature. This allows you to set a hook which
+transforms files on their way in and out of cvs (apparently on the
+NeXT there is some particular usefulness in tarring things up in the
+repository). It also allows you to declare files as merge-by-copy
+which means that instead of trying to merge the file, CVS will merely
+copy the new version. There is a CVSROOT/cvswrappers file and an
+optionsl ~/.cvswrappers file to support this feature.
+
+* You can set CVSROOT to user@host:dir, not just host:dir, if your
+username on the server host is different than on the client host.
+
+* VISUAL is accepted as well as EDITOR.
+
+* $CVSROOT is expanded in *info files.
+
+Changes from 1.4A2 to 1.5:
+
+* Remote implementation. This is very helpful when collaborating on a
+project with someone across a wide-area network. This release can
+also be used locally, like other CVS versions, if you have no need for
+remote access.
+
+Here are some of the features of the remote implementation:
+- It uses reliable transport protocols (TCP/IP) for remote repository
+ access, not NFS. NFS is unusable over long distances (and sometimes
+ over short distances)
+- It transfers only those files that have changed in the repository or
+ the working directory. To save transmission time, it will transfer
+ patches when appropriate, and can compress data for transmission.
+- The server never holds CVS locks while waiting for a reply from the client;
+ this makes the system robust when used over flaky networks.
+
+The remote features are documented in doc/cvsclient.texi in the CVS
+distribution, but the main doc file, cvs.texinfo, has not yet been
+updated to include the remote features.
+
+* Death support. See src/README-rm-add for more information on this.
+
+* Many speedups, especially from jtc@cygnus.com.
+
+* CVS 1.2 compatibility code has been removed as a speedup. If you
+have working directories checked out by CVS 1.2, CVS 1.3 or 1.4A2 will
+try to convert them, but CVS 1.5 and later will not (if the working
+directory is up to date and contains no extraneous files, you can just
+remove it, and then check out a new working directory). Likewise if
+your repository contains a CVSROOT.adm directory instead of a CVSROOT
+directory, you need to rename it.
+
+Fri Oct 21 20:58:54 1994 Brian Berliner <berliner@sun.com>
+
+ * Changes between CVS 1.3 and CVS 1.4 Alpha-2
+
+ * A new program, "cvsbug", is provided to let you send bug reports
+ directly to the CVS maintainers. Please use it instead of sending
+ mail to the info-cvs mailing list. If your build fails, you may
+ have to invoke "cvsbug" directly from the "src" directory as
+ "src/cvsbug.sh".
+
+ * A new User's Guide and Tutorial, written by Per Cederqvist
+ <ceder@signum.se> of Signum Support. See the "doc" directory. A
+ PostScript version is included as "doc/cvs.ps".
+
+ * The Frequesntly Asked Questions file, FAQ, has been added to the
+ release. Unfortunately, its contents are likely out-of-date.
+
+ * The "cvsinit" shell script is now installed in the $prefix/bin
+ directory like the other programs. You can now create new
+ CVS repositories with great ease.
+
+ * Index: lines are now printed on output from 'diff' and 'rdiff',
+ in order to facilitate application of patches to multiple subdirs.
+
+ * Support for a ~/.cvsrc file, which allows you to specify options
+ that are always supposed to be given to a specific command. This
+ feature shows the non-orthogonality of the option set, since while
+ there may be an option to turn something on, the option to turn
+ that same thing off may not exist.
+
+ * You can now list subdirectories that you wish to ignore in a
+ modules listing, such as:
+
+ gcc -a gnu/gcc, !gnu/gcc/testsuites
+
+ which will check out everything underneath gnu/gcc, except
+ everything underneath gnu/gcc/testsuites.
+
+ * It is now much harder to accidentally overwrite an existing tag
+ name, since attempting to move a tag name will result in a error,
+ unless the -F (force) flag is given to the tag subcommands.
+
+ * Better error checking on matching of the repository used to
+ check code out from against the repository the current cvs
+ commnands would use. (Thanks to Mark Baushke <mdb@cisco.com>)
+
+ * Better support for sites with multiple CVSROOT repositories has
+ been contributed. The file "CVS/Root" in your working directory
+ is created to hold the full path to the CVS repository and a
+ simple check is made against your current CVSROOT setting.
+
+ * You can now specify an RCS keyword substitution value when you
+ import files into the repository.
+
+ * Uses a much newer version of Autoconf, and conforms to the GNU
+ coding standards much more closely. No, it still doesn't have
+ long option names.
+
+ * Code cleanup. Many passes through gcc -Wall helped to identify
+ a number of questionable constructs. Most arbitrary length limits
+ were removed.
+
+ * Profiling to determine bottlenecks helped to identify the best
+ places to spend time speeding up the code, which was then done. A
+ number of performance enhancements in filename matching have sped
+ up checkouts.
+
+ * Many more contributions have been added to the "contrib"
+ directory. See the README file in that directory for more
+ information.
+
+ * "cvs commit" will try harder to not change the file's
+ modification time after the commit. If the file does not change
+ as a result of the commit operation, CVS will preserve the
+ original modification time, thus speeding up future make-type
+ builds.
+
+ * "cvs commit" now includes any removed files in the (optional)
+ pre-commit checking program that may be invoked. Previously, only
+ added and modified files were included.
+
+ * It is now possible to commit a file directly onto the trunk at a
+ specific revision level by doing "cvs commit -r3.0 file.c", where
+ "3.0" specifies the revision you wish to create. The file must be
+ up-to-date with the current head of the trunk for this to succeed.
+
+ * "cvs commit" will now function with a pre-commit program that
+ has arguments specified in the "commitinfo" file.
+
+ * The "mkmodules" program will now look within the
+ $CVSROOT/CVSROOT/checkoutlist" file for any additional files that
+ should be automatically checked out within CVSROOT; mkmodules also
+ tries harder to preserve any execute bits the files may have
+ originally had.
+
+ * "cvs diff" is much more accurate about its exit status now. It
+ now returns the maximum exit status of any invoked diff.
+
+ * The "-I !" option is now supported for the import and update
+ commands correctly. It will properly clear the ignore list now.
+
+ * Some problems with "cvs import" handling of .cvsignore have been
+ fixed; as well, some rampant recursion problems with import have
+ also been fixed.
+
+ * "cvs rdiff" (aka "cvs patch") now tries to set the modify time
+ of any temporary files it uses to match those specified for the
+ particular revision. This allows a more accurate patch image to
+ be created.
+
+ * "cvs status" has improved revision descriptions. "Working
+ revision" is used for the revision of the working file that you
+ edit directly; "Repository revision" is the revision of the file
+ with the $CVSROOT source repository. Also, the output is clearer
+ with regard to sticky and branch revisions.
+
+ * CVS no longer dumps core when given a mixture of directories and
+ files in sub-directories (as in "cvs ci file1 dir1/file2").
+ Instead, arguments are now clumped into their respective directory
+ and operated on in chunks, together.
+
+ * If the CVSEDITOR environment variable is set, that editor is
+ used for log messages instead of the EDITOR environment variable.
+ This makes it easy to substitute intelligent programs to make more
+ elaborate log messages. Contributed by Mark D Baushke
+ (mdb@cisco.com).
+
+ * Command argument changes:
+ cvs: The "-f" option has been added to ignore
+ the ~/.cvsrc file.
+ commit: Renamed the "-f logfile" option to the
+ "-F logfile" option. Added the "-f"
+ option to force a commit of the specified
+ files (this disables recursion).
+ history: Added "-t timezone" option to force any
+ date-specific output into the specified
+ timezone.
+ import: Added "-d" option to use the file's
+ modification time as the time of the
+ import. Added "-k sub" option to set the
+ default RCS keyword substitution mode for
+ newly-created files.
+ remove: Added "-f" option to force the file's
+ automatic removal if it still exists in
+ the working directory (use with caution).
+ rtag: Added "-F" option to move the tag if it
+ already exists -- new default is to NOT
+ move tags automatically.
+ tag: Added "-F" option to move the tag if it
+ already exists -- new default is to NOT
+ move tags automatically.
+
+Tue Apr 7 15:55:25 1992 Brian Berliner (berliner at sun.com)
+
+ * Changes between CVS 1.3 Beta-3 and official CVS 1.3!
+
+ * A new shell script is provided, "./cvsinit", which can be run at
+ install time to help setup your $CVSROOT area. This can greatly
+ ease your entry into CVS usage.
+
+ * The INSTALL file has been updated to include the machines on
+ which CVS has compiled successfully. I think CVS 1.3 is finally
+ portable. Thanks to all the Beta testers!
+
+ * Support for the "editinfo" file was contributed. This file
+ (located in $CVSROOT/CVSROOT) can be used to specify a special
+ "editor" to run on a per-directory basis within the repository,
+ instead of the usual user's editor. As such, it can verify that
+ the log message entered by the user is of the appropriate form
+ (contains a bugid and test validation, for example).
+
+ * The manual pages cvs(1) and cvs(5) have been updated.
+
+ * The "mkmodules" command now informs you when your modules file
+ has duplicate entries.
+
+ * The "add" command now preserves any per-directory sticky tag when
+ you add a new directory to your checked-out sources.
+
+ * The "admin" command is now a fully recursive interface to the
+ "rcs" program which operates on your checked-out sources. It no
+ longer requires you to specify the full path to the RCS file.
+
+ * The per-file sticky tags can now be effectively removed with
+ "cvs update -A file", even if you had checked out the whole
+ directory with a per-directory sticky tag. This allows a great
+ deal of flexibility in managing the revisions that your checked-out
+ sources are based upon (both per-directory and per-file sticky
+ tags).
+
+ * The "cvs -n commit" command now works, to show which files are
+ out-of-date and will cause the real commit to fail, or which files
+ will fail any pre-commit checks. Also, the "cvs -n import ..."
+ command will now show you what it would've done without actually
+ doing it.
+
+ * Doing "cvs commit modules" to checkin the modules file will no
+ properly run the "mkmodules" program (assuming you have setup your
+ $CVSROOT/CVSROOT/modules file to do so).
+
+ * The -t option in the modules file (which specifies a program to
+ run when you do a "cvs rtag" operation on a module) now gets the
+ symbolic tag as the second argument when invoked.
+
+ * When the source repository is locked by another user, that user's
+ login name will be displayed as the holder of the lock.
+
+ * Doing "cvs checkout module/file.c" now works even if
+ module/file.c is in the Attic (has been removed from main-line
+ development).
+
+ * Doing "cvs commit */Makefile" now works as one would expect.
+ Rather than trying to commit everything recursively, it will now
+ commit just the files specified.
+
+ * The "cvs remove" command is now fully recursive. To schedule a
+ file for removal, all you have to do is "rm file" and "cvs rm".
+ With no arguments, "cvs rm" will schedule all files that have been
+ physically removed for removal from the source repository at the
+ next "cvs commit".
+
+ * The "cvs tag" command now prints "T file" for each file that was
+ tagged by this invocation and "D file" for each file that had the
+ tag removed (as with "cvs tag -d").
+
+ * The -a option has been added to "cvs rtag" to force it to clean
+ up any old, matching tags for files that have been removed (in the
+ Attic) that may not have been touched by this tag operation. This
+ can help keep a consistent view with your tag, even if you re-use
+ it frequently.
+
+Sat Feb 29 16:02:05 1992 Brian Berliner (berliner at sun.com)
+
+ * Changes between CVS 1.3 Beta-2 and CVS 1.3 Beta-3
+
+ * Many portability fixes, thanks to all the Beta testers! With any
+ luck, this Beta release will compile correctly on most anything.
+ Hey, what are we without our dreams.
+
+ * CVS finally has support for doing isolated development on a
+ branch off the current (or previous!) revisions. This is also
+ extremely nice for generating patches for previously released
+ software while development is progressing on the next release.
+ Here's an example of creating a branch to fix a patch with the 2.0
+ version of the "foo" module, even though we are already well into
+ the 3.0 release. Do:
+
+ % cvs rtag -b -rFOO_2_0 FOO_2_0_Patch foo
+ % cvs checkout -rFOO_2_0_Patch foo
+ % cd foo
+ [[ hack away ]]
+ % cvs commit
+
+ A physical branch will be created in the RCS file only when you
+ actually commit the change. As such, forking development at some
+ random point in time is extremely light-weight -- requiring just a
+ symbolic tag in each file until a commit is done. To fork
+ development at the currently checked out sources, do:
+
+ % cvs tag -b Personal_Hack
+ % cvs update -rPersonal_Hack
+ [[ hack away ]]
+ % cvs commit
+
+ Now, if you decide you want the changes made in the Personal_Hack
+ branch to be merged in with other changes made in the main-line
+ development, you could do:
+
+ % cvs commit # to make Personal_Hack complete
+ % cvs update -A # to update sources to main-line
+ % cvs update -jPersonal_Hack # to merge Personal_Hack
+
+ to update your checked-out sources, or:
+
+ % cvs checkout -jPersonal_Hack module
+
+ to checkout a fresh copy.
+
+ To support this notion of forked development, CVS reserves
+ all even-numbered branches for its own use. In addition, CVS
+ reserves the ".0" and ".1" branches. So, if you intend to do your
+ own branches by hand with RCS, you should use odd-numbered branches
+ starting with ".3", as in "1.1.3", "1.1.5", 1.2.9", ....
+
+ * The "cvs commit" command now supports a fully functional -r
+ option, allowing you to commit your changes to a specific numeric
+ revision or symbolic tag with full consistency checks. Numeric
+ tags are useful for bringing your sources all up to some revision
+ level:
+
+ % cvs commit -r2.0
+
+ For symbolic tags, you can only commit to a tag that references a
+ branch in the RCS file. One created by "cvs rtag -b" or from
+ "cvs tag -b" is appropriate (see below).
+
+ * Roland Pesch <pesch@cygnus.com> and K. Richard Pixley
+ <rich@cygnus.com> were kind enough to contribute two new manual
+ pages for CVS: cvs(1) and cvs(5). Most of the new CVS 1.3 features
+ are now documented, with the exception of the new branch support
+ added to commit/rtag/tag/checkout/update.
+
+ * The -j options of checkout/update have been added. The "cvs join"
+ command has been removed.
+
+ With one -j option, CVS will merge the changes made between the
+ resulting revision and the revision that it is based on (e.g., if
+ the tag refers to a branch, CVS will merge all changes made in
+ that branch into your working file).
+
+ With two -j options, CVS will merge in the changes between the two
+ respective revisions. This can be used to "remove" a certain delta
+ from your working file. E.g., If the file foo.c is based on
+ revision 1.6 and I want to remove the changes made between 1.3 and
+ 1.5, I might do:
+
+ % cvs update -j1.5 -j1.3 foo.c # note the order...
+
+ In addition, each -j option can contain on optional date
+ specification which, when used with branches, can limit the chosen
+ revision to one within a specific date. An optional date is
+ specified by adding a colon (:) to the tag, as in:
+
+ -jSymbolic_Tag:Date_Specifier
+
+ An example might be what "cvs import" tells you to do when you have
+ just imported sources that have conflicts with local changes:
+
+ % cvs checkout -jTAG:yesterday -jTAG module
+
+ which tells CVS to merge in the changes made to the branch
+ specified by TAG in the last 24 hours. If this is not what is
+ intended, substitute "yesterday" for whatever format of date that
+ is appropriate, like:
+
+ % cvs checkout -jTAG:'1 week ago' -jTAG module
+
+ * "cvs diff" now supports the special tags "BASE" and "HEAD". So,
+ the command:
+
+ % cvs diff -u -rBASE -rHEAD
+
+ will effectively show the changes made by others (in unidiff
+ format) that will be merged into your working sources with your
+ next "cvs update" command. "-rBASE" resolves to the revision that
+ your working file is based on. "-rHEAD" resolves to the current
+ head of the branch or trunk that you are working on.
+
+ * The -P option of "cvs checkout" now means to Prune empty
+ directories, as with "update". The default is to not remove empty
+ directories. However, if you do "checkout" with any -r options, -P
+ will be implied. I.e., checking out with a tag will cause empty
+ directories to be pruned automatically.
+
+ * The new file INSTALL describes how to install CVS, including
+ detailed descriptions of interfaces to "configure".
+
+ * The example loginfo file in examples/loginfo has been updated to
+ use the perl script included in contrib/log.pl. The nice thing
+ about this log program is that it records the revision numbers of
+ your change in the log message.
+
+ Example files for commitinfo and rcsinfo are now included in the
+ examples directory.
+
+ * All "#if defined(__STDC__) && __STDC__ == 1" lines have been
+ changed to be "#if __STDC__" to fix some problems with the former.
+
+ * The lib/regex.[ch] files have been updated to the 1.3 release of
+ the GNU regex package.
+
+ * The ndbm emulation routines included with CVS 1.3 Beta-2 in the
+ src/ndbm.[ch] files has been moved into the src/myndbm.[ch] files
+ to avoid any conflict with the system <ndbm.h> header file. If
+ you had a previous CVS 1.3 Beta release, you will want to "cvs
+ remove ndbm.[ch]" form your copy of CVS as well.
+
+ * "cvs add" and "cvs remove" are a bit more verbose, telling you
+ what to do to add/remove your file permanently.
+
+ * We no longer mess with /dev/tty in "commit" and "add".
+
+ * More things are quiet with the -Q option set.
+
+ * New src/config.h option: If CVS_BADROOT is set, CVS will not
+ allow people really logged in as "root" to commit changes.
+
+ * "cvs diff" exits with a status of 0 if there were no diffs, 1 if
+ there were diffs, and 2 if there were errors.
+
+ * "cvs -n diff" is now supported so that you can still run diffs
+ even while in the middle of committing files.
+
+ * Handling of the CVS/Entries file is now much more robust.
+
+ * The default file ignore list now includes "*.so".
+
+ * "cvs import" did not expand '@' in the log message correctly. It
+ does now. Also, import now uses the ignore file facility
+ correctly.
+
+ Import will now tell you whether there were conflicts that need to
+ be resolved, and how to resolve them.
+
+ * "cvs log" has been changed so that you can "log" things that are
+ not a part of the current release (in the Attic).
+
+ * If you don't change the editor message on commit, CVS now prompts
+ you with the choice:
+
+ !)reuse this message unchanged for remaining dirs
+
+ which allows you to tell CVS that you have no intention of changing
+ the log message for the remainder of the commit.
+
+ * It is no longer necessary to have CVSROOT set if you are using
+ the -H option to get Usage information on the commands.
+
+ * Command argument changes:
+ checkout: -P handling changed as described above.
+ New -j option (up to 2 can be specified)
+ for doing rcsmerge kind of things on
+ checkout.
+ commit: -r option now supports committing to a
+ numeric or symbolic tags, with some
+ restrictions. Full consistency checks will
+ be done.
+ Added "-f logfile" option, which tells
+ commit to glean the log message from the
+ specified file, rather than invoking the
+ editor.
+ rtag: Added -b option to create a branch tag,
+ useful for creating a patch for a previous
+ release, or for forking development.
+ tag: Added -b option to create a branch tag,
+ useful for creating a patch for a previous
+ release, or for forking development.
+ update: New -j option (up to 2 can be specified)
+ for doing rcsmerge kind of things on
+ update.
+
+Thu Jan 9 10:51:35 MST 1992 Jeff Polk (polk at BSDI.COM)
+
+ * Changes between CVS 1.3 Beta-1 and CVS 1.3 Beta-2
+
+ * Thanks to K. Richard Pixley at Cygnus we now have function
+ prototypes in all the files
+
+ * Some small changes to configure for portability. There have
+ been other portability problems submitted that have not been fixed
+ (Brian will be working on those). Additionally all __STDC__
+ tests have been modified to check __STDC__ against the constant 1
+ (this is what the Second edition of K&R says must be true).
+
+ * Lots of additional error checking for forked processes (run_exec)
+ (thanks again to K. Richard Pixley)
+
+ * Lots of miscellaneous bug fixes - including but certainly not
+ limited to:
+ various commit core dumps
+ various update core dumps
+ bogus results from status with numeric sticky tags
+ commitprog used freed memory
+ Entries file corruption caused by No_Difference
+ commit to revision broken (now works if branch exists)
+ ignore file processing broken for * and !
+ ignore processing didn't handle memory reasonably
+ miscellaneous bugs in the recursion processor
+ file descriptor leak in ParseInfo
+ CVSROOT.adm->CVSROOT rename bug
+ lots of lint fixes
+
+ * Reformatted all the code in src (with GNU indent) and then
+ went back and fixed prototypes, etc since indent gets confused. The
+ rationale is that it is better to do it sooner than later and now
+ everything is consistent and will hopefully stay that way.
+ The basic options to indent were: "-bad -bbb -bap -cdb -d0 -bl -bli0
+ -nce -pcs -cs -cli4 -di1 -nbc -psl -lp -i4 -ip4 -c41" and then
+ miscellaneous formatting fixes were applied. Note also that the
+ "-nfc1" or "-nfca" may be appropriate in files where comments have
+ been carefully formatted (e.g, modules.c).
+
+Sat Dec 14 20:35:22 1991 Brian Berliner (berliner at sun.com)
+
+ * Changes between CVS 1.2 and CVS 1.3 Beta are described here.
+
+ * Lots of portability work. CVS now uses the GNU "configure"
+ script to dynamically determine the features provided by your
+ system. It probably is not foolproof, but it is better than
+ nothing. Please let me know of any portability problems. Some
+ file names were changed to fit within 14-characters.
+
+ * CVS has a new RCS parser that is much more flexible and
+ extensible. It should read all known RCS ",v" format files.
+
+ * Most of the commands now are fully recursive, rather than just
+ operating on the current directory alone. This includes "commit",
+ which makes it real easy to do an "atomic" commit of all the
+ changes made to a CVS hierarchy of sources. Most of the commands
+ also correctly handle file names that are in directories other than
+ ".", including absolute path names. Commands now accept the "-R"
+ option to force recursion on (though it is always the default now)
+ and the "-l" option to force recursion off, doing just "." and not
+ any sub-directories.
+
+ * CVS supports many of the features provided with the RCS 5.x
+ distribution - including the new "-k" keyword expansion options. I
+ recommend using RCS 5.x (5.6 is the current official RCS version)
+ and GNU diff 1.15 (or later) distributions with CVS.
+
+ * Checking out files with symbolic tags/dates is now "sticky", in
+ that CVS remembers the tag/date used for each file (and directory)
+ and will use that tag/date automatically on the next "update" call.
+ This stickyness also holds for files checked out with the the new
+ RCS 5.x "-k" options.
+
+ * The "cvs diff" command now recognizes all of the rcsdiff 5.x
+ options. Unidiff format is available by installing the GNU
+ diff 1.15 distribution.
+
+ * The old "CVS.adm" directories created on checkout are now called
+ "CVS" directories, to look more like "RCS" and "SCCS". Old CVS.adm
+ directories are automagically converted to CVS directories. The
+ old "CVSROOT.adm" directory within the source repository is
+ automagically changed into a "CVSROOT" directory as well.
+
+ * Symbolic links in the source repository are fully supported ONLY
+ if you use RCS 5.6 or later and (of course) your system supports
+ symlinks.
+
+ * A history database has been contributed which maintains the
+ history of certain CVS operations, as well as providing a wide array
+ of querying options.
+
+ * The "cvs" program has a "-n" option which can be used with the
+ "update" command to show what would be updated without actually
+ doing the update, like: "cvs -n update". All usage statements
+ have been cleaned up and made more verbose.
+
+ * The module database parsing has been rewritten. The new format
+ is compatible with the old format, but with much more
+ functionality. It allows modules to be created that grab pieces or
+ whole directories from various different parts of your source
+ repository. Module-relative specifications are also correctly
+ recognized now, like "cvs checkout module/file.c".
+
+ * A configurable template can be specified such that on a "commit",
+ certain directories can supply a template that the user must fill
+ before completing the commit operation.
+
+ * A configurable pre-commit checking program can be specified which
+ will run to verify that a "commit" can happen. This feature can be
+ used to restrict certain users from changing certain pieces of the
+ source repository, or denying commits to the entire source
+ repository.
+
+ * The new "cvs export" command is much like "checkout", but
+ establishes defaults suitable for exporting code to others (expands
+ out keywords, forces the use of a symbolic tag, and does not create
+ "CVS" directories within the checked out sources.
+
+ * The new "cvs import" command replaces the deprecated "checkin"
+ shell script and is used to import sources into CVS control. It is
+ also much faster for the first-time import. Some algorithmic
+ improvements have also been made to reduce the number of
+ conflicting files on next-time imports.
+
+ * The new "cvs admin" command is basically an interface to the
+ "rcs" program. (Not yet implemented very well).
+
+ * Signal handling (on systems with BSD or POSIX signals) is much
+ improved. Interrupting CVS now works with a single interrupt!
+
+ * CVS now invokes RCS commands by direct fork/exec rather than
+ calling system(3). This improves performance by removing a call to
+ the shell to parse the arguments.
+
+ * Support for the .cvsignore file has been contributed. CVS will
+ now show "unknown" files as "? filename" as the result of an "update"
+ command. The .cvsignore file can be used to add files to the
+ current list of ignored files so that they won't show up as unknown.
+
+ * Command argument changes:
+ cvs: Added -l to turn off history logging.
+ Added -n to show what would be done without actually
+ doing anything.
+ Added -q/-Q for quiet and really quiet settings.
+ Added -t to show debugging trace.
+ add: Added -k to allow RCS 5.x -k options to be specified.
+ admin: New command; an interface to rcs(1).
+ checkout: Added -A to reset sticky tags/date/options.
+ Added -N to not shorten module paths.
+ Added -R option to force recursion.
+ Changed -p (prune empty directories) to -P option.
+ Changed -f option; forcing tags match is now default.
+ Added -p option to checkout module to standard output.
+ Added -s option to cat the modules db with status.
+ Added -d option to checkout in the specified directory.
+ Added -k option to use RCS 5.x -k support.
+ commit: Removed -a option; use -l instead.
+ Removed -f option.
+ Added -l option to disable recursion.
+ Added -R option to force recursion.
+ If no files specified, commit is recursive.
+ diff: Now recognizes all RCS 5.x rcsdiff options.
+ Added -l option to disable recursion.
+ Added -R option to force recursion.
+ history: New command; displays info about CVS usage.
+ import: Replaces "checkin" shell script; imports sources
+ under CVS control. Ignores files on the ignore
+ list (see -I option or .cvsignore description above).
+ export: New command; like "checkout", but w/special options
+ turned on by default to facilitate exporting sources.
+ join: Added -B option to join from base of the branch;
+ join now defaults to only joining with the top two
+ revisions on the branch.
+ Added -k option for RCS 5.x -k support.
+ log: Supports all RCS 5.x options.
+ Added -l option to disable recursion.
+ Added -R option to force recursion.
+ patch: Changed -f option; forcing tags match is now default.
+ Added -c option to force context-style diffs.
+ Added -u option to support unidiff-style diffs.
+ Added -V option to support RCS specific-version
+ keyword expansion formats.
+ Added -R option to force recursion.
+ remove: No option changes. It's a bit more verbose.
+ rtag: Equivalent to the old "cvs tag" command.
+ No option changes. It's a lot faster for re-tag.
+ status: New output formats with more information.
+ Added -l option to disable recursion.
+ Added -R option to force recursion.
+ Added -v option to show symbolic tags for files.
+ tag: Functionality changed to tag checked out files
+ rather than modules; use "rtag" command to get the
+ old "cvs tag" behaviour.
+ update: Added -A to reset sticky tags/date/options.
+ Changed -p (prune empty directories) to -P option.
+ Changed -f option; forcing tags match is now default.
+ Added -p option to checkout module to standard output.
+ Added -I option to add files to the ignore list.
+ Added -R option to force recursion.
+
+ Major Contributors:
+
+ * Jeff Polk <polk@bsdi.com> rewrote most of the grody code of CVS
+ 1.2. He made just about everything dynamic (by using malloc),
+ added a generic hashed list manager, re-wrote the modules database
+ parsing in a compatible - but extended way, generalized directory
+ hierarchy recursion for virtually all the commands (including
+ commit!), generalized the loginfo file to be used for pre-commit
+ checks and commit templates, wrote a new and flexible RCS parser,
+ fixed an uncountable number of bugs, and helped in the design of
+ future CVS features. If there's anything gross left in CVS, it's
+ probably my fault!
+
+ * David G. Grubbs <dgg@odi.com> contributed the CVS "history" and
+ "release" commands. As well as the ever-so-useful "-n" option of
+ CVS which tells CVS to show what it would do, without actually
+ doing it. He also contributed support for the .cvsignore file.
+
+ * Paul Sander, HaL Computer Systems, Inc. <paul@hal.com> wrote and
+ contributed the code in lib/sighandle.c. I added support for
+ POSIX, BSD, and non-POSIX/non-BSD systems.
+
+ * Free Software Foundation contributed the "configure" script and
+ other compatibility support in the "lib" directory, which will help
+ make CVS much more portable.
+
+ * Many others have contributed bug reports and enhancement requests.
+ Some have even submitted actual code which I have not had time yet
+ to integrate into CVS. Maybe for the next release.
+
+ * Thanks to you all!
+
+Wed Feb 6 10:10:58 1991 Brian Berliner (berliner at sun.com)
+
+ * Changes from CVS 1.0 Patchlevel 1 to CVS 1.0 Patchlevel 2; also
+ known as "Changes from CVS 1.1 to CVS 1.2".
+
+ * Major new support with this release is the ability to use the
+ recently-posted RCS 5.5 distribution with CVS 1.2. See below for
+ other assorted bug-fixes that have been thrown in.
+
+ * ChangeLog (new): Added Emacs-style change-log file to CVS 1.2
+ release. Chronological description of changes between release.
+
+ * README: Small fixes to installation instructions. My email
+ address is now "berliner@sun.com".
+
+ * src/Makefile: Removed "rcstime.h". Removed "depend" rule.
+
+ * src/partime.c: Updated to RCS 5.5 version with hooks for CVS.
+ * src/maketime.c: Updated to RCS 5.5 version with hooks for CVS.
+ * src/rcstime.h: Removed from the CVS 1.2 distribution.
+ Thanks to Paul Eggert <eggert@twinsun.com> for these changes.
+
+ * src/checkin.csh: Support for RCS 5.5 parsing.
+ Thanks to Paul Eggert <eggert@twinsun.com> for this change.
+
+ * src/collect_sets.c (Collect_Sets): Be quieter if "-f" option is
+ specified. When checking out files on-top-of other files that CVS
+ doesn't know about, run a diff in the hopes that they are really
+ the same file before aborting.
+
+ * src/commit.c (branch_number): Fix for RCS 5.5 parsing.
+ Thanks to Paul Eggert <eggert@twinsun.com> for this change.
+
+ * src/commit.c (do_editor): Bug fix - fprintf missing argument
+ which sometimes caused core dumps.
+
+ * src/modules.c (process_module): Properly NULL-terminate
+ update_dir[] in all cases.
+
+ * src/no_difference.c (No_Difference): The wrong RCS revision was
+ being registered in certain (strange) cases.
+
+ * src/patch.c (get_rcsdate): New algorithm. No need to call
+ maketime() any longer.
+ Thanks to Paul Eggert <eggert@twinsun.com> for this change.
+
+ * src/patchlevel.h: Increased patch level to "2".
+
+ * src/subr.c (isdir, islink): Changed to compare stat mode bits
+ correctly.
+
+ * src/tag.c (tag_file): Added support for following symbolic links
+ that are in the master source repository when tagging. Made tag
+ somewhat quieter in certain cases.
+
+ * src/update.c (update_process_lists): Unlink the user's file if it
+ was put on the Wlist, meaning that the user's file is not modified
+ and its RCS file has been removed by someone else.
+
+ * src/update.c (update): Support for "cvs update dir" to correctly
+ just update the argument directory "dir".
+
+ * src/cvs.h: Fixes for RCS 5.5 parsing.
+ * src/version_number.c (Version_Number): Fixes for parsing RCS 5.5
+ and older RCS-format files.
+ Thanks to Paul Eggert <eggert@twinsun.com> for these changes.
+
+ * src/version_number.c (Version_Number): Bug fixes for "-f" option.
+ Bug fixes for parsing with certain branch numbers. RCS
+ revision/symbol parsing is much more solid now.
+
+Wed Feb 14 10:01:33 1990 Brian Berliner (berliner at sun.com)
+
+ * Changes from CVS 1.0 Patchlevel 0 to CVS 1.0 Patchlevel 1; also
+ known as "Changes from CVS 1.0 to CVS 1.1".
+
+ * src/patch.c (get_rcsdate): Portability fix. Replaced call to
+ timelocal() with call to maketime().
+
+Mon Nov 19 23:15:11 1990 Brian Berliner (berliner at prisma.com)
+
+ * Sent CVS 1.0 release to comp.sources.unix moderator and FSF.
+
+ * Special thanks to Dick Grune <dick@cs.vu.nl> for his work on the
+ 1986 version of CVS and making it available to the world. Dick's
+ version is available on uunet.uu.net in the
+ comp.sources.unix/volume6/cvs directory.
+
+$CVSid: @(#)ChangeLog 1.35 94/10/22 $
diff --git a/gnu/usr.bin/cvs/PROJECTS b/gnu/usr.bin/cvs/PROJECTS
new file mode 100644
index 000000000000..de765760631e
--- /dev/null
+++ b/gnu/usr.bin/cvs/PROJECTS
@@ -0,0 +1,59 @@
+This is a list of projects for CVS. In general, unlike the things in
+the TODO file, these need more analysis to determine if and how
+worthwhile each task is.
+
+I haven't gone through TODO, but it's likely that it has entries that
+are actually more appropriate for this list.
+
+0. Improved Efficency
+
+* CVS uses a single doubly linked list/hash table data structure for
+ all of its lists. Since the back links are only used for deleting
+ list nodes it might be beneficial to use singly linked lists or a
+ tree structure. Most likely, a single list implementation will not
+ be appropriate for all uses.
+
+ One easy change would be to remove the "type" field out of the list
+ and node structures. I have found it to be of very little use when
+ debugging, and each instance eats up a word of memory. This can add
+ up and be a problem on memory-starved machines.
+
+ Profiles have shown that on fast machines like the Alpha, fsortcmp()
+ is one of the hot spots.
+
+* Dynamically allocated character strings are created, copied, and
+ destroyed throughout CVS. The overhead of malloc()/strcpy()/free()
+ needs to be measured. If significant, it could be minimized by using a
+ reference counted string "class".
+
+* File modification time is stored as a character string. It might be
+ worthwile to use a time_t internally if the time to convert a time_t
+ (from struct stat) to a string is greater that the time to convert a
+ ctime style string (from the entries file) to a time_t. time_t is
+ an machine-dependant type (although it's pretty standard on UN*X
+ systems), so we would have to have different conversion routines.
+ Profiles show that both operations are called about the same number
+ of times.
+
+* stat() is one of the largest performance bottlenecks on systems
+ without the 4.4BSD filesystem. By spliting information out of
+ the filesystem (perhaps the "rename database") we should be
+ able to improve performance.
+
+* Parsing RCS files is very expensive. This might be unnecessary if
+ RCS files are only used as containers for revisions, and tag,
+ revision, and date information was available in easy to read
+ (and modify) indexes. This becomes very apparent with files
+ with several hundred revisions.
+
+* A RCS "library", so CVS could operate on RCS files directly.
+
+ CVS parses RCS files in order to determine if work needs to be done,
+ and then RCS parses the files again when it is performing the work.
+ This would be much faster if CVS could do whatever is necessary
+ by itself.
+
+1. Improved testsuite/sanity check script
+
+* Need to use a code coverage tool to determine how much the sanity
+ script tests, and fill in the holes.
diff --git a/gnu/usr.bin/cvs/contrib/Makefile b/gnu/usr.bin/cvs/contrib/Makefile
new file mode 100644
index 000000000000..3e21c047ec13
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/Makefile
@@ -0,0 +1,37 @@
+# $Id: Makefile,v 1.3 1995/12/11 01:23:42 peter Exp $
+
+SUBDIR= pcl-cvs
+
+SCRIPTS= ccvs-rsh rcs2log clmerge cln_hist commit_prep cvs_acls cvscheck \
+ log log_accum mfpipe rcs-to-cvs rcs2log rcslock sccs2rcs \
+ easy-import
+
+FILES= README cvscheck.man cvshelp.man descend.man intro.doc
+
+EXAMPDIR= /usr/share/examples/cvs
+PERLPATH= /usr/bin/perl
+CLEANFILES+= $(SCRIPTS)
+
+.SUFFIXES: .sh .csh .pl
+
+all: ${SCRIPTS}
+
+.sh:
+ cp $< $@
+
+.csh:
+ sed -e 's,xCSH_PATHx,/bin/csh,' $< > $@
+
+.pl:
+ sed -e 's,xPERL_PATHx,$(PERLPATH),' $< > $@
+
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${SCRIPTS} ${DESTDIR}${EXAMPDIR}/contrib
+ cd ${.CURDIR} ; \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 \
+ ${FILES} ${DESTDIR}${EXAMPDIR}/contrib
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cvs/contrib/ccvs-rsh.pl b/gnu/usr.bin/cvs/contrib/ccvs-rsh.pl
new file mode 100644
index 000000000000..f01c66ae392d
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/ccvs-rsh.pl
@@ -0,0 +1,97 @@
+#! xPERL_PATHx
+
+# The version of the remote shell program on some Linuxes, at least,
+# misuses GNU getopt in such a way that it plucks arguments to rsh
+# that look like command-line switches from anywhere in rsh's
+# arguments. This is the Wrong Thing to do, and causes older versions
+# of CCVS to break.
+
+# In addition, if we live behind a firewall and have to construct a
+# "pipeline" of rshes through different machines in order to get to
+# the outside world, each rshd along the way undoes the hard work CCVS
+# does to put the command to be executed at the far end into a single
+# argument. Sigh.
+
+# This script is a very minimal wrapper to rsh which makes sure that
+# the commands to be executed remotely are packed into a single
+# argument before we call exec(). It works on the idea of a "proxy
+# chain", which is a set of machines you go through to get to the CCVS
+# server machine.
+
+# Each host you go through before you reach the CCVS server machine
+# should have a copy of this script somewhere (preferably accessible
+# directly from your PATH envariable). In addition, each host you go
+# through before you reach the firewall should have the CVS_PROXY_HOST
+# envariable set to the next machine in the chain, and CVS_PROXY_USER
+# set if necessary.
+
+# This really isn't as complex as it sounds. Honest.
+
+# Bryan O'Sullivan <bos@serpentine.com> April 1995
+
+$usage = "usage: ccvs-rsh hostname [-l username] command [...]\n";
+
+if ($#ARGV < 1) {
+ print STDERR $usage;
+ exit 1;
+}
+
+# Try to pick a sane version of the remote shell command to run. This
+# only understands BSD and Linux machines; if your remote shell is
+# called "remsh" under some System V (e.g. HP-SUX), you should edit
+# the line manually to suit yourself.
+
+$rsh = (-x "/usr/ucb/rsh") ? "/usr/ucb/rsh" : "/usr/bin/rsh";
+
+# If you are not rshing directly to the CCVS server machine, make the
+# following variable point at ccvs-rsh on the next machine in the
+# proxy chain. If it's accessible through the PATH envariable, you
+# can just set this to "ccvs-rsh".
+
+$ccvs_rsh = "ccvs-rsh";
+
+# There shouldn't be any user-serviceable parts beyond this point.
+
+$host = $ARGV[0];
+
+if ($ARGV[1] eq "-l") {
+ if ($#ARGV < 3) {
+ print STDERR $usage;
+ exit 1;
+ }
+ $user = $ARGV[2];
+ $cbase = 3;
+} else {
+ $cbase = 1;
+}
+
+# You might think you shoul be able to do something like
+# $command = join(' ', $ARGV[$cbase..$#ARGV]);
+# to achieve the effect of the following block of code, but it doesn't
+# work under Perl 4 on Linux, at least. Sigh.
+
+$command = $ARGV[$cbase];
+for ($cbase++; $cbase <= $#ARGV; $cbase++) {
+ $command .= " " . $ARGV[$cbase];
+}
+
+if (defined $ENV{"CVS_PROXY_HOST"}) {
+ $command = (defined $user)
+ ? "$ccvs_rsh $host -l $user $command"
+ : "$ccvs_rsh $host $command";
+
+ if (defined $ENV{"CVS_PROXY_USER"}) {
+ exec ($rsh, $ENV{"CVS_PROXY_HOST"}, "-l", $ENV{"CVS_PROXY_USER"},
+ $command);
+ } else {
+ exec ($rsh, $ENV{"CVS_PROXY_HOST"}, $command);
+ }
+} elsif (defined $user) {
+ exec ($rsh, $host, "-l", $user, $command);
+} else {
+ if (defined $ENV{"CVS_PROXY_USER"}) {
+ exec ($rsh, $host, "-l", $ENV{"CVS_PROXY_USER"}, $command);
+ } else {
+ exec ($rsh, $host, $command);
+ }
+}
diff --git a/gnu/usr.bin/cvs/contrib/clmerge.pl b/gnu/usr.bin/cvs/contrib/clmerge.pl
new file mode 100644
index 000000000000..ac813713a827
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/clmerge.pl
@@ -0,0 +1,152 @@
+#! xPERL_PATHx
+
+# Merge conflicted ChangeLogs
+# tromey Mon Aug 15 1994
+
+# Usage is:
+#
+# cl-merge [-i] file ...
+#
+# With -i, it works in place (backups put in a ~ file). Otherwise the
+# merged ChangeLog is printed to stdout.
+
+# Please report any bugs to me. I wrote this yesterday, so there are no
+# guarantees about its performance. I recommend checking its output
+# carefully. If you do send a bug report, please include the failing
+# ChangeLog, so I can include it in my test suite.
+#
+# Tom
+# ---
+# tromey@busco.lanl.gov Member, League for Programming Freedom
+# Sadism and farce are always inexplicably linked.
+# -- Alexander Theroux
+
+
+# Month->number mapping. Used for sorting.
+%months = ('Jan', 0,
+ 'Feb', 1,
+ 'Mar', 2,
+ 'Apr', 3,
+ 'May', 4,
+ 'Jun', 5,
+ 'Jul', 6,
+ 'Aug', 7,
+ 'Sep', 8,
+ 'Oct', 9,
+ 'Nov', 10,
+ 'Dec', 11);
+
+# If '-i' is given, do it in-place.
+if ($ARGV[0] eq '-i') {
+ shift (@ARGV);
+ $^I = '~';
+}
+
+$lastkey = '';
+$lastval = '';
+$conf = 0;
+%conflist = ();
+
+$tjd = 0;
+
+# Simple state machine. The states:
+#
+# 0 Not in conflict. Just copy input to output.
+# 1 Beginning an entry. Next non-blank line is key.
+# 2 In entry. Entry beginner transitions to state 1.
+while (<>) {
+ if (/^<<<</ || /^====/) {
+ # Start of a conflict.
+
+ # Copy last key into array.
+ if ($lastkey ne '') {
+ $conflist{$lastkey} = $lastval;
+
+ $lastkey = '';
+ $lastval = '';
+ }
+
+ $conf = 1;
+ } elsif (/^>>>>/) {
+ # End of conflict. Output.
+
+ # Copy last key into array.
+ if ($lastkey ne '') {
+ $conflist{$lastkey} = $lastval;
+
+ $lastkey = '';
+ $lastval = '';
+ }
+
+ foreach (reverse sort clcmp keys %conflist) {
+ print STDERR "doing $_" if $tjd;
+ print $_;
+ print $conflist{$_};
+ }
+
+ $lastkey = '';
+ $lastval = '';
+ $conf = 0;
+ %conflist = ();
+ } elsif ($conf == 1) {
+ # Beginning an entry. Skip empty lines. Error if not a real
+ # beginner.
+ if (/^$/) {
+ # Empty line; just skip at this point.
+ } elsif (/^[MTWFS]/) {
+ # Looks like the name of a day; assume opener and move to
+ # "in entry" state.
+ $lastkey = $_;
+ $conf = 2;
+ print STDERR "found $_" if $tjd;
+ } else {
+ die ("conflict crosses entry boundaries: $_");
+ }
+ } elsif ($conf == 2) {
+ # In entry. Copy into variable until we see beginner line.
+ if (/^[MTWFS]/) {
+ # Entry beginner line.
+
+ # Copy last key into array.
+ if ($lastkey ne '') {
+ $conflist{$lastkey} = $lastval;
+
+ $lastkey = '';
+ $lastval = '';
+ }
+
+ $lastkey = $_;
+ print STDERR "found $_" if $tjd;
+ $lastval = '';
+ } else {
+ $lastval .= $_;
+ }
+ } else {
+ # Just copy.
+ print;
+ }
+}
+
+# Compare ChangeLog time strings like <=>.
+#
+# 0 1 2 3
+# Thu Aug 11 13:22:42 1994 Tom Tromey (tromey@creche.colorado.edu)
+# 0123456789012345678901234567890
+#
+sub clcmp {
+ # First check year.
+ $r = substr ($a, 20, 4) <=> substr ($b, 20, 4);
+
+ # Now check month.
+ $r = $months{substr ($a, 4, 3)} <=> $months{substr ($b, 4, 3)} if !$r;
+
+ # Now check day.
+ $r = substr ($a, 8, 2) <=> substr ($b, 8, 2) if !$r;
+
+ # Now check time (3 parts).
+ $r = substr ($a, 11, 2) <=> substr ($b, 11, 2) if !$r;
+ $r = substr ($a, 14, 2) <=> substr ($b, 14, 2) if !$r;
+ $r = substr ($a, 17, 2) <=> substr ($b, 17, 2) if !$r;
+
+ $r;
+}
diff --git a/gnu/usr.bin/cvs/contrib/cvscheck.sh b/gnu/usr.bin/cvs/contrib/cvscheck.sh
new file mode 100644
index 000000000000..96dba6e1f87e
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvscheck.sh
@@ -0,0 +1,84 @@
+#! /bin/sh
+# $Id: cvscheck.sh,v 1.1 1995/07/10 02:26:29 kfogel Exp $
+#
+# cvscheck - identify files added, changed, or removed
+# in CVS working directory
+#
+# Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+#
+# This program should be run in a working directory that has been
+# checked out using CVS. It identifies files that have been added,
+# changed, or removed in the working directory, but not "cvs
+# committed". It also determines whether the files have been "cvs
+# added" or "cvs removed". For directories, it is only practical to
+# determine whether they have been added.
+
+name=cvscheck
+changes=0
+
+# If we can't run CVS commands in this directory
+cvs status . > /dev/null 2>&1
+if [ $? != 0 ] ; then
+
+ # Bail out
+ echo "$name: there is no version here; bailing out" 1>&2
+ exit 1
+fi
+
+# Identify files added to working directory
+for file in .* * ; do
+
+ # Skip '.' and '..'
+ if [ $file = '.' -o $file = '..' ] ; then
+ continue
+ fi
+
+ # If a regular file
+ if [ -f $file ] ; then
+ if cvs status $file | grep -s '^From:[ ]*New file' ; then
+ echo "file added: $file - not CVS committed"
+ changes=`expr $changes + 1`
+ elif cvs status $file | grep -s '^From:[ ]*no entry for' ; then
+ echo "file added: $file - not CVS added, not CVS committed"
+ changes=`expr $changes + 1`
+ fi
+
+ # Else if a directory
+ elif [ -d $file -a $file != CVS.adm ] ; then
+
+ # Move into it
+ cd $file
+
+ # If CVS commands don't work inside
+ cvs status . > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "directory added: $file - not CVS added"
+ changes=`expr $changes + 1`
+ fi
+
+ # Move back up
+ cd ..
+ fi
+done
+
+# Identify changed files
+changedfiles=`cvs diff | egrep '^diff' | awk '{print $3}'`
+for file in $changedfiles ; do
+ echo "file changed: $file - not CVS committed"
+ changes=`expr $changes + 1`
+done
+
+# Identify files removed from working directory
+removedfiles=`cvs status | egrep '^File:[ ]*no file' | awk '{print $4}'`
+
+# Determine whether each file has been cvs removed
+for file in $removedfiles ; do
+ if cvs status $file | grep -s '^From:[ ]*-' ; then
+ echo "file removed: $file - not CVS committed"
+ else
+ echo "file removed: $file - not CVS removed, not CVS committed"
+ fi
+ changes=`expr $changes + 1`
+done
+
+exit $changes
diff --git a/gnu/usr.bin/cvs/contrib/descend.sh b/gnu/usr.bin/cvs/contrib/descend.sh
new file mode 100644
index 000000000000..e6a788079416
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/descend.sh
@@ -0,0 +1,116 @@
+#! /bin/sh
+# $Id: descend.sh,v 1.1 1995/07/10 02:26:32 kfogel Exp $
+#
+# descend - walk down a directory tree and execute a command at each node
+
+fullname=$0
+name=descend
+usage="Usage: $name [-afqrv] command [directory ...]\n
+\040\040-a\040\040All: descend into directories starting with '.'\n
+\040\040-f\040\040Force: ignore errors during descent\n
+\040\040-q\040\040Quiet: don't print directory names\n
+\040\040-r\040\040Restricted: don't descend into RCS, CVS.adm, SCCS directories\n
+\040\040-v\040\040Verbose: print command before executing it"
+
+# Scan for options
+while getopts afqrv option; do
+ case $option in
+ a)
+ alldirs=$option
+ options=$options" "-$option
+ ;;
+ f)
+ force=$option
+ options=$options" "-$option
+ ;;
+ q)
+ verbose=
+ quiet=$option
+ options=$options" "-$option
+ ;;
+ r)
+ restricted=$option
+ options=$options" "-$option
+ ;;
+ v)
+ verbose=$option
+ quiet=
+ options=$options" "-$option
+ ;;
+ \?)
+ /usr/5bin/echo $usage 1>&2
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+# Get command to execute
+if [ $# -lt 1 ] ; then
+ /usr/5bin/echo $usage 1>&2
+ exit 1
+else
+ command=$1
+ shift
+fi
+
+# If no directory specified, use '.'
+if [ $# -lt 1 ] ; then
+ default_dir=.
+fi
+
+# For each directory specified
+for dir in $default_dir "$@" ; do
+
+ # Spawn sub-shell so we return to starting directory afterward
+ (cd $dir
+
+ # Execute specified command
+ if [ -z "$quiet" ] ; then
+ echo In directory `hostname`:`pwd`
+ fi
+ if [ -n "$verbose" ] ; then
+ echo $command
+ fi
+ eval "$command" || if [ -z "$force" ] ; then exit 1; fi
+
+ # Collect dot file names if necessary
+ if [ -n "$alldirs" ] ; then
+ dotfiles=.*
+ else
+ dotfiles=
+ fi
+
+ # For each file in current directory
+ for file in $dotfiles * ; do
+
+ # Skip '.' and '..'
+ if [ "$file" = "." -o "$file" = ".." ] ; then
+ continue
+ fi
+
+ # If a directory but not a symbolic link
+ if [ -d "$file" -a ! -h "$file" ] ; then
+
+ # If not skipping this type of directory
+ if [ \( "$file" != "RCS" -a \
+ "$file" != "SCCS" -a \
+ "$file" != "CVS" -a \
+ "$file" != "CVS.adm" \) \
+ -o -z "$restricted" ] ; then
+
+ # Recursively descend into it
+ $fullname $options "$command" "$file" \
+ || if [ -z "$force" ] ; then exit 1; fi
+ fi
+
+ # Else if a directory AND a symbolic link
+ elif [ -d "$file" -a -h "$file" ] ; then
+
+ if [ -z "$quiet" ] ; then
+ echo In directory `hostname`:`pwd`/$file: symbolic link: skipping
+ fi
+ fi
+ done
+ ) || if [ -z "$force" ] ; then exit 1; fi
+done
diff --git a/gnu/usr.bin/cvs/contrib/dirfns.shar b/gnu/usr.bin/cvs/contrib/dirfns.shar
new file mode 100644
index 000000000000..8324c4198e35
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/dirfns.shar
@@ -0,0 +1,481 @@
+echo 'directory.3':
+sed 's/^X//' >'directory.3' <<'!'
+X.TH DIRECTORY 3 imported
+X.DA 9 Oct 1985
+X.SH NAME
+Xopendir, readdir, telldir, seekdir, rewinddir, closedir \- high-level directory operations
+X.SH SYNOPSIS
+X.B #include <sys/types.h>
+X.br
+X.B #include <ndir.h>
+X.PP
+X.SM
+X.B DIR
+X.B *opendir(filename)
+X.br
+X.B char *filename;
+X.PP
+X.SM
+X.B struct direct
+X.B *readdir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B long
+X.B telldir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B seekdir(dirp, loc)
+X.br
+X.B DIR *dirp;
+X.br
+X.B long loc;
+X.PP
+X.SM
+X.B rewinddir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B closedir(dirp)
+X.br
+X.B DIR *dirp;
+X.SH DESCRIPTION
+XThis library provides high-level primitives for directory scanning,
+Xsimilar to those available for 4.2BSD's (very different) directory system.
+X.\"The purpose of this library is to simulate
+X.\"the new flexible length directory names of 4.2bsd UNIX
+X.\"on top of the old directory structure of v7.
+XIt incidentally provides easy portability to and from 4.2BSD (insofar
+Xas such portability is not compromised by other 4.2/VAX dependencies).
+X.\"It allows programs to be converted immediately
+X.\"to the new directory access interface,
+X.\"so that they need only be relinked
+X.\"when moved to 4.2bsd.
+X.\"It is obtained with the loader option
+X.\".BR \-lndir .
+X.PP
+X.I Opendir
+Xopens the directory named by
+X.I filename
+Xand associates a
+X.I directory stream
+Xwith it.
+X.I Opendir
+Xreturns a pointer to be used to identify the
+X.I directory stream
+Xin subsequent operations.
+XThe pointer
+X.SM
+X.B NULL
+Xis returned if
+X.I filename
+Xcannot be accessed or is not a directory.
+X.PP
+X.I Readdir
+Xreturns a pointer to the next directory entry.
+XIt returns
+X.B NULL
+Xupon reaching the end of the directory or detecting
+Xan invalid
+X.I seekdir
+Xoperation.
+X.PP
+X.I Telldir
+Xreturns the current location associated with the named
+X.I directory stream.
+X.PP
+X.I Seekdir
+Xsets the position of the next
+X.I readdir
+Xoperation on the
+X.I directory stream.
+XThe new position reverts to the one associated with the
+X.I directory stream
+Xwhen the
+X.I telldir
+Xoperation was performed.
+XValues returned by
+X.I telldir
+Xare good only for the lifetime of the DIR pointer from
+Xwhich they are derived.
+XIf the directory is closed and then reopened,
+Xthe
+X.I telldir
+Xvalue may be invalidated
+Xdue to undetected directory compaction in 4.2BSD.
+XIt is safe to use a previous
+X.I telldir
+Xvalue immediately after a call to
+X.I opendir
+Xand before any calls to
+X.I readdir.
+X.PP
+X.I Rewinddir
+Xresets the position of the named
+X.I directory stream
+Xto the beginning of the directory.
+X.PP
+X.I Closedir
+Xcauses the named
+X.I directory stream
+Xto be closed,
+Xand the structure associated with the DIR pointer to be freed.
+X.PP
+XA
+X.I direct
+Xstructure is as follows:
+X.PP
+X.RS
+X.nf
+Xstruct direct {
+X /* unsigned */ long d_ino; /* inode number of entry */
+X unsigned short d_reclen; /* length of this record */
+X unsigned short d_namlen; /* length of string in d_name */
+X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
+X};
+X.fi
+X.RE
+X.PP
+XThe
+X.I d_reclen
+Xfield is meaningless in non-4.2BSD systems and should be ignored.
+XThe use of a
+X.I long
+Xfor
+X.I d_ino
+Xis also a 4.2BSDism;
+X.I ino_t
+X(see
+X.IR types (5))
+Xshould be used elsewhere.
+XThe macro
+X.I DIRSIZ(dp)
+Xgives the minimum memory size needed to hold the
+X.I direct
+Xvalue pointed to by
+X.IR dp ,
+Xwith the minimum necessary allocation for
+X.IR d_name .
+X.PP
+XThe preferred way to search the current directory for entry ``name'' is:
+X.PP
+X.RS
+X.nf
+X len = strlen(name);
+X dirp = opendir(".");
+X if (dirp == NULL) {
+X fprintf(stderr, "%s: can't read directory .\\n", argv[0]);
+X return NOT_FOUND;
+X }
+X while ((dp = readdir(dirp)) != NULL)
+X if (dp->d_namlen == len && strcmp(dp->d_name, name) == 0) {
+X closedir(dirp);
+X return FOUND;
+X }
+X closedir(dirp);
+X return NOT_FOUND;
+X.RE
+X.\".SH LINKING
+X.\"This library is accessed by specifying ``-lndir'' as the
+X.\"last argument to the compile line, e.g.:
+X.\".PP
+X.\" cc -I/usr/include/ndir -o prog prog.c -lndir
+X.SH "SEE ALSO"
+Xopen(2),
+Xclose(2),
+Xread(2),
+Xlseek(2)
+X.SH HISTORY
+XWritten by
+XKirk McKusick at Berkeley (ucbvax!mckusick).
+XMiscellaneous bug fixes from elsewhere.
+XThe size of the data structure has been decreased to avoid excessive
+Xspace waste under V7 (where filenames are 14 characters at most).
+XFor obscure historical reasons, the include file is also available
+Xas
+X.IR <ndir/sys/dir.h> .
+XThe Berkeley version lived in a separate library (\fI\-lndir\fR),
+Xwhereas ours is
+Xpart of the C library, although the separate library is retained to
+Xmaximize compatibility.
+X.PP
+XThis manual page has been substantially rewritten to be informative in
+Xthe absence of a 4.2BSD manual.
+X.SH BUGS
+XThe
+X.I DIRSIZ
+Xmacro actually wastes a bit of space due to some padding requirements
+Xthat are an artifact of 4.2BSD.
+X.PP
+XThe returned value of
+X.I readdir
+Xpoints to a static area that will be overwritten by subsequent calls.
+X.PP
+XThere are some unfortunate name conflicts with the \fIreal\fR V7
+Xdirectory structure definitions.
+!
+echo 'dir.h':
+sed 's/^X//' >'dir.h' <<'!'
+X/* dir.h 4.4 82/07/25 */
+X
+X/*
+X * A directory consists of some number of blocks of DIRBLKSIZ
+X * bytes, where DIRBLKSIZ is chosen such that it can be transferred
+X * to disk in a single atomic operation (e.g. 512 bytes on most machines).
+X *
+X * Each DIRBLKSIZ byte block contains some number of directory entry
+X * structures, which are of variable length. Each directory entry has
+X * a struct direct at the front of it, containing its inode number,
+X * the length of the entry, and the length of the name contained in
+X * the entry. These are followed by the name padded to a 4 byte boundary
+X * with null bytes. All names are guaranteed null terminated.
+X * The maximum length of a name in a directory is MAXNAMLEN.
+X *
+X * The macro DIRSIZ(dp) gives the amount of space required to represent
+X * a directory entry. Free space in a directory is represented by
+X * entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes
+X * in a directory block are claimed by the directory entries. This
+X * usually results in the last entry in a directory having a large
+X * dp->d_reclen. When entries are deleted from a directory, the
+X * space is returned to the previous entry in the same directory
+X * block by increasing its dp->d_reclen. If the first entry of
+X * a directory block is free, then its dp->d_ino is set to 0.
+X * Entries other than the first in a directory do not normally have
+X * dp->d_ino set to 0.
+X */
+X#define DIRBLKSIZ 512
+X#ifdef VMUNIX
+X#define MAXNAMLEN 255
+X#else
+X#define MAXNAMLEN 14
+X#endif
+X
+Xstruct direct {
+X /* unsigned */ long d_ino; /* inode number of entry */
+X unsigned short d_reclen; /* length of this record */
+X unsigned short d_namlen; /* length of string in d_name */
+X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
+X};
+X
+X/*
+X * The DIRSIZ macro gives the minimum record length which will hold
+X * the directory entry. This requires the amount of space in struct direct
+X * without the d_name field, plus enough space for the name with a terminating
+X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+X */
+X#undef DIRSIZ
+X#define DIRSIZ(dp) \
+X ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+X
+X#ifndef KERNEL
+X/*
+X * Definitions for library routines operating on directories.
+X */
+Xtypedef struct _dirdesc {
+X int dd_fd;
+X long dd_loc;
+X long dd_size;
+X char dd_buf[DIRBLKSIZ];
+X} DIR;
+X#ifndef NULL
+X#define NULL 0
+X#endif
+Xextern DIR *opendir();
+Xextern struct direct *readdir();
+Xextern long telldir();
+X#ifdef void
+Xextern void seekdir();
+Xextern void closedir();
+X#endif
+X#define rewinddir(dirp) seekdir((dirp), (long)0)
+X#endif KERNEL
+!
+echo 'makefile':
+sed 's/^X//' >'makefile' <<'!'
+XDIR = closedir.o opendir.o readdir.o seekdir.o telldir.o
+XCFLAGS=-O -I. -Dvoid=int
+XDEST=..
+X
+Xall: $(DIR)
+X
+Xmv: $(DIR)
+X mv $(DIR) $(DEST)
+X
+Xcpif: dir.h
+X cp dir.h /usr/include/ndir.h
+X
+Xclean:
+X rm -f *.o
+!
+echo 'closedir.c':
+sed 's/^X//' >'closedir.c' <<'!'
+Xstatic char sccsid[] = "@(#)closedir.c 4.2 3/10/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * close a directory.
+X */
+Xvoid
+Xclosedir(dirp)
+X register DIR *dirp;
+X{
+X close(dirp->dd_fd);
+X dirp->dd_fd = -1;
+X dirp->dd_loc = 0;
+X free((char *)dirp);
+X}
+!
+echo 'opendir.c':
+sed 's/^X//' >'opendir.c' <<'!'
+X/* Copyright (c) 1982 Regents of the University of California */
+X
+Xstatic char sccsid[] = "@(#)opendir.c 4.4 11/12/82";
+X
+X#include <sys/types.h>
+X#include <sys/stat.h>
+X#include <dir.h>
+X
+X/*
+X * open a directory.
+X */
+XDIR *
+Xopendir(name)
+X char *name;
+X{
+X register DIR *dirp;
+X register int fd;
+X struct stat statbuf;
+X char *malloc();
+X
+X if ((fd = open(name, 0)) == -1)
+X return NULL;
+X if (fstat(fd, &statbuf) == -1 || !(statbuf.st_mode & S_IFDIR)) {
+X close(fd);
+X return NULL;
+X }
+X if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+X close (fd);
+X return NULL;
+X }
+X dirp->dd_fd = fd;
+X dirp->dd_loc = 0;
+X dirp->dd_size = 0; /* so that telldir will work before readdir */
+X return dirp;
+X}
+!
+echo 'readdir.c':
+sed 's/^X//' >'readdir.c' <<'!'
+X/* Copyright (c) 1982 Regents of the University of California */
+X
+Xstatic char sccsid[] = "@(#)readdir.c 4.3 8/8/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * read an old stlye directory entry and present it as a new one
+X */
+X#define ODIRSIZ 14
+X
+Xstruct olddirect {
+X ino_t od_ino;
+X char od_name[ODIRSIZ];
+X};
+X
+X/*
+X * get next entry in a directory.
+X */
+Xstruct direct *
+Xreaddir(dirp)
+X register DIR *dirp;
+X{
+X register struct olddirect *dp;
+X static struct direct dir;
+X
+X for (;;) {
+X if (dirp->dd_loc == 0) {
+X dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
+X DIRBLKSIZ);
+X if (dirp->dd_size <= 0) {
+X dirp->dd_size = 0;
+X return NULL;
+X }
+X }
+X if (dirp->dd_loc >= dirp->dd_size) {
+X dirp->dd_loc = 0;
+X continue;
+X }
+X dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
+X dirp->dd_loc += sizeof(struct olddirect);
+X if (dp->od_ino == 0)
+X continue;
+X dir.d_ino = dp->od_ino;
+X strncpy(dir.d_name, dp->od_name, ODIRSIZ);
+X dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
+X dir.d_namlen = strlen(dir.d_name);
+X dir.d_reclen = DIRBLKSIZ;
+X return (&dir);
+X }
+X}
+!
+echo 'seekdir.c':
+sed 's/^X//' >'seekdir.c' <<'!'
+Xstatic char sccsid[] = "@(#)seekdir.c 4.9 3/25/83";
+X
+X#include <sys/param.h>
+X#include <dir.h>
+X
+X/*
+X * seek to an entry in a directory.
+X * Only values returned by "telldir" should be passed to seekdir.
+X */
+Xvoid
+Xseekdir(dirp, loc)
+X register DIR *dirp;
+X long loc;
+X{
+X long curloc, base, offset;
+X struct direct *dp;
+X extern long lseek();
+X
+X curloc = telldir(dirp);
+X if (loc == curloc)
+X return;
+X base = loc & ~(DIRBLKSIZ - 1);
+X offset = loc & (DIRBLKSIZ - 1);
+X (void) lseek(dirp->dd_fd, base, 0);
+X dirp->dd_size = 0;
+X dirp->dd_loc = 0;
+X while (dirp->dd_loc < offset) {
+X dp = readdir(dirp);
+X if (dp == NULL)
+X return;
+X }
+X}
+!
+echo 'telldir.c':
+sed 's/^X//' >'telldir.c' <<'!'
+Xstatic char sccsid[] = "@(#)telldir.c 4.1 2/21/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * return a pointer into a directory
+X */
+Xlong
+Xtelldir(dirp)
+X DIR *dirp;
+X{
+X long lseek();
+X
+X return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
+X}
+!
+echo done
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/compile.sh b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile.sh
new file mode 100644
index 000000000000..b940370da197
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+emacs -batch -l compile-all.el -f compile-pcl-cvs
diff --git a/gnu/usr.bin/cvs/contrib/rcs-to-cvs.sh b/gnu/usr.bin/cvs/contrib/rcs-to-cvs.sh
new file mode 100644
index 000000000000..3af83d708ca4
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcs-to-cvs.sh
@@ -0,0 +1,185 @@
+#! /bin/sh
+#
+# $Id: rcs-to-cvs.sh,v 1.2 1995/07/15 03:40:34 jimb Exp $
+# Based on the CVS 1.0 checkin csh script.
+# Contributed by Per Cederqvist <ceder@signum.se>.
+# Rewritten in sh by David MacKenzie <djm@cygnus.com>.
+#
+# Copyright (c) 1989, Brian Berliner
+#
+# You may distribute under the terms of the GNU General Public License.
+#
+#############################################################################
+#
+# Check in sources that previously were under RCS or no source control system.
+#
+# The repository is the directory where the sources should be deposited.
+#
+# Traverses the current directory, ensuring that an
+# identical directory structure exists in the repository directory. It
+# then checks the files in in the following manner:
+#
+# 1) If the file doesn't yet exist, check it in as revision 1.1
+#
+# The script also is somewhat verbose in letting the user know what is
+# going on. It prints a diagnostic when it creates a new file, or updates
+# a file that has been modified on the trunk.
+#
+# Bugs: doesn't put the files in branch 1.1.1
+# doesn't put in release and vendor tags
+#
+#############################################################################
+
+usage="Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
+vbose=0
+message=""
+message_file=/usr/tmp/checkin.$$
+got_one=0
+
+if [ $# -lt 1 ]; then
+ echo "$usage" >&2
+ exit 1
+fi
+
+while [ $# -ne 0 ]; do
+ case "$1" in
+ -v)
+ vbose=1
+ ;;
+ -m)
+ shift
+ echo $1 > $message_file
+ got_one=1
+ ;;
+ -f)
+ shift
+ message_file=$1
+ got_one=2
+ ;;
+ *)
+ break
+ esac
+ shift
+done
+
+if [ $# -lt 1 ]; then
+ echo "$usage" >&2
+ exit 1
+fi
+
+repository=$1
+shift
+
+if [ -z "$CVSROOT" ]; then
+ echo "Please the environmental variable CVSROOT to the root" >&2
+ echo " of the tree you wish to update" >&2
+ exit 1
+fi
+
+if [ $got_one -eq 0 ]; then
+ echo "Please Edit this file to contain the RCS log information" >$message_file
+ echo "to be associated with this directory (please remove these lines)">>$message_file
+ ${EDITOR-/usr/ucb/vi} $message_file
+ got_one=1
+fi
+
+# Ya gotta share.
+umask 0
+
+update_dir=${CVSROOT}/${repository}
+[ ! -d ${update_dir} ] && mkdir $update_dir
+
+if [ -d SCCS ]; then
+ echo SCCS files detected! >&2
+ exit 1
+fi
+if [ -d RCS ]; then
+ co RCS/*
+fi
+
+for name in * .[a-zA-Z0-9]*
+do
+ case "$name" in
+ RCS | *~ | \* | .\[a-zA-Z0-9\]\* ) continue ;;
+ esac
+ echo $name
+ if [ $vbose -ne 0 ]; then
+ echo "Updating ${repository}/${name}"
+ fi
+ if [ -d "$name" ]; then
+ if [ ! -d "${update_dir}/${name}" ]; then
+ echo "WARNING: Creating new directory ${repository}/${name}"
+ mkdir "${update_dir}/${name}"
+ if [ $? -ne 0 ]; then
+ echo "ERROR: mkdir failed - aborting" >&2
+ exit 1
+ fi
+ fi
+ cd "$name"
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Couldn\'t cd to $name - aborting" >&2
+ exit 1
+ fi
+ if [ $vbose -ne 0 ]; then
+ $0 -v -f $message_file "${repository}/${name}"
+ else
+ $0 -f $message_file "${repository}/${name}"
+ fi
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+ cd ..
+ else # if not directory
+ if [ ! -f "$name" ]; then
+ echo "WARNING: $name is neither a regular file"
+ echo " nor a directory - ignored"
+ continue
+ fi
+ file="${update_dir}/${name},v"
+ comment=""
+ if grep -s '\$Log.*\$' "${name}"; then # If $Log keyword
+ myext=`echo $name | sed 's,.*\.,,'`
+ [ "$myext" = "$name" ] && myext=
+ case "$myext" in
+ c | csh | e | f | h | l | mac | me | mm | ms | p | r | red | s | sh | sl | cl | ml | el | tex | y | ye | yr | "" )
+ ;;
+
+ * )
+ echo "For file ${file}:"
+ grep '\$Log.*\$' "${name}"
+ echo -n "Please insert a comment leader for file ${name} > "
+ read comment
+ ;;
+ esac
+ fi
+ if [ ! -f "$file" ]; then # If not exists in repository
+ if [ ! -f "${update_dir}/Attic/${name},v" ]; then
+ echo "WARNING: Creating new file ${repository}/${name}"
+ if [ -f RCS/"${name}",v ]; then
+ echo "MSG: Copying old rcs file."
+ cp RCS/"${name}",v "$file"
+ else
+ if [ -n "${comment}" ]; then
+ rcs -q -i -c"${comment}" -t${message_file} -m'.' "$file"
+ fi
+ ci -q -u1.1 -t${message_file} -m'.' "$file"
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Initial check-in of $file failed - aborting" >&2
+ exit 1
+ fi
+ fi
+ else
+ file="${update_dir}/Attic/${name},v"
+ echo "WARNING: IGNORED: ${repository}/Attic/${name}"
+ continue
+ fi
+ else # File existed
+ echo "ERROR: File exists in repository: Ignored: $file"
+ continue
+ fi
+ fi
+done
+
+[ $got_one -eq 1 ] && rm -f $message_file
+
+exit 0
diff --git a/gnu/usr.bin/cvs/contrib/rcs2log.sh b/gnu/usr.bin/cvs/contrib/rcs2log.sh
new file mode 100644
index 000000000000..ccea907ea37b
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcs2log.sh
@@ -0,0 +1,592 @@
+#! /bin/sh
+
+# RCS to ChangeLog generator
+
+# Generate a change log prefix from RCS files and the ChangeLog (if any).
+# Output the new prefix to standard output.
+# You can edit this prefix by hand, and then prepend it to ChangeLog.
+
+# Ignore log entries that start with `#'.
+# Clump together log entries that start with `{topic} ',
+# where `topic' contains neither white space nor `}'.
+
+# Author: Paul Eggert <eggert@twinsun.com>
+
+# $Id: rcs2log.sh,v 1.2 1995/07/28 19:48:45 eggert Exp $
+
+# Copyright 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+tab=' '
+nl='
+'
+
+# Parse options.
+
+# defaults
+: ${AWK=awk}
+: ${TMPDIR=/tmp}
+hostname= # name of local host (if empty, will deduce it later)
+indent=8 # indent of log line
+length=79 # suggested max width of log line
+logins= # login names for people we know fullnames and mailaddrs of
+loginFullnameMailaddrs= # login<tab>fullname<tab>mailaddr triplets
+recursive= # t if we want recursive rlog
+rlog_options= # options to pass to rlog
+tabwidth=8 # width of horizontal tab
+
+while :
+do
+ case $1 in
+ -i) indent=${2?}; shift;;
+ -h) hostname=${2?}; shift;;
+ -l) length=${2?}; shift;;
+ -[nu]) # -n is obsolescent; it is replaced by -u.
+ case $1 in
+ -n) case ${2?}${3?}${4?} in
+ *"$tab"* | *"$nl"*)
+ echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed"
+ exit 1
+ esac
+ loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2$tab$3$tab$4
+ shift; shift; shift;;
+ -u)
+ # If $2 is not tab-separated, use colon for separator.
+ case ${2?} in
+ *"$nl"*)
+ echo >&2 "$0: -u '$2': newlines not allowed"
+ exit 1;;
+ *"$tab"*)
+ t=$tab;;
+ *)
+ t=:
+ esac
+ case $2 in
+ *"$t"*"$t"*"$t"*)
+ echo >&2 "$0: -u '$2': too many fields"
+ exit 1;;
+ *"$t"*"$t"*)
+ ;;
+ *)
+ echo >&2 "$0: -u '$2': not enough fields"
+ exit 1
+ esac
+ loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2
+ shift
+ esac
+ logins=$logins$nl$login
+ ;;
+ -r) rlog_options=$rlog_options$nl${2?}; shift;;
+ -R) recursive=t;;
+ -t) tabwidth=${2?}; shift;;
+ -*) echo >&2 "$0: usage: $0 [options] [file ...]
+Options:
+ [-h hostname] [-i indent] [-l length] [-R] [-r rlog_option]
+ [-t tabwidth] [-u 'login<TAB>fullname<TAB>mailaddr']..."
+ exit 1;;
+ *) break
+ esac
+ shift
+done
+
+month_data='
+ m[0]="Jan"; m[1]="Feb"; m[2]="Mar"
+ m[3]="Apr"; m[4]="May"; m[5]="Jun"
+ m[6]="Jul"; m[7]="Aug"; m[8]="Sep"
+ m[9]="Oct"; m[10]="Nov"; m[11]="Dec"
+
+ # days in non-leap year thus far, indexed by month (0-12)
+ mo[0]=0; mo[1]=31; mo[2]=59; mo[3]=90
+ mo[4]=120; mo[5]=151; mo[6]=181; mo[7]=212
+ mo[8]=243; mo[9]=273; mo[10]=304; mo[11]=334
+ mo[12]=365
+'
+
+
+# Put rlog output into $rlogout.
+
+# If no rlog options are given,
+# log the revisions checked in since the first ChangeLog entry.
+case $rlog_options in
+'')
+ date=1970
+ if test -s ChangeLog
+ then
+ # Add 1 to seconds to avoid duplicating most recent log.
+ e='
+ /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{
+ '"$month_data"'
+ year = $5
+ for (i=0; i<=11; i++) if (m[i] == $2) break
+ dd = $3
+ hh = substr($0,12,2)
+ mm = substr($0,15,2)
+ ss = substr($0,18,2)
+ ss++
+ if (ss == 60) {
+ ss = 0
+ mm++
+ if (mm == 60) {
+ mm = 0
+ hh++
+ if (hh == 24) {
+ hh = 0
+ dd++
+ monthdays = mo[i+1] - mo[i]
+ if (i == 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0)) monthdays++
+ if (dd == monthdays + 1) {
+ dd = 1
+ i++
+ if (i == 12) {
+ i = 0
+ year++
+ }
+ }
+ }
+ }
+ }
+ # Output comma instead of space to avoid CVS 1.5 bug.
+ printf "%d/%02d/%02d,%02d:%02d:%02d\n", year,i+1,dd,hh,mm,ss
+ exit
+ }
+ '
+ d=`$AWK "$e" <ChangeLog` || exit
+ case $d in
+ ?*) date=$d
+ esac
+ fi
+ datearg="-d>$date"
+esac
+
+# If CVS is in use, examine its repository, not the normal RCS files.
+if test ! -f CVS/Repository
+then
+ rlog=rlog
+ repository=
+else
+ rlog='cvs log'
+ repository=`sed 1q <CVS/Repository` || exit
+ test ! -f CVS/Root || CVSROOT=`cat <CVS/Root` || exit
+ case $CVSROOT in
+ *:/*)
+ # remote repository
+ ;;
+ *)
+ # local repository
+ case $repository in
+ /*) ;;
+ *) repository=${CVSROOT?}/$repository
+ esac
+ if test ! -d "$repository"
+ then
+ echo >&2 "$0: $repository: bad repository (see CVS/Repository)"
+ exit 1
+ fi
+ esac
+fi
+
+# With no arguments, examine all files under the RCS directory.
+case $# in
+0)
+ case $repository in
+ '')
+ oldIFS=$IFS
+ IFS=$nl
+ case $recursive in
+ t)
+ RCSdirs=`find . -name RCS -type d -print`
+ filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||'
+ files=`
+ {
+ case $RCSdirs in
+ ?*) find $RCSdirs -type f -print
+ esac
+ find . -name '*,v' -print
+ } |
+ sort -u |
+ sed "$filesFromRCSfiles"
+ `;;
+ *)
+ files=
+ for file in RCS/.* RCS/* .*,v *,v
+ do
+ case $file in
+ RCS/. | RCS/..) continue;;
+ RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue
+ esac
+ files=$files$nl$file
+ done
+ case $files in
+ '') exit 0
+ esac
+ esac
+ set x $files
+ shift
+ IFS=$oldIFS
+ esac
+esac
+
+llogout=$TMPDIR/rcs2log$$l
+rlogout=$TMPDIR/rcs2log$$r
+trap exit 1 2 13 15
+trap "rm -f $llogout $rlogout; exit 1" 0
+
+case $rlog_options in
+?*) $rlog $rlog_options ${1+"$@"} >$rlogout;;
+'') $rlog "$datearg" ${1+"$@"} >$rlogout
+esac || exit
+
+
+# Get the full name of each author the logs mention, and set initialize_fullname
+# to awk code that initializes the `fullname' awk associative array.
+# Warning: foreign authors (i.e. not known in the passwd file) are mishandled;
+# you have to fix the resulting output by hand.
+
+initialize_fullname=
+initialize_mailaddr=
+
+case $loginFullnameMailaddrs in
+?*)
+ case $loginFullnameMailaddrs in
+ *\"* | *\\*)
+ sed 's/["\\]/\\&/g' >$llogout <<EOF || exit
+$loginFullnameMailaddrs
+EOF
+ loginFullnameMailaddrs=`cat $llogout`
+ esac
+
+ oldIFS=$IFS
+ IFS=$nl
+ for loginFullnameMailaddr in $loginFullnameMailaddrs
+ do
+ case $loginFullnameMailaddr in
+ *"$tab"*) IFS=$tab;;
+ *) IFS=:
+ esac
+ set x $loginFullnameMailaddr
+ login=$2
+ fullname=$3
+ mailaddr=$4
+ initialize_fullname="$initialize_fullname
+ fullname[\"$login\"] = \"$fullname\""
+ initialize_mailaddr="$initialize_mailaddr
+ mailaddr[\"$login\"] = \"$mailaddr\""
+ done
+ IFS=$oldIFS
+esac
+
+case $llogout in
+?*) sort -u -o $llogout <<EOF || exit
+$logins
+EOF
+esac
+output_authors='/^date: / {
+ if ($2 ~ /^[0-9]*[-\/][0-9][0-9][-\/][0-9][0-9]$/ && $3 ~ /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9:]*;$/ && $4 == "author:" && $5 ~ /^[^;]*;$/) {
+ print substr($5, 1, length($5)-1)
+ }
+}'
+authors=`
+ $AWK "$output_authors" <$rlogout |
+ case $llogout in
+ '') sort -u;;
+ ?*) sort -u | comm -23 - $llogout
+ esac
+`
+case $authors in
+?*)
+ cat >$llogout <<EOF || exit
+$authors
+EOF
+ initialize_author_script='s/["\\]/\\&/g; s/.*/author[\"&\"] = 1/'
+ initialize_author=`sed -e "$initialize_author_script" <$llogout`
+ awkscript='
+ BEGIN {
+ alphabet = "abcdefghijklmnopqrstuvwxyz"
+ ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ '"$initialize_author"'
+ }
+ {
+ if (author[$1]) {
+ fullname = $5
+ if (fullname ~ /[0-9]+-[^(]*\([0-9]+\)$/) {
+ # Remove the junk from fullnames like "0000-Admin(0000)".
+ fullname = substr(fullname, index(fullname, "-") + 1)
+ fullname = substr(fullname, 1, index(fullname, "(") - 1)
+ }
+ if (fullname ~ /,[^ ]/) {
+ # Some sites put comma-separated junk after the fullname.
+ # Remove it, but leave "Bill Gates, Jr" alone.
+ fullname = substr(fullname, 1, index(fullname, ",") - 1)
+ }
+ abbr = index(fullname, "&")
+ if (abbr) {
+ a = substr($1, 1, 1)
+ A = a
+ i = index(alphabet, a)
+ if (i) A = substr(ALPHABET, i, 1)
+ fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1)
+ }
+
+ # Quote quotes and backslashes properly in full names.
+ # Do not use gsub; traditional awk lacks it.
+ quoted = ""
+ rest = fullname
+ for (;;) {
+ p = index(rest, "\\")
+ q = index(rest, "\"")
+ if (p) {
+ if (q && q<p) p = q
+ } else {
+ if (!q) break
+ p = q
+ }
+ quoted = quoted substr(rest, 1, p-1) "\\" substr(rest, p, 1)
+ rest = substr(rest, p+1)
+ }
+
+ printf "fullname[\"%s\"] = \"%s%s\"\n", $1, quoted, rest
+ author[$1] = 0
+ }
+ }
+ '
+
+ initialize_fullname=`
+ (cat /etc/passwd; ypmatch $authors passwd) 2>/dev/null |
+ $AWK -F: "$awkscript"
+ `$initialize_fullname
+esac
+
+
+# Function to print a single log line.
+# We don't use awk functions, to stay compatible with old awk versions.
+# `Log' is the log message (with \n replaced by \r).
+# `files' contains the affected files.
+printlogline='{
+
+ # Following the GNU coding standards, rewrite
+ # * file: (function): comment
+ # to
+ # * file (function): comment
+ if (Log ~ /^\([^)]*\): /) {
+ i = index(Log, ")")
+ files = files " " substr(Log, 1, i)
+ Log = substr(Log, i+3)
+ }
+
+ # If "label: comment" is too long, break the line after the ":".
+ sep = " "
+ if ('"$length"' <= '"$indent"' + 1 + length(files) + index(Log, CR)) sep = "\n" indent_string
+
+ # Print the label.
+ printf "%s*%s:", indent_string, files
+
+ # Print each line of the log, transliterating \r to \n.
+ while ((i = index(Log, CR)) != 0) {
+ logline = substr(Log, 1, i-1)
+ if (logline ~ /[^'"$tab"' ]/) {
+ printf "%s%s\n", sep, logline
+ } else {
+ print ""
+ }
+ sep = indent_string
+ Log = substr(Log, i+1)
+ }
+}'
+
+case $hostname in
+'')
+ hostname=`(
+ hostname || uname -n || uuname -l || cat /etc/whoami
+ ) 2>/dev/null` || {
+ echo >&2 "$0: cannot deduce hostname"
+ exit 1
+ }
+esac
+
+
+# Process the rlog output, generating ChangeLog style entries.
+
+# First, reformat the rlog output so that each line contains one log entry.
+# Transliterate \n to \r so that multiline entries fit on a single line.
+# Discard irrelevant rlog output.
+$AWK <$rlogout '
+ BEGIN { repository = "'"$repository"'" }
+ /^RCS file:/ {
+ if (repository != "") {
+ filename = $3
+ if (substr(filename, 1, length(repository) + 1) == repository "/") {
+ filename = substr(filename, length(repository) + 2)
+ }
+ if (filename ~ /,v$/) {
+ filename = substr(filename, 1, length(filename) - 2)
+ }
+ }
+ }
+ /^Working file:/ { if (repository == "") filename = $3 }
+ /^date: /, /^(-----------*|===========*)$/ {
+ if ($0 ~ /^branches: /) { next }
+ if ($0 ~ /^date: [0-9][- +\/0-9:]*;/) {
+ date = $2
+ if (date ~ /-/) {
+ # An ISO format date. Replace all "-"s with "/"s.
+ newdate = ""
+ while ((i = index(date, "-")) != 0) {
+ newdate = newdate substr(date, 1, i-1) "/"
+ date = substr(date, i+1)
+ }
+ date = newdate date
+ }
+ # Ignore any time zone; ChangeLog has no room for it.
+ time = substr($3, 1, 8)
+ author = substr($5, 1, length($5)-1)
+ printf "%s %s %s %s %c", filename, date, time, author, 13
+ next
+ }
+ if ($0 ~ /^(-----------*|===========*)$/) { print ""; next }
+ printf "%s%c", $0, 13
+ }
+' |
+
+# Now each line is of the form
+# FILENAME YYYY/MM/DD HH:MM:SS AUTHOR \rLOG
+# where \r stands for a carriage return,
+# and each line of the log is terminated by \r instead of \n.
+# Sort the log entries, first by date+time (in reverse order),
+# then by author, then by log entry, and finally by file name (just in case).
+sort +1 -3r +3 +0 |
+
+# Finally, reformat the sorted log entries.
+$AWK '
+ BEGIN {
+ # Some awk variants do not understand "\r" or "\013", so we have to
+ # put a carriage return directly in the file.
+ CR=" " # <-- There is a single CR between the " chars here.
+
+ # Initialize the fullname and mailaddr associative arrays.
+ '"$initialize_fullname"'
+ '"$initialize_mailaddr"'
+
+ # Initialize indent string.
+ indent_string = ""
+ i = '"$indent"'
+ if (0 < '"$tabwidth"')
+ for (; '"$tabwidth"' <= i; i -= '"$tabwidth"')
+ indent_string = indent_string "\t"
+ while (1 <= i--)
+ indent_string = indent_string " "
+
+ # Set up date conversion tables.
+ # RCS uses a nice, clean, sortable format,
+ # but ChangeLog wants the traditional, ugly ctime format.
+
+ # January 1, 0 AD (Gregorian) was Saturday = 6
+ EPOCH_WEEKDAY = 6
+ # Of course, there was no 0 AD, but the algorithm works anyway.
+
+ w[0]="Sun"; w[1]="Mon"; w[2]="Tue"; w[3]="Wed"
+ w[4]="Thu"; w[5]="Fri"; w[6]="Sat"
+
+ '"$month_data"'
+ }
+
+ {
+ newlog = substr($0, 1 + index($0, CR))
+
+ # Ignore log entries prefixed by "#".
+ if (newlog ~ /^#/) { next }
+
+ if (Log != newlog || date != $2 || author != $4) {
+
+ # The previous log and this log differ.
+
+ # Print the old log.
+ if (date != "") '"$printlogline"'
+
+ # Logs that begin with "{clumpname} " should be grouped together,
+ # and the clumpname should be removed.
+ # Extract the new clumpname from the log header,
+ # and use it to decide whether to output a blank line.
+ newclumpname = ""
+ sep = "\n"
+ if (date == "") sep = ""
+ if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) {
+ i = index(newlog, "}")
+ newclumpname = substr(newlog, 1, i)
+ while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++
+ newlog = substr(newlog, i+1)
+ if (clumpname == newclumpname) sep = ""
+ }
+ printf sep
+ clumpname = newclumpname
+
+ # Get ready for the next log.
+ Log = newlog
+ if (files != "")
+ for (i in filesknown)
+ filesknown[i] = 0
+ files = ""
+ }
+ if (date != $2 || author != $4) {
+ # The previous date+author and this date+author differ.
+ # Print the new one.
+ date = $2
+ author = $4
+
+ # Convert nice RCS date like "1992/01/03 00:03:44"
+ # into ugly ctime date like "Fri Jan 3 00:03:44 1992".
+ # Calculate day of week from Gregorian calendar.
+ i = index($2, "/")
+ year = substr($2, 1, i-1) + 0
+ monthday = substr($2, i+1)
+ i = index(monthday, "/")
+ month = substr(monthday, 1, i-1) + 0
+ day = substr(monthday, i+1) + 0
+ leap = 0
+ if (2 < month && year%4 == 0 && (year%100 != 0 || year%400 == 0)) leap = 1
+ days_since_Sunday_before_epoch = EPOCH_WEEKDAY + year * 365 + int((year + 3) / 4) - int((year + 99) / 100) + int((year + 399) / 400) + mo[month-1] + leap + day - 1
+
+ # Print "date fullname (email address)".
+ # Get fullname and email address from associative arrays;
+ # default to author and author@hostname if not in arrays.
+ if (fullname[author])
+ auth = fullname[author]
+ else
+ auth = author
+ printf "%s %s %2d %s %d %s ", w[days_since_Sunday_before_epoch%7], m[month-1], day, $3, year, auth
+ if (mailaddr[author])
+ printf "<%s>\n\n", mailaddr[author]
+ else
+ printf "<%s@%s>\n\n", author, "'"$hostname"'"
+ }
+ if (! filesknown[$1]) {
+ filesknown[$1] = 1
+ if (files == "") files = " " $1
+ else files = files ", " $1
+ }
+ }
+ END {
+ # Print the last log.
+ if (date != "") {
+ '"$printlogline"'
+ printf "\n"
+ }
+ }
+' &&
+
+
+# Exit successfully.
+
+exec rm -f $llogout $rlogout
diff --git a/gnu/usr.bin/cvs/contrib/rcs2sccs.sh b/gnu/usr.bin/cvs/contrib/rcs2sccs.sh
new file mode 100644
index 000000000000..af701384e4b6
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcs2sccs.sh
@@ -0,0 +1,143 @@
+#! /bin/sh
+#
+#
+# OrigId: rcs2sccs,v 1.12 90/10/04 20:52:23 kenc Exp Locker: kenc
+# $Id: rcs2sccs.sh,v 1.1 1995/07/10 02:26:45 kfogel Exp $
+
+############################################################
+# Error checking
+#
+if [ ! -d SCCS ] ; then
+ mkdir SCCS
+fi
+
+logfile=/tmp/rcs2sccs_$$_log
+rm -f $logfile
+tmpfile=/tmp/rcs2sccs_$$_tmp
+rm -f $tmpfile
+emptyfile=/tmp/rcs2sccs_$$_empty
+echo -n "" > $emptyfile
+initialfile=/tmp/rcs2sccs_$$_init
+echo "Initial revision" > $initialfile
+sedfile=/tmp/rcs2sccs_$$_sed
+rm -f $sedfile
+revfile=/tmp/rcs2sccs_$$_rev
+rm -f $revfile
+commentfile=/tmp/rcs2sccs_$$_comment
+rm -f $commentfile
+
+# create the sed script
+cat > $sedfile << EOF
+s,;Id;,%Z%%M% %I% %E%,g
+s,;SunId;,%Z%%M% %I% %E%,g
+s,;RCSfile;,%M%,g
+s,;Revision;,%I%,g
+s,;Date;,%E%,g
+s,;Id:.*;,%Z%%M% %I% %E%,g
+s,;SunId:.*;,%Z%%M% %I% %E%,g
+s,;RCSfile:.*;,%M%,g
+s,;Revision:.*;,%I%,g
+s,;Date:.*;,%E%,g
+EOF
+sed -e 's/;/\\$/g' $sedfile > $tmpfile
+cp $tmpfile $sedfile
+############################################################
+# Loop over every RCS file in RCS dir
+#
+for vfile in *,v; do
+ # get rid of the ",v" at the end of the name
+ file=`echo $vfile | sed -e 's/,v$//'`
+
+ # work on each rev of that file in ascending order
+ firsttime=1
+ rlog $file | grep "^revision [0-9][0-9]*\." | awk '{print $2}' | sed -e 's/\./ /g' | sort -n -u +0 +1 +2 +3 +4 +5 +6 +7 +8 | sed -e 's/ /./g' > $revfile
+ for rev in `cat $revfile`; do
+ if [ $? != 0 ]; then
+ echo ERROR - revision
+ exit
+ fi
+ # get file into current dir and get stats
+ date=`rlog -r$rev $file | grep "^date: " | awk '{print $2; exit}' | sed -e 's/^19//'`
+ time=`rlog -r$rev $file | grep "^date: " | awk '{print $3; exit}' | sed -e 's/;//'`
+ author=`rlog -r$rev $file | grep "^date: " | awk '{print $5; exit}' | sed -e 's/;//'`
+ date="$date $time"
+ echo ""
+ rlog -r$rev $file | sed -e '/^branches: /d' -e '1,/^date: /d' -e '/^===========/d' -e 's/$/\\/' | awk '{if ((total += length($0) + 1) < 510) print $0}' > $commentfile
+ echo "==> file $file, rev=$rev, date=$date, author=$author"
+ rm -f $file
+ co -r$rev $file >> $logfile 2>&1
+ if [ $? != 0 ]; then
+ echo ERROR - co
+ exit
+ fi
+ echo checked out of RCS
+
+ # add SCCS keywords in place of RCS keywords
+ sed -f $sedfile $file > $tmpfile
+ if [ $? != 0 ]; then
+ echo ERROR - sed
+ exit
+ fi
+ echo performed keyword substitutions
+ rm -f $file
+ cp $tmpfile $file
+
+ # check file into SCCS
+ if [ "$firsttime" = "1" ]; then
+ firsttime=0
+ echo about to do sccs admin
+ echo sccs admin -n -i$file $file < $commentfile
+ sccs admin -n -i$file $file < $commentfile >> $logfile 2>&1
+ if [ $? != 0 ]; then
+ echo ERROR - sccs admin
+ exit
+ fi
+ echo initial rev checked into SCCS
+ else
+ case $rev in
+ *.*.*.*)
+ brev=`echo $rev | sed -e 's/\.[0-9]*$//'`
+ sccs admin -fb $file 2>>$logfile
+ echo sccs get -e -p -r$brev $file
+ sccs get -e -p -r$brev $file >/dev/null 2>>$logfile
+ ;;
+ *)
+ echo sccs get -e -p $file
+ sccs get -e -p $file >/dev/null 2>> $logfile
+ ;;
+ esac
+ if [ $? != 0 ]; then
+ echo ERROR - sccs get
+ exit
+ fi
+ sccs delta $file < $commentfile >> $logfile 2>&1
+ if [ $? != 0 ]; then
+ echo ERROR - sccs delta -r$rev $file
+ exit
+ fi
+ echo checked into SCCS
+ fi
+ sed -e "s;^d D $rev ../../.. ..:..:.. [^ ][^ ]*;d D $rev $date $author;" SCCS/s.$file > $tmpfile
+ rm -f SCCS/s.$file
+ cp $tmpfile SCCS/s.$file
+ chmod 444 SCCS/s.$file
+ sccs admin -z $file
+ if [ $? != 0 ]; then
+ echo ERROR - sccs admin -z
+ exit
+ fi
+ done
+ rm -f $file
+done
+
+
+############################################################
+# Clean up
+#
+echo cleaning up...
+rm -f $tmpfile $emptyfile $initialfile $sedfile $commentfile
+echo ===================================================
+echo " Conversion Completed Successfully"
+echo ===================================================
+
+rm -f *,v
diff --git a/gnu/usr.bin/cvs/contrib/sccs2rcs.csh b/gnu/usr.bin/cvs/contrib/sccs2rcs.csh
new file mode 100644
index 000000000000..0f31893d3b24
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/sccs2rcs.csh
@@ -0,0 +1,277 @@
+#! xCSH_PATHx -f
+#
+# Sccs2rcs is a script to convert an existing SCCS
+# history into an RCS history without losing any of
+# the information contained therein.
+# It has been tested under the following OS's:
+# SunOS 3.5, 4.0.3, 4.1
+# Ultrix-32 2.0, 3.1
+#
+# Things to note:
+# + It will NOT delete or alter your ./SCCS history under any circumstances.
+#
+# + Run in a directory where ./SCCS exists and where you can
+# create ./RCS
+#
+# + /usr/local/bin is put in front of the default path.
+# (SCCS under Ultrix is set-uid sccs, bad bad bad, so
+# /usr/local/bin/sccs here fixes that)
+#
+# + Date, time, author, comments, branches, are all preserved.
+#
+# + If a command fails somewhere in the middle, it bombs with
+# a message -- remove what it's done so far and try again.
+# "rm -rf RCS; sccs unedit `sccs tell`; sccs clean"
+# There is no recovery and exit is far from graceful.
+# If a particular module is hanging you up, consider
+# doing it separately; move it from the current area so that
+# the next run will have a better chance or working.
+# Also (for the brave only) you might consider hacking
+# the s-file for simpler problems: I've successfully changed
+# the date of a delta to be in sync, then run "sccs admin -z"
+# on the thing.
+#
+# + After everything finishes, ./SCCS will be moved to ./old-SCCS.
+#
+# This file may be copied, processed, hacked, mutilated, and
+# even destroyed as long as you don't tell anyone you wrote it.
+#
+# Ken Cox
+# Viewlogic Systems, Inc.
+# kenstir@viewlogic.com
+# ...!harvard!cg-atla!viewlog!kenstir
+#
+# Various hacks made by Brian Berliner before inclusion in CVS contrib area.
+#
+# $Id: sccs2rcs.csh,v 1.1 1995/07/10 02:26:48 kfogel Exp $
+
+
+#we'll assume the user set up the path correctly
+# for the Pmax, /usr/ucb/sccs is suid sccs, what a pain
+# /usr/local/bin/sccs should override /usr/ucb/sccs there
+set path = (/usr/local/bin $path)
+
+
+############################################################
+# Error checking
+#
+if (! -w .) then
+ echo "Error: ./ not writeable by you."
+ exit 1
+endif
+if (! -d SCCS) then
+ echo "Error: ./SCCS directory not found."
+ exit 1
+endif
+set edits = (`sccs tell`)
+if ($#edits) then
+ echo "Error: $#edits file(s) out for edit...clean up before converting."
+ exit 1
+endif
+if (-d RCS) then
+ echo "Warning: RCS directory exists"
+ if (`ls -a RCS | wc -l` > 2) then
+ echo "Error: RCS directory not empty
+ exit 1
+ endif
+else
+ mkdir RCS
+endif
+
+sccs clean
+
+set logfile = /tmp/sccs2rcs_$$_log
+rm -f $logfile
+set tmpfile = /tmp/sccs2rcs_$$_tmp
+rm -f $tmpfile
+set emptyfile = /tmp/sccs2rcs_$$_empty
+echo -n "" > $emptyfile
+set initialfile = /tmp/sccs2rcs_$$_init
+echo "Initial revision" > $initialfile
+set sedfile = /tmp/sccs2rcs_$$_sed
+rm -f $sedfile
+set revfile = /tmp/sccs2rcs_$$_rev
+rm -f $revfile
+
+# the quotes surround the dollar signs to fool RCS when I check in this script
+set sccs_keywords = (\
+ '%W%[ ]*%G%'\
+ '%W%[ ]*%E%'\
+ '%W%'\
+ '%Z%%M%[ ]*%I%[ ]*%G%'\
+ '%Z%%M%[ ]*%I%[ ]*%E%'\
+ '%M%[ ]*%I%[ ]*%G%'\
+ '%M%[ ]*%I%[ ]*%E%'\
+ '%M%'\
+ '%I%'\
+ '%G%'\
+ '%E%'\
+ '%U%')
+set rcs_keywords = (\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'SunId'$'\
+ '$'SunId'$'\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'RCSfile'$'\
+ '$'Revision'$'\
+ '$'Date'$'\
+ '$'Date'$'\
+ '')
+
+
+############################################################
+# Get some answers from user
+#
+echo ""
+echo "Do you want to be prompted for a description of each"
+echo "file as it is checked in to RCS initially?"
+echo -n "(y=prompt for description, n=null description) [y] ?"
+set ans = $<
+if ((_$ans == _) || (_$ans == _y) || (_$ans == _Y)) then
+ set nodesc = 0
+else
+ set nodesc = 1
+endif
+echo ""
+echo "The default keyword substitutions are as follows and are"
+echo "applied in the order specified:"
+set i = 1
+while ($i <= $#sccs_keywords)
+# echo ' '\"$sccs_keywords[$i]\"' ==> '\"$rcs_keywords[$i]\"
+ echo " $sccs_keywords[$i] ==> $rcs_keywords[$i]"
+ @ i = $i + 1
+end
+echo ""
+echo -n "Do you want to change them [n] ?"
+set ans = $<
+if ((_$ans != _) && (_$ans != _n) && (_$ans != _N)) then
+ echo "You can't always get what you want."
+ echo "Edit this script file and change the variables:"
+ echo ' $sccs_keywords'
+ echo ' $rcs_keywords'
+else
+ echo "good idea."
+endif
+
+# create the sed script
+set i = 1
+while ($i <= $#sccs_keywords)
+ echo "s,$sccs_keywords[$i],$rcs_keywords[$i],g" >> $sedfile
+ @ i = $i + 1
+end
+
+onintr ERROR
+
+############################################################
+# Loop over every s-file in SCCS dir
+#
+foreach sfile (SCCS/s.*)
+ # get rid of the "s." at the beginning of the name
+ set file = `echo $sfile:t | sed -e "s/^..//"`
+
+ # work on each rev of that file in ascending order
+ set firsttime = 1
+ sccs prs $file | grep "^D " | awk '{print $2}' | sed -e 's/\./ /g' | sort -n -u +0 +1 +2 +3 +4 +5 +6 +7 +8 | sed -e 's/ /./g' > $revfile
+ foreach rev (`cat $revfile`)
+ if ($status != 0) goto ERROR
+
+ # get file into current dir and get stats
+ set date = `sccs prs -r$rev $file | grep "^D " | awk '{printf("19%s %s", $3, $4); exit}'`
+ set author = `sccs prs -r$rev $file | grep "^D " | awk '{print $5; exit}'`
+ echo ""
+ echo "==> file $file, rev=$rev, date=$date, author=$author"
+ sccs edit -r$rev $file >>& $logfile
+ if ($status != 0) goto ERROR
+ echo checked out of SCCS
+
+ # add RCS keywords in place of SCCS keywords
+ sed -f $sedfile $file > $tmpfile
+ if ($status != 0) goto ERROR
+ echo performed keyword substitutions
+ cp $tmpfile $file
+
+ # check file into RCS
+ if ($firsttime) then
+ set firsttime = 0
+ if ($nodesc) then
+ echo about to do ci
+ echo ci -f -r$rev -d"$date" -w$author -t$emptyfile $file
+ ci -f -r$rev -d"$date" -w$author -t$emptyfile $file < $initialfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo initial rev checked into RCS without description
+ else
+ echo ""
+ echo Enter a brief description of the file $file \(end w/ Ctrl-D\):
+ cat > $tmpfile
+ ci -f -r$rev -d"$date" -w$author -t$tmpfile $file < $initialfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo initial rev checked into RCS
+ endif
+ else
+ # get RCS lock
+ set lckrev = `echo $rev | sed -e 's/\.[0-9]*$//'`
+ if ("$lckrev" =~ [0-9]*.*) then
+ # need to lock the brach -- it is OK if the lock fails
+ rcs -l$lckrev $file >>& $logfile
+ else
+ # need to lock the trunk -- must succeed
+ rcs -l $file >>& $logfile
+ if ($status != 0) goto ERROR
+ endif
+ echo got lock
+ sccs prs -r$rev $file | grep "." > $tmpfile
+ # it's OK if grep fails here and gives status == 1
+ # put the delta message in $tmpfile
+ ed $tmpfile >>& $logfile <<EOF
+/COMMENTS
+1,.d
+w
+q
+EOF
+ ci -f -r$rev -d"$date" -w$author $file < $tmpfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo checked into RCS
+ endif
+ sccs unedit $file >>& $logfile
+ if ($status != 0) goto ERROR
+ end
+ rm -f $file
+end
+
+
+############################################################
+# Clean up
+#
+echo cleaning up...
+mv SCCS old-SCCS
+rm -f $tmpfile $emptyfile $initialfile $sedfile
+echo ===================================================
+echo " Conversion Completed Successfully"
+echo ""
+echo " SCCS history now in old-SCCS/"
+echo ===================================================
+set exitval = 0
+goto cleanup
+
+ERROR:
+foreach f (`sccs tell`)
+ sccs unedit $f
+end
+echo ""
+echo ""
+echo Danger\! Danger\!
+echo Some command exited with a non-zero exit status.
+echo Log file exists in $logfile.
+echo ""
+echo Incomplete history in ./RCS -- remove it
+echo Original unchanged history in ./SCCS
+set exitval = 1
+
+cleanup:
+# leave log file
+rm -f $tmpfile $emptyfile $initialfile $sedfile $revfile
+
+exit $exitval
diff --git a/gnu/usr.bin/cvs/cvs/ChangeLog b/gnu/usr.bin/cvs/cvs/ChangeLog
new file mode 100644
index 000000000000..36c39062d15b
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/ChangeLog
@@ -0,0 +1,3205 @@
+Sat Dec 9 22:01:41 1995 Dan O'Connor <doconnor@tii.com>
+
+ * commit.c (check_fileproc): pass RUN_REALLY flag to run_exec,
+ because it's okay to examine the file with noexec set.
+
+Sat Dec 9 20:28:01 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (update_entries): new var, `bin, init to 0.
+ Use it in determining whether to convert the file.
+ (send_modified): same as above.
+
+Fri Dec 8 17:47:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (downcase_string): removed.
+ (check_repository_password): don't deal with case-insensitivity
+ anymore.
+
+ * options.h.in (CVS_PASSWORDS_CASE_SENSITIVE): deleted this. No
+ need for it anymore.
+
+Thu Dec 7 21:08:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_repository_password): when checking for false
+ prefix-matches, look for ':', not '@'. Duh.
+
+Thu Dec 7 18:44:51 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * options.h.in (CVS_PASSWORDS_CASE_SENSITIVE): replaces
+ CVS_PASSWORDS_CASE_INSENSITIVE; passwords are now insensitive by
+ default. Expanded explanatory comment.
+
+ * login.c (get_cvs_password): Use memset(), not bzero(). I
+ botched this change earlier.
+
+ * server.c (check_repository_password): no need to check
+ xmalloc()'s return value.
+ (check_repository_password): check for false prefix-matches (for
+ example, username is "theo" and linebuf contains user
+ "theocracy").
+
+Thu Dec 7 14:49:16 1995 Jim Meyering (meyering@comco.com)
+
+ * filesubr.c (isaccessible): Rename from isaccessable.
+ Update callers.
+ * cvs.h: Update prototype.
+ * main.c (main): Update callers.
+ * server.c (main): Update callers.
+
+Thu Dec 7 12:50:20 1995 Adam Glass <glass@NetBSD.ORG>
+
+ * cvs.h: "isaccessible" is the correct spelling.
+ Also add "const" to second arg to make prototype match
+ declaration.
+
+Thu Dec 7 11:06:51 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c, login.c: memset() instead of bzero().
+
+Thu Dec 7 00:08:53 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): document server's side of
+ the Authentication Protocol too.
+
+ * client.c (connect_to_pserver): when printing out "unrecognized
+ response", also print out the offending response.
+
+ * server.c (check_password): take `repository' arg too now.
+ Call check_repository_password() before checking /etc/passwd.
+ (check_repository_password): new func.
+
+ * options.h.in (CVS_PASSWORDS_CASE_INSENSITIVE): new define, unset
+ by default.
+
+Wed Dec 6 18:51:16 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_password): If user has a null password, then
+ return 1 if arg is also null.
+ Reverse sense of return value. Caller changed.
+
+Wed Dec 6 14:42:57 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_password): new func.
+ (authenticate_connection): call above new func.
+
+ * login.c (login): use construct_cvspass_filename().
+ If CVSroot is not "fully-qualified", then insist the user qualify
+ it before going on.
+ (get_cvs_password): fleshed out. Now reads from ~/.cvspass, or
+ prompts if no appropriate password found.
+ (construct_cvspass_filename): new func.
+
+ * server.c (authenticate_connection): send ACK or NACK to client.
+
+ * client.c (connect_to_pserver): check for ACK vs NACK response
+ from server after sending authorization request.
+
+ * login.c (get_cvs_password): new func.
+
+ * client.c (connect_to_pserver): use new func get_cvs_password().
+ Prototype it at top of file. Hmmm.
+
+Wed Dec 6 13:29:22 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c: same as below (AUTH_SERVER_SUPPORT).
+
+ * main.c: same as below (AUTH_SERVER_SUPPORT where appropriate).
+
+ * login.c: same same as below.
+
+ * cvs.h: same as below.
+
+ * client.c: use AUTH_CLIENT_SUPPORT, not CVS_LOGIN.
+
+ * options.h.in (AUTH_CLIENT_SUPPORT, AUTH_SERVER_SUPPORT): these
+ replace CVS_LOGIN.
+
+Wed Dec 6 00:04:58 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): expanded comment.
+
+Tue Dec 5 23:37:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (connect_to_pserver): read password from prompt for
+ now.
+
+ * server.c (authenticate_connection): if the password passes
+ muster, then don't abort.
+
+Tue Dec 5 22:46:37 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * subr.c (strip_trailing_newlines): new func.
+
+ * client.c (connect_to_pserver): took out print statements.
+
+ * server.c (authenticate_connection): removed print statments.
+ Use new func strip_trailing_newlines() to purify `repository',
+ `username', and `password'.
+ Run a primitive password check, just for testing.
+
+ * client.c (connect_to_pserver): use CVS_AUTH_PORT.
+ Take tofdp, fromfdp, and log args. Caller changed.
+ (get_responses_and_close): either kerberos and CVS_LOGIN might
+ have one fd for both directions, so adjust #ifdef accordingly.
+
+ * cvs.h (CVS_AUTH_PORT): new define, default to 2401.
+ Prototype strip_trailing_newlines().
+
+Tue Dec 5 16:53:35 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): new func.
+
+ * client.c (init_sockaddr): func moved here from login.c.
+ (connect_to_pserver): same as above. Take no args, now.
+ Include <sys/socket.h>, <netinet/in.h>, <netdb.h>, if CVS_LOGIN.
+
+ * cvs.h: Declare use_authenticating_server, as extern int.
+ Declare connect_to_pserver().
+
+ * main.c (main): call authenticate_connection(). Removed testing
+ code.
+ Add 'a' to the short-option string in the getopt() call.
+
+ * login.c (connect_to_pserver): moved to client.c.
+
+Tue Dec 5 16:01:42 1995 Peter Chubb <peterc@bookworm.sw.oz.au>
+ (patch applied by Karl Fogel <kfogel@cyclic.com>)
+
+ * update.c (join_file): if vers->vn_user is "0", file has been
+ removed on the current branch, so print an error and return.
+
+Mon Dec 4 14:27:42 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.3.
+
+Mon Dec 4 16:28:25 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * release.c (release): add return (0) as last line
+
+ * cvs.h: declare program_path
+
+ * main.c define program_path
+ (main): set program_path
+
+ * release.c (release): use program_path for update_cmd
+
+Mon Dec 4 11:22:42 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.2.
+
+Sun Dec 3 20:02:29 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * rcs.h (struct rcsnode), rcs.c (freercsnode): Add expand field.
+ * rcs.h (RCSEXPAND): New #define.
+ * rcs.c (RCS_reparsercsfile): Record keyword expansion in expand
+ field of struct rcsnode.
+ * update.c (checkout_file): Set keyword expansion in Entries file
+ from rcs file if there is nowhere else to set it from.
+ * client.c (send_modified, update_entries) [LINES_CRLF_TERMINATED]:
+ If -kb is in effect, don't convert.
+
+ * update.c (update_file_proc), commit.c (check_fileproc),
+ rcscmds.c (RCS_merge): Direct stdout to DEVNULL rather than
+ passing -s option to grep. This avoids trouble with respect to
+ finding a grep which support -s and whether we should use the (GNU
+ grep) -q option if it exists.
+ * options.h.in: Change "@ggrep_path@" to "grep".
+
+Fri Dec 1 11:53:19 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * rcs.c (RCS_gettag): new parameter return_both force return both
+ tags: the symbolic and the numeric one.
+ (RCS_getversion): new parameter return_both is forwarded to
+ RCS_gettag.
+
+ * rtag.c, tag.c, commit.c, patch.c, update.c: pass 0 as additional
+ last parameter to RCS_getversion and RCS_gettag
+
+ * rcs.h (RCS_gettag): new parameter return_both.
+ (RCS_getversion): new parameter return_both.
+
+ * cvs.h (struct vers_ts): add vn_tag slot for symbolic tag name
+
+ * vers_ts.c (Version_TS): call RCS_getversion with 1 for
+ return_both and split output into vn_rcs and vn_tag
+ (freevers_ts): free vn_tag
+
+ * update.c (checkout_file): use vn_tag instead of vn_rcs when
+ calling 'rcs co' to allow rcs expansion of :$Name :
+
+Thu Nov 30 20:44:30 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (get_responses_and_close): undo previous change
+ regarding waitpid(). The problem has been solved by modifying
+ os2/waitpid.c instead of its callers.
+
+Thu Nov 30 16:37:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c: All these changes are for OS/2, which will no longer have
+ a separate client.c:
+ (start_kerberos_server): new func, contains code that
+ used to be in start_server().
+ (start_server): moved kerberos code to above function, reorganized
+ the rest. Added authentication clause.
+ (call_in_directory): test errno against EACCESS, if EACCESS is
+ defined (this is for OS/2's oddball mkdir).
+ (change_mode): don't set execute permission on anything if
+ EXECUTE_PERMISSION_LOSES is defined.
+ (get_responses_and_close): if START_RSH_WITH_POPEN_RW, then use
+ pclose() instead of fclose().
+ If waitpid errors with ECHILD, don't die. This is okay.
+ (start_rsh_server): alternate definition if
+ START_RSH_WITH_POPEN_RW.
+
+ * main.c: [all these changes conditional on CVS_LOGIN: ]
+ Don't prototype connect_to_pserver, don't enter it in cmds[]
+ (actually, it was never in there, I don't know why my previous
+ change said it was).
+ (use_authenticating_server): new global var.
+ (main): if "-a", then set above new var to TRUE.
+ (usg): document "-a" option.
+
+Wed Nov 29 12:55:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c: Prototype connect_to_pserver(), and enter it in cmds[].
+ (main): test some extremely primitive authentication.
+
+ * login.c: Include <sys/socket.h>
+ (connect_to_pserver): new func.
+ (init_sockaddr): new func.
+
+Mon Nov 20 14:07:41 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (TAGFILES): Separate out from DISTFILES, for C code.
+ (TAGS,tags): Use TAGFILES not DISTFILES.
+
+Sun Nov 19 11:22:43 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * recurse.c (do_recursion): Don't call server_pause_check if there
+ are writelocks around. Revise comment to reflect fact we are no
+ longer relying on a writelock'd operations being "unable" to
+ generate enough data to pause.
+
+Sun Nov 19 10:04:50 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * server.c, server.h, options.h.in: Implement hooks for doing
+ simple flow control on the server to prevent VM exhaustion on a
+ slow network with a fast server.
+ * recurse.c: Call the flow control check at a convenient location
+ while no locks are active. This is a convenience tradeoff against
+ accurate flow control - if you have a large directory it will all
+ be queued up, bypassing the flow control check until the next
+ directory is processed.
+
+Sat Nov 18 16:22:06 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c, update.c, vers_ts.c, server.c, rcs.c, lock.c,
+ ignore.c, entries.c, diff.c, commit.c, checkin.c:
+ Use new macro `existence_error', instead of comparing errno to
+ ENOENT directly.
+
+Fri Nov 17 14:56:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (start_server): removed alternate version of this func,
+ since os2/client.c will now be used under OS/2.
+
+Thu Nov 16 22:57:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (start_server): ifdef HAVE_POPEN_RW, use a different
+ version of start_server(). This is maybe not the cleanest cut to
+ make, but it's better than mucking around with yet more #ifdefs in
+ the middle of the old start_server() function. Once things are
+ up, I may reposition this code.
+
+Wed Nov 15 15:33:37 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): ifdef NEED_CALL_SOCKINIT, then call SockInit().
+ Only OS/2 needs this initialization.
+
+Tue Nov 14 18:54:01 1995 Greg A. Woods <woods@most.weird.com>
+
+ * patch.c:
+ - fix orientation of test for result of getline() call
+ - use fputs() not printf() when just copying file out
+
+ * cvsbug.sh:
+ - add space after #!
+ - new rcs id
+ - allow version to be edited by Makefile.
+
+ * Makefile.in:
+ - make Makefile a dependent of all (this might not be perfect, but
+ it at least gives you a chance to catch up on the second
+ go-around).
+ - filter cvsbug.sh in a manner similar to cvsinit.sh to get the
+ version number set from version.c
+
+Tue Nov 14 13:28:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Call old log file check.plog, not check.olog.
+
+ * sanity.sh: Convert remaining tests from old-style ('***' on fail
+ and nothing on pass), to new-style (FAIL on fail and PASS on pass).
+
+ * sanity.sh: Fix ability to run only some of the tests (always run
+ tests 1-4.75 to set up repository, document better how it works).
+
+ * sanity.sh: Change "completed successfully" to "completed" in
+ message--many tests, but not all, exit if they fail.
+
+Tue Nov 14 15:10:00 1995 Greg A. Woods <woods@most.weird.com>
+
+ * sanity.sh: test 63 doesn't work and probably can't
+
+Tue Nov 14 12:22:00 1995 Greg A. Woods <woods@most.weird.com>
+
+ * sanity.sh: many minor tweaks:
+ - make the optional arguments almost work
+ - use a function 'directory_cmp' instead of 'diff -r'
+ - fix up a few more tests that weren't working....
+
+Mon Nov 13 07:33:55 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: ifdef USE_OWN_POPEN, #include "popen.h". Only OS/2 has
+ its own popen()/pclose() right now.
+
+Mon Nov 13 04:06:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: conform to 80 column standard (yes, I'm a pedant).
+
+Sat Nov 11 13:45:13 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (process_prune_candidates): use unlink_file_dir() to
+ remove the directory, instead of invoking "rm" via run_exec().
+
+Fri Nov 10 14:38:56 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): removed "#define KF_GETOPT_LONG 1", since that
+ change is no longer in testing.
+
+Thu Nov 9 20:32:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * release.c (release): Use Popen(), not popen().
+
+Wed Nov 8 10:20:20 1995 Jim Meyering (meyering@comco.com)
+
+ * entries.c (ParseTag): Remove dcl of unused local.
+
+ * patch.c: Include getline.h.
+
+Wed Nov 8 11:57:31 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * options.h.in: add configuration option STEXID_SUPPORT (default
+ is off i.e. old semantics)
+
+ * filesubr.c (isaccessable): new function. Checks access-rights
+ for files like access(), but is getxid-safe. Falls back to
+ access() if SETXID_SUPPORT is not enabled.
+ (isfile): replace stat() by isaccessable(file, F_OK)
+ (isreadable): replace access() by isaccessable()
+ (iswritable): ditto
+ (make_directory): rename local variable buf to sb
+
+ * cvs.h: add prototype for new function isaccessable.
+
+ * server.c (serve_root): replace access() by isaccessable()
+
+ * cvsrc.c (read_cvsrc): replace access() by isreadable()
+
+ * main.c (main): replace access() by isaccessable()
+
+Wed Nov 8 10:22:41 1995 Greg A. Woods <woods@most.weird.com>
+
+ * entries.c (fgetentent): change definition to static to match the
+ declaration at the top of the file
+
+Tue Nov 7 16:59:25 1995 J.T. Conklin <jtc@lestat.cygnus.com>
+
+ * rcs.c (RCS_getbranch, RCS_getdate, RCS_getrevtime, RCS_gettag,
+ RCS_getversion, RCS_head): Use assert() instead of attempting to
+ "do the right thing" with a bogus RCSNode argument.
+
+Mon Nov 6 14:24:34 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * vers_ts.c: Remove ctime define. It is just asking for trouble.
+
+Mon Nov 6 11:58:26 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * vers_ts.c: ifdef ctime, undef it before redefining it. It is a
+ macro on some systems.
+
+ * lock.c: don't prototype ctime() here. (See note below about
+ fgetentent() in entries.c.)
+
+Sun Nov 5 16:06:01 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * entries.c (fgetentent): don't prototype ctime here; we include
+ cvs.h, which includes system.h, which includes <time.h>
+ unconditionally (either as <time.h> or <sys/time.h>). Anyway, IBM
+ C/C++ chokes on mid-function, or even mid-file, prototypes. Sigh.
+
+Thu Nov 2 21:51:04 1995 Dan Wilder <dan@gasboy.com>
+
+ * rtag.c (rtag): Fix typo ("-T" -> "-F").
+
+Tue Oct 31 19:09:11 1995 Dan Wilder <dan@gasboy.com>
+
+ * diff.c (diff_dirproc): just return R_SKIP_ALL if dir not exist.
+ (diff_file_nodiff): don't complain if file doesn't exist, just
+ ignore.
+
+Tue Oct 31 09:25:10 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * sanity.sh: Use absolute pathname for mkmodules.
+
+Sat Oct 28 01:01:41 1995 Jim Meyering (meyering@comco.com)
+
+ * entries.c (ParseTag): Use getline instead of fgets.
+
+Fri Oct 27 13:44:20 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: do nothing about alloca ifdef ALLOCA_IN_STDLIB. I am
+ rather suspicious of this solution, and will not be surprised to
+ find out that there's a Right Way to handle this situation ("this
+ situation" being that OS/2 simply declares alloca in <stdlib.h>).
+ Suggestions are welcome; see src/cvs.h and lib/system.h to see why
+ I was getting a conflict in the first place.
+
+Wed Oct 25 16:03:20 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * cvs.h (struct entnode): Add user field.
+ * entries.c (fputentent): New function, write entries line.
+ (write_ent_proc): Call fputentent to write entries line.
+ (Entnode_Create): New function, construct new Entnode.
+ (Entnode_Destroy): New function, destruct old Entnode.
+ (AddEntryNode): Changed to take an Entnode argument instead of
+ separate user, version, timestamp, etc. arguments.
+ (fgetentent): Changed to return Entnode.
+ (struct entent, free_entent): Removed.
+
+Wed Oct 25 12:44:32 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * admin.c (admin): Don't rely on ANSI C string concatenation;
+ SunOS 4.1.3 /bin/cc doesn't support it.
+
+Tue Oct 24 22:34:22 1995 Anthony J. Lill <ajlill@ajlc.waterloo.on.ca>
+
+ * import.c (expand_at_signs): Check errno as well as return value
+ from putc. Some systems bogusly return EOF when successfully
+ writing 0xff.
+
+Tue Oct 24 14:32:45 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * admin.c (admin): use getcaller() instead of getpwuid
+
+ * subr.c (getcaller): prefer getlogin() to $USER and $LOGNAME
+ (especially useful for NT where getuid always returns 0)
+
+Tue Oct 24 06:22:08 1995 Jim Meyering (meyering@comco.com)
+
+ * cvsrc.c (read_cvsrc): Use getline instead of fgets.
+ * patch.c (patch_fileproc): Use getline instead of fgets.
+
+ * entries.c (fgetentent): Use getline instead of fgets.
+ Use xmalloc to allocate space for each returned entry.
+ Since LINE is no longer static, save it in struct entent.
+ (struct entent): New member, line.
+ (free_entent): New function.
+ (Entries_Open): Call it after each call to fgetentent.
+
+Tue Oct 24 11:13:15 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * cvs.h: Declare valloc again, but this time with the right
+ signature (also changed in libs/valloc.c)
+
+Mon Oct 23 12:17:03 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * logmsg.c (do_editor): Check for errors from stdio calls.
+
+Mon Oct 23 12:37:06 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h: Don't declare valloc. Some systems (e.g. linux) declare
+ it in stdlib.h in a conflicting way.
+
+Mon Oct 23 08:41:25 1995 Jim Meyering (meyering@comco.com)
+
+ * commit.c (commit_filesdoneproc): Use getline instead of fgets.
+
+ * logmsg.c (do_editor): Use getline instead of fgets.
+ (rcsinfo_proc): Likewise.
+
+ * logmsg.c (do_editor): Lose if fclose of temp file output
+ stream fails.
+
+Mon Oct 23 11:59:41 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * cvs.h: add valloc declaration
+
+ * server.h: add server_cleanup prototype
+
+ * server.c: remove server_cleanup prototype
+
+ * mkmodules.c (server_cleanup): fix parameter type
+
+ * server.c: encapsulate wait_sig in #ifdef sun (it's only used in
+ code which is also encapsulated in #ifdef sun)
+
+ * rcscmds.c (RCS_deltag, RCS_lock): add definition of noerr
+ parameter
+
+ * error.c: include cvs.h instead of config.h, add USE(rcsid)
+
+ * error.c (error): fix parameter type
+
+ * update.c (join_file): encapsulate recent changes from garyo
+ within #ifdef SERVER_SUPPORT
+
+Sun Oct 22 13:47:53 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * client.c (update_entries): Fix memory leak; free mode_string and
+ file_timestamp.
+ (send_fileproc): Fix memory leak; call freevers_ts before exiting.
+
+ * module.c (do_module): Partially fix memory leak; added
+ variable so that the address of memory allocated by line2argv
+ is retained, but comment out the call to free_names. Freeing
+ the vector at that point loses because some of the elements
+ may be used later in the function.
+ (cat_module): fix memory leak.
+
+ * recurse.c (start_recursion): Fix memory leak; free return
+ value of Name_Repository after it has been used.
+
+Sat Oct 21 23:24:26 1995 Jim Meyering (meyering@comco.com)
+
+ * client.c (send_modified) [LINES_CRLF_TERMINATED]: Comment text
+ after #endif.
+
+Fri Oct 20 14:41:49 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Add test 87a, to test for bug fixed by garyo in
+ change below.
+
+Fri Oct 20 10:59:58 1995 Gary Oberbrunner <garyo@darkstar.avs.com>
+
+ * update.c (join_file): send file back to client even if no
+ conflicts were detected, by calling Register().
+
+Fri Oct 20 10:46:45 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * lock.c: Add prototype for Check_Owner
+
+Thu Oct 19 16:38:14 1995 Jim Meyering (meyering@comco.com)
+
+ * lock.c (Check_Owner): Declare function `static int'.
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * expand_path.c (expand_variable): Fix typo ('*'->'(').
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * commit.c (commit_filesdoneproc): Check for errors from fopen,
+ fgets, and fclose.
+
+ * rcscmds.c (RCS_merge): Remove comment about rcsmerge -E.
+ Hacking CVS was never a very good solution; the situation is fixed
+ in RCS 5.7, and is documented in ../INSTALL.
+
+Thu Oct 19 15:06:15 1995 Jim Meyering (meyering@comco.com)
+
+ * filesubr.c (xchmod): Parenthesize arithmetic in operand of |
+ to placate gcc -Wall.
+
+ * expand_path.c (expand_path): Parenthesize assignments used as
+ truth values to placate gcc -Wall.
+
+ * commit.c (checkaddfile): Remove dcls of unused variables.
+ * lock.c (unlock): Remove dcl of unused variable.
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * root.c (Create_Root): If noexec, don't create CVS/Root.
+
+Wed Oct 18 11:19:40 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * lock.c (unlock): Change order of comparison so that Check_Owner
+ is called only if other conditions are true. This performance
+ enhancement was broken when the AFS support was added.
+
+Wed Oct 18 12:51:33 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): check if argv[0] is "pserver" with else-if, not
+ if, since we've already asked if it's "kserver".
+
+Tue Oct 17 18:09:23 1995 Warren Jones <wjones@tc.fluke.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Deal with supplying a relative cvs filename, or
+ with a cvs filename which doesn't have basename "cvs".
+
+Mon Oct 16 15:58:31 1995 Vince Demarco <vdemarco@bou.shl.com>
+
+ * parseinfo.c (Parse_Info): if the Keyword isn't ALL the current
+ version doesn't use the expanded variable, It should.
+
+Mon Oct 16 15:58:31 1995 Gary Oberbrunner <garyo@avs.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c (server_register): Don't pass NULL to printf if tag,
+ date, or conflict is NULL.
+
+Thu Oct 12 12:13:42 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): begin to handle "pserver"; support not complete
+ yet, however.
+
+Thu Oct 12 02:52:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * expand_path.c: Don't #include <pwd.h>, since cvs.h already does,
+ and not all systems' <pwd.h>s are protected from multiple inclusion.
+ * login.c: Likewise.
+
+Wed Oct 11 15:23:24 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): handle everything correctly now.
+
+Wed Oct 11 12:02:48 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * rcs.c (RCS_gettag): support RCS keyword Name
+
+Tue Oct 10 19:11:16 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * options.h.in (CVS_LOGIN): discuss, but leave commented out.
+ The "cvs login" command is still under construction; however, the
+ repository was changing so fast that instead of creating a branch
+ and dealing with the attendant hair, I'm just developing on the
+ trunk, making sure that everything is surrounded by "#ifdef
+ CVS_LOGIN ... #endif" so I don't get in anyone's way.
+
+ * login.c: include cvs.h before checking CVS_LOGIN, so it has a
+ chance to get defined before we ask if it's defined.
+ (login): oops, use semi not comma in `for' loop init.
+
+ * Makefile.in (SOURCES, OBJECTS): include login.c, login.o.
+
+ * main.c: added protoype for login().
+ Added "login" entry to cmds[].
+ (usg): added line about "login".
+
+ * login.c: new file.
+
+Tue Oct 10 18:33:47 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * Makefile.in (COMMON_OBJECTS): added error.o.
+ (OBJECTS): took error.o out; it's in COMMON_OBJECTS now.
+
+Tue Oct 10 12:02:37 1995 Thorsten Lockert <tholo@sigmasoft.com>
+
+ * cvsbug.sh: Cater to lame versions of sh (4.4BSD ash) by using
+ ${foo-bar} instead of `if....`.
+
+Tue Oct 10 12:02:37 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * remove.c (remove_fileproc): If noexec, don't remove file. Check
+ for error when removing file.
+
+Sun Oct 8 12:32:15 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * run.c: detect/use POSIX/BSD style reliable signals for critical
+ section masking etc. Helps prevent stray locks on interruption.
+
+Sat Oct 7 23:26:54 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * admin.c (admin): If group CVS_ADMIN_GROUP exists, allow only
+ users in that group to use "cvs admin".
+ * options.h.in: Default CVS_ADMIN_GROUP to "cvsadmin".
+
+Sat Oct 7 23:05:24 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * add.c, checkout.c, commit.c, cvs.h, filesubr.c, import.c,
+ lock.c, main.c, modules.c, options.h.in: New variable cvsumask
+ which is used to set mode of files in repository (regardless of
+ umask in effect when cvs is run).
+
+Sat Oct 7 22:40:17 1995 Stephen Bailey <sjbailey@sand.npl.washington.edu>
+
+ * lock.c: Include AFSCVS ifdefs to deal with AFS's lack of
+ correspondance between userid's from stat and from geteuid.
+
+Sat Oct 7 22:28:49 1995 Scott Carson <sdc@TracerTech.COM>
+
+ * add.c (add): Pass -ko, not -k -ko, to set keyword expansion options.
+
+ * admin.c (admin): Don't skip first argument when sending to server.
+
+Fri Oct 6 21:45:03 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * version.c: Version 1.6.1.
+
+Fri Oct 6 21:31:28 1995 Jeff Johnson <jbj@brewster.jbj.org>
+
+ * cvs.h, admin.c, client.c, commit.c, log.c, modules.c,
+ parseinfo.c, patch.c, recurse.c, rtag.c, status.c, tag.c:
+ Prototype when dealing in pointers to functions.
+
+Fri Oct 6 21:07:22 1995 Mark H. Wilkinson <mhw@minster.york.ac.uk>
+
+ * cvsrc.c (read_cvsrc): fix look up of command names in cvsrc file
+ to use full name from command table rather than possible nickname
+ in argv. Fixes errors with things like `cvs di' when cvsrc has
+ `diff -u5' in it.
+
+Thu Aug 3 01:03:52 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * parseinfo.c (Parse_Info): Add code to call expand_path function
+ instead of using built in code.
+
+ * wrapper.c (wrap_add): Add code to call expand_path function to
+ expand all built in variables.
+
+ * expand_path.c (New file): expand things that look like
+ environmental variables (only expand local CVS environmental
+ variables) and user names like ~/.
+ * cvs.h: Declare expand_path.
+
+ * Makefile.in (SOURCES, OBJECTS): Added expand_path.c,
+ expand_path.o.
+
+Fri Oct 6 14:03:09 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * ignore.c (ign_setup): Don't try to look for a file in CVSroot if
+ client. (The recent tightening of the error checking detects this).
+
+ * commit.c (checkaddfile): Don't try to pass options if it is "".
+
+Thu Oct 5 18:04:46 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * sanity.sh: unset CVSREAD, since it causes the script to bomb.
+
+Thu Oct 5 18:29:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * remove.c, add.c, commit.c, cvs.h: Remove CVSEXT_OPT stuff; it
+ has been broken for ages and the options are already stored in the
+ Entries file.
+
+Thu Oct 5 18:20:13 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * commit.c (checkaddfile): New argument options; pass it to RCS.
+ (commit_fileproc): Pass it.
+
+Tue Oct 3 09:26:00 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * version.c: upped to 1.6.
+
+Mon Oct 2 18:10:35 1995 Larry Jones <larry.jones@sdrc.com>
+
+ * server.c: if HAVE_SYS_BSDTYPES_H, include <sys/bsdtypes.h>.
+
+Mon Oct 2 10:34:53 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * version.c: Upped version to 1.5.95.
+
+Mon Oct 2 15:16:47 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * tag.c, rtag.c: pass "mov" instead of "add" if tag will be moved
+ (i.e. invoked with -F)
+
+Sun Oct 1 18:36:34 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * version.c: upped to 1.5.94.
+
+ * server.c: reverted earlier ISC change (of Sep. 28).
+
+ * version.c: upped to 1.5.93, for Peter Wemm's new SVR4 patch.
+
+Sun Oct 1 14:51:59 1995 Harlan Stenn <Harlan.Stenn@pfcs.com>
+
+ * main.c: don't #include <pwd.h>; cvs.h does that already.
+
+Fri Sep 29 15:21:35 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * version.c: upped to 1.5.91 for another pre-1.6 release.
+
+Fri Sep 29 14:41:14 1995 <bmeier@rzu.unizh.ch>
+
+ * root.c: start rcsid[] with "CVSid".
+
+Fri Sep 29 13:22:44 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * diff.c (diff): Doc fix.
+
+Fri Sep 29 14:32:36 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * repos.c (Short_Repository): chop superfluous "/".
+
+ * tag.c (pretag_proc): correct user-visible string.
+
+ * rtag.c (pretag_proc): correct user-visible string.
+
+Fri Sep 29 13:45:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h (USE): if __GNUC__ != 2, expand to a dummy var instead of
+ nothing.
+
+Thu Sep 28 13:37:05 1995 Larry Jones <larry.jones@sdrc.com>
+
+ * server.c: ifdef ISC, include <sys/bsdtypes.h>.
+
+Fri Sep 29 07:54:22 1995 Mike Sutton <mws115@llcoolj.dayton.saic.com>
+
+ * filesubr.c (last_component): Don't use ANSI style declaration.
+
+Wed Sep 27 15:24:00 1995 Del <del@matra.com.au>
+
+ * tag.c, rtag.c: Pass a few extra options to the script
+ named in taginfo (del/add, and revision number).
+
+ * tag.c: Support a -r option (at long last). Also needs
+ a -f option to tag the head if there is no matching -r tag.
+
+Tue Sep 26 11:41:08 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * version.c: Upped version to 1.5.89 for test release preceding
+ 1.6.
+
+Wed Sep 20 15:32:49 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * ignore.c (ign_add_file): Check for errors from fopen and fclose.
+
+Tue Sep 19 18:02:16 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * Makefile.in (DISTFILES): Remove sanity.el from this list; the
+ file has been deleted.
+
+Thu Sep 14 14:17:52 1995 Peter Wemm <peter@haywire.dialix.com>
+
+ * import.c: Recover from being unable to open the user file.
+
+ * update.c (join_file): Print a message in the case where the file
+ was added.
+
+ * mkmodules.c: Deal with .db as well as .pag/.dir (for use with
+ BSD 4.4 and real dbm support).
+
+Mon Sep 11 15:44:13 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * release.c (release): Revise comment regarding why and how we
+ skip argv[0].
+
+Mon Sep 11 10:03:59 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * release.c (release): use return value of pclose to determine
+ success of update.
+
+Mon Sep 11 09:56:33 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * release.c (release_delete): Fix comment.
+
+Sun Sep 10 18:48:35 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * release.c (release): made work with client/server.
+ Don't ask if <arg> is mentioned in `modules'.
+
+Fri Sep 8 13:25:55 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: When committing a removal, send stdout to LOGFILE;
+ this is no longer a silent operation.
+
+ * sanity.sh: Remove OUTPUT variable; it is unused.
+
+ * client.c: Add comment regarding deleting temp file.
+ * main.c: Add comment regarding getopt REQUIRE_ORDER.
+
+Thu Sep 7 20:24:46 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): use getopt_long(), accept "--help" and
+ "--version".
+ Don't assume EOF is -1.
+
+Thu Sep 7 19:18:00 1995 Jim Blandy <jimb@cyclic.com>
+
+ * cvs.h (unlink_file_dir): Add prototype for this.
+
+Thu Sep 7 14:38:06 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * ALL FILES: add semicolon, as indicated below.
+
+ * cvs.h (USE): don't provide semicolon in the expansion of the USE
+ macro; we'd rather the callers provided it themselves because that
+ way etags doesn't get fooled.
+
+Mon Sep 4 23:30:41 1995 Magnus Hyllander <mhy@os.se>
+
+ * checkout.c: cvs export now takes -k option and does not default
+ to -kv.
+ * checkout.c, cvs.h, modules.c: Modules file now takes -e option
+ for cvs export.
+
+Mon Sep 4 23:30:41 1995 Kirby Koster <koster@sctc.com>
+
+ * commit.c: When committing a removal, print a message saying what
+ we are doing.
+
+Wed Aug 2 10:06:51 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * server.c: fix compiler warnings (on NeXT) (declare functions as
+ static inline instead of just static) functions: get_buffer_date,
+ buf_append_char, and buf_append_data
+
+Mon Sep 4 22:31:28 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (update_entries), import.c (expand_at_signs): Check for
+ errors from fread and putc.
+
+Fri Sep 1 00:03:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Fix TODO item pathname.
+
+ * sanity.el: Removed. It was out of date, didn't do much, and I
+ doubt anyone was using it.
+
+ * no_diff.c (No_Difference): Don't change the modes of the files.
+
+Thu Aug 31 13:14:34 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * version.c: Change version to 1.5.1.
+
+ * client.c (start_rsh_server): Don't pass -d to "cvs server"
+ invocation via rsh (restore change which was lost when NT stuff
+ was merged in).
+ * sanity.sh: Add TODO item suggesting test for bug which this fixes.
+
+Wed Aug 30 12:36:37 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * sanity.sh (basic1): Make sure first-dir is deleted before
+ running this set of tests.
+
+ * subr.c: Extract file twiddling functions to a different file,
+ because we want to use different versions of many of these
+ routines under Windows NT.
+ (copy_file, isdir, islink, isfile, isreadable, iswritable,
+ open_file, make_directory, make_directories, xchmod,
+ rename_file, link_file, unlink_file, xcmp, tmpnam,
+ unlink_file_dir, deep_remove_dir): Moved to...
+ * filesubr.c: ...this file, which is new.
+ * Makefile.in (SOURCES): Mention filesubr.c.
+ (COMMON_OBJECTS): Mention filesubr.o.
+
+ * subr.c: Extract process execution guts to a different file,
+ because we want to replace these routines entirely under
+ Windows NT.
+ (VA_START, va_alist, va_dcl): Move this stuff...
+ (run_add_arg, run_init_prog): and these declarations...
+ (run_prog, run_argv, run_argc, run_argc_allocated): and these
+ variables...
+ (run_setup, run_arg, run_args, run_add_arg, run_init_prog,
+ run_exec, run_print, Popen): and these functions...
+ * run.c: To this file, which is new.
+ * Makefile.in (SOURCES): Mention run.c.
+ (COMMON_OBJECTS): Mention run.o.
+
+ * status.c (status): Call ign_setup, if client_active. Otherwise,
+ we don't end up ignoring CVS directories and such.
+
+ * server.c (mkdir_p, dirswitch): Use CVS_MKDIR instead of mkdir.
+
+ * repos.c (Name_Repository): Use the isabsolute function instead of
+ checking the first character of the path.
+ * root.c (Name_Root): Same.
+
+ * release.c (release): Use fncmp instead of strcmp to compare
+ filenames.
+
+ * rcs.c (RCS_parse, RCS_parsercsfile) [LINES_CRLF_TERMINATED]:
+ Abort, because we have strong reason to believe this code is
+ wrong.
+
+ * patch.c (patch): Register signal handlers iff the signal name is
+ #defined.
+
+ * no_diff.c (No_Difference): Don't try to include server_active in
+ trace message unless SERVER_SUPPORT is #defined.
+
+ * modules.c (do_module): Use CVS_MKDIR instead of mkdir.
+
+ * mkmodules.c (main): Call last_component instead of writing it out.
+
+ * main.c (main): Call last_component instead of writing it out.
+ Break up the long copyright string into several strings; Microsoft
+ Visual C++ can't handle a line that long. Feh.
+ Use fncmp instead of strcmp to compare filenames.
+ Register signal handlers iff the signal name is #defined.
+
+ * lock.c (readers_exist): Don't check return value of closedir.
+ Most of the rest of the code doesn't, and some systems don't
+ provide a return value anyway.
+ (set_lock): Use CVS_MKDIR instead of mkdir.
+
+ * import.c (import): Use the isabsolute function instead of
+ checking the first character of the path.
+ Try to delete the temporary file again after we close it, so it'll
+ get deleted on systems that don't let you delete files that are
+ open.
+ (add_rev): Instead of making a hard link to the working file and
+ checking in the revision with ci -r, use ci -u and restore the
+ permission bits.
+ (comtable): Include lines from SYSTEM_COMMENT_TABLE, if it is
+ #defined.
+ (add_rcs_file) [LINES_CRLF_TERMINATED]: Abort, because we have
+ strong reason to believe this code is wrong.
+ (import_descend_dir): Use CVS_MKDIR instead of mkdir.
+
+ * history.c (read_hrecs): Open the file with OPEN_BINARY.
+
+ * find_names.c (add_entries_proc, fsortcmp): Add prototypes.
+ * entries.c (write_ent_proc): Add prototype.
+ * hash.c (walklist): Add prototype for PROC argument.
+ (sortlist): Add prototype for COMP argument.
+ (printnode): Add a prototype, and make it static.
+
+ * cvs.h (wrap_add_file, wrap_add): Add extern decls for these;
+ they're used in import.c and update.c.
+ * wrapper.c (wrap_add_file, wrap_add): Remove them from here.
+
+ * cvs.h (RUN_NORMAL, RUN_COMBINED, RUN_REALLY, RUN_STDOUT_APPEND,
+ RUN_STDERR_APPEND, RUN_SIGNIGNORE, RUN_TTY, run_arg, run_print,
+ run_setup, run_args, run_exec, Popen, piped_child, close_on_exec,
+ filter_stream_through_program, waitpid): Move all these
+ declarations and definitions to the same section.
+
+ * cvs.h (error_set_cleanup): Fix prototype.
+
+ * cvs.h (isabsolute, last_component): New extern decls.
+
+ * cvs.h (link_file): Function is deleted; remove extern decl.
+
+ * cvs.h (DEATH_STATE, DEATH_SUPPORT): Move #definitions of these
+ above the point where we #include rcs.h, since rcs.h tests them
+ (or DEATH_SUPPORT, at least).
+
+ * cvs.h (DEVNULL): #define this iff it isn't already #defined.
+ config.h may want to override it.
+
+ * cvs.h (SERVER_SUPPORT, CLIENT_SUPPORT): Don't #define these
+ here; let config.h do that. On some systems, we don't have any
+ server support.
+
+ * cvs.h: Don't #include <io.h> or <direct.h>; we take care of
+ those in lib/system.h.
+
+ * commit.c (commit): Open logfile with the OPEN_BINARY flag.
+ (precommit_proc): Use the isabsolute function, instead of
+ comparing the first character with /.
+ (remove_file, checkaddfile): Use CVS_MKDIR instead of mkdir.
+
+ * client.c (send_repository): Use larger line buffers.
+
+ * client.c [LINES_CRLF_TERMINATED] (update_entries): If we've just
+ received a gzipped file, copy it over, converting LF to CRLF,
+ instead of just renaming it into place.
+ [LINES_CRLF_TERMINATED] (send_modified): Convert file to LF format
+ before sending with gzip.
+ (send_modified): Don't be disturbed if we get fewer than
+ sb.st_size characters when we read. The read function may be
+ collapsing CRLF to LF for us.
+
+ * client.c: Add forward declarations for all the cvs command
+ functions we call.
+
+ * client.c: Add forward static declarations for all the
+ handle_mumble functions.
+
+ On some systems, RSH converts LF to CRLF; this screws us up.
+ * client.c (rsh_pid): Declare this iff RSH_NOT_TRANSPARENT is not
+ #defined.
+ (get_responses_and_close): Use SHUTDOWN_SERVER if it is #defined.
+ Only wait for rsh process to exit if RSH_NOT_TRANSPARENT is not
+ #defined.
+ (start_rsh_server): Declare and define only if
+ RSH_NOT_TRANSPARENT is not #defined. Use piped_child, instead of
+ writing all that out.
+ (start_server): Only try to call start_rsh_server if
+ RSH_NOT_TRANSPARENT is not #defined. Use START_SERVER if it is
+ #defined. Convert file descriptors to stdio file pointers using
+ the FOPEN_BINARY_WRITE and FOPEN_BINARY_READ strings.
+
+ * client.h (rsh_pid): Don't declare this; it's never used elsewhere.
+ (supported_request): Add external declaration for this;
+ it's used in checkout.c.
+
+ Move process-running functions to run.c; we need to totally
+ replace these on other systems, like Windows NT.
+ * client.c (close_on_exec, filter_stream_through_program): Moved
+ to run.c.
+ * run.c (close_on_exec, filter_stream_through_program): Here they
+ are.
+
+ * add.c (add_directory): Use CVS_MKDIR instead of straight mkdir.
+ * checkout.c (checkout, build_dirs_and_chdir): Same.
+ (checkout_proc): Use fncmp instead of strcmp.
+ * client.c (call_in_directory): Use CVS_MKDIR instead of straight
+ mkdir.
+
+ * client.c (handle_checksum): Cast return value of strtol.
+
+Wed Aug 30 10:35:46 1995 Stefan Monnier <stefan.monnier@epfl.ch>
+
+ * main.c (main): Allow -d to override CVSROOT_ENV.
+
+Thu Aug 24 18:57:49 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h, rcscmds.c (RCS_unlock, RCS_deltag, RCS_lock): Add extra
+ parameter for whether to direct stderr to DEVNULL.
+ * checkin.c, tag.c, rtag.c, import.c, commit.c: Pass extra
+ argument. 1 if stderr had been directed to DEVNULL before
+ rcscmds.c was in use, 0 if it was RUN_TTY.
+
+ * cvs.h: Add comment regarding attic.
+
+Tue Aug 22 10:09:29 1995 Alexander Dupuy <dupuy@smarts.com>
+
+ * rcs.c (whitespace): Cast to unsigned char in case char is signed
+ and value is negative.
+
+Tue Aug 22 10:09:29 1995 Kirby Koster <koster@sctc.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * update.c (join_file): If vers->vn_user is NULL, just return.
+
+Tue Aug 22 10:09:29 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c, client.c: Add comments about modes and umasks.
+
+Mon Aug 21 12:54:14 1995 Rick Sladkey <jrs@world.std.com>
+
+ * update.c (update_filesdone_proc): If pipeout, don't try to
+ create CVS/Root.
+
+Mon Aug 21 12:54:14 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (start_rsh_server): Don't pass -d to "cvs server"
+ invocation via rsh.
+
+ * server.c (serve_root): Report errors via pending_error_text.
+ (serve_valid_requests): Check for pending errors.
+
+Sun Aug 20 00:59:46 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * options.h.in: Document usage of DIFF in update.c
+ * update.c: Use DIFF -c, not DIFF -u. The small improvement in
+ diff size is not worth the hassle in terms of everyone having to
+ make sure that DIFF is GNU diff (IMHO).
+
+Sat Aug 19 22:05:46 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * recurse.c (start_recursion): Doc fix.
+
+ * server.c (do_cvs_command): Clear error_use_protocol in the
+ child.
+ (server): Set error_use_protocol.
+
+Sun Aug 13 15:33:37 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c (do_cvs_command): Don't select on exceptions.
+
+Fri Aug 4 00:13:47 1995 Jim Meyering (meyering@comco.com)
+
+ * Makefile.in (LDFLAGS): Set to @LDFLAGS@.
+ (options.h): Depend on ../config.status and options.h.in.
+ Add rule to build it from dependents.
+
+ * add.c: Include save-cwd.h.
+ (add_directory): Use save_cwd and restore_cwd instead of
+ explicit getwd then chdir.
+ * import.c (import_descend_dir): Likewise.
+ * modules.c (do_module): Likewise.
+
+ * recurse.c (save_cwd, restore_cwd, free_cwd): Remove functions.
+ New versions have been broken out into save-cwd.c.
+ (do_dir_proc): Adapt to handle status code returned by new versions
+ of save_cwd and restore_cwd -- and one fewer argument to restore_cwd.
+ (unroll_files_proc): Likewise.
+
+ * wrapper.c (wrap_name_has): Add default: abort () to switch
+ statement to avoid warning from gcc -Wall.
+ (wrap_matching_entry): Remove dcl of unused TEMP.
+ (wrap_tocvs_process_file): Remove dcl of unused ERR.
+ (wrap_fromcvs_process_file): Likewise.
+
+ * cvs.h: Remove prototype for error. Instead, include error.h.
+ Also, remove trailing white space.
+
+Thu Aug 3 10:12:20 1995 Jim Meyering (meyering@comco.com)
+
+ * import.c (import_descend_dir): Don't print probably-bogus CWD
+ in error messages saying `cannot get working directory'.
+
+Sun Jul 30 20:52:04 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * parseinfo.c (Parse_Info): Revise comments and indentation.
+
+Sun Jul 30 15:30:16 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * history.c: put ifdef SERVER_SUPPORT around tracing code incase
+ the client/server code is not compiled into the program.
+
+Sat Jul 29 16:59:49 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * subr.c (deep_remove_dir): Use struct dirent, not struct direct.
+
+Sat Jul 29 18:32:06 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * add.c: Check wrap_name_has.
+
+ * diff.c, checkin.c, import.c: have code call unlink_file_dir in
+ the appropriate places instead of just calling unlink_file.
+
+ * checkin.c: Remove one unlink call.
+
+ * import.c (comtable): Add .m .psw .pswm.
+
+ * import.c (add_rcs_file): Remove tocvsPath before returning.
+
+ * subr.c (unlink_file_dir): Add new function. unlinks the file if
+ it is a file. or will do a recursive delete if the path is
+ actually a directory.
+ (deep_remove_dir): New function, helps unlink_file_dir.
+
+ * mkmodules.c: Added CVSROOTADM_WRAPPER (cvswrappers file) to the
+ checkout file list.
+
+Fri Jul 28 16:27:56 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * checkout.c (safe_location): Use PATH_MAX not MAXPATHLEN.
+
+Fri Jul 28 19:37:03 1995 Paul Eggert <eggert@twinsun.com>
+
+ * log.c (cvslog, log_fileproc): Pass all options (except -l)
+ to rlog as-is, so that users can put spaces in options,
+ can specify multiple -d options, etc.
+ (ac, av): New variables.
+ (log_option_with_arg, options): Remove.
+
+ (log_fileproc): Don't prepend `/' to file name if update_dir is empty.
+
+Tue Jul 25 00:52:26 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * checkout.c (safe_location): Don't use PROTO in function definition.
+
+Mon Jul 24 18:32:06 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * checkout.c (safe_location): fix a compiler warning. (Declare
+ safe_location). Changed code in safe_location to call getwd
+ instead of getcwd. getwd is declared in the ../lib directory and
+ used exclusively thoughout the code. (this helps portability on
+ non POSIX systems).
+
+ * wrapper.c: updated Andrew Athan's email address.
+
+ * main.c: fix an ifdef so the code will compile. syntax error in
+ the ifdef for CVS_NOADMIN.
+
+Mon Jul 24 13:25:00 1995 Del <del@babel.dialix.oz.au>
+
+ * checkout.c: New procedure safe_location.
+ Ensures that you don't check out into the repository
+ itself.
+
+ * tag.c, rtag.c, cvs.h, mkmodules.c: Added a "taginfo" file in
+ CVSROOT to perform pre-tag checks.
+
+ * main.c, options.h.in: Added a compile time option to
+ disable the admin command.
+
+Fri Jul 21 17:07:42 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * update.c, status.c, patch.c, checkout.c, import.c, release.c,
+ rtag.c, tag.c: Now -q and -Q options just print an error message
+ telling you to use global -q and -Q options. The non-global
+ options were a mess because some commands accepted them and some
+ did not, and they were redundant with -q and -Q global options.
+
+ * rcs.c, cvs.h, commit.c, log.c, find_names.c: Remove CVS.dea
+ stuff. It is slower than the alternatives and I don't think
+ anyone ever actually used it.
+
+Fri Jul 21 10:35:10 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * Makefile.in (SOURCES, OBJECTS): Add wrapper.c, wrapper.o.
+ * add.c, admin.c, checkout.c, commit.c, diff.c, import.c, log.c,
+ remove.c, status.c: Call wrap_setup at start of commands.
+ * add.c (add): Check for wrapper, as well as directory, in repository.
+ * checkin.c: Add tocvsPath variable and associated handling.
+ * cvs.h: Add wrapper declarations.
+ * diff.c: Add tocvsPath variable and associated handling.
+ * import.c: Add -W option, CVSDOTWRAPPER handling.
+ (import_descend): check wrap_name_has.
+ (update_rcs_file, add_rev, add_rcs_file): add tocvsPath
+ variable and associated handling.
+ * no_diff.c: Add tocvsPath variable and associated handling.
+ * recurse.c (start_recursion): Check wrap_name_has.
+ * update.c: Copy, don't merge, copy-by-merge files. Attempt to
+ use -j on a copy-by-merge file generates a warning and no further
+ action.
+ * update.c: Add CVSDOTWRAPPER handling.
+ * wrapper.c: Added.
+
+Fri Jul 21 00:20:52 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c: Revert David Lamkin patch, except for the bits about
+ removing temp_filename and the .rej file.
+ * sanity.sh (errmsg1): Test for the underlying bug which Lamkin
+ kludged around.
+ * client.c (call_in_directory): Set short_pathname to include the
+ filename, not just the directory. Improve comments regarding what
+ is passed to FUNC.
+
+Thu Jul 20 17:51:54 1995 David Lamkin <drl@net-tel.co.uk>
+
+ * client.c (short_pathname): Fixes the fetching of the whole file
+ after a patch to bring it up to date has failed:
+ - failed_patches[] now holds short path to file that failed
+ - patch temp files are unlinked where the patch is done
+
+Thu Jul 20 12:37:10 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h: Declare error_set_cleanup
+ * main.c: Call it.
+ (error_cleanup): New function.
+
+Thu Jul 20 12:17:16 1995 Mark H. Wilkinson <mhw@minster.york.ac.uk>
+
+ * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c,
+ client.h, commit.c, create_adm.c, cvs.h, diff.c, entries.c,
+ history.c, import.c, log.c, main.c, modules.c, no_diff.c, patch.c,
+ release.c, remove.c, repos.c, rtag.c, server.c, server.h,
+ status.c, subr.c, tag.c, update.c, vers_ts.c, version.c: Put
+ client code inside #ifdef CLIENT_SUPPORT, server code inside
+ #ifdef SERVER_SUPPORT. When reporting version, report whether
+ client and/or server are compiled in.
+
+Wed Jul 19 18:00:00 1995 Jim Blandy <jimb@cyclic.com>
+
+ * subr.c (copy_file): Declare local var n to be an int,
+ not a size_t. size_t is unsigned, and the return values
+ of read and write are definitely not unsigned.
+
+ * cvs.h [HAVE_IO_H]: #include <io.h>.
+ [HAVE_DIRECT_H]: #include <direct.h>.
+
+Fri Jul 14 22:28:46 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * server.c (dirswitch, serve_static_directory, serve_sticky,
+ serve_lost, server_write_entries, serve_checkin_prog,
+ serve_update_prog): Include more information in error messages.
+ (Thanks, DJM.)
+
+ * cvsbug.sh: Use /usr/sbin/sendmail, unless it doesn't
+ exist, in which case use /usr/lib/sendmail. (Thanks, DJM.)
+
+ * server.c (server, server_cleanup): Use "/tmp" instead of
+ "/usr/tmp" when the TMPDIR environment variable isn't set. This
+ is what the rest of the code uses.
+
+Thu Jul 13 11:03:17 1995 Jim Meyering (meyering@comco.com)
+
+ * recurse.c (free_cwd): New function.
+ (save_cwd, restore_cwd): Use it instead of simply freeing any
+ string. The function also closes any open file descriptor.
+
+ * import.c (comtable): Now static.
+ (comtable): Put braces around each element of initializer.
+
+ * cvs.h: Add prototype for xgetwd.
+ * recurse.c (save_cwd, restore_cwd): New functions to encapsulate
+ run-time solution to secure-SunOS vs. fchown problem.
+ (do_dir_proc, unroll_files_proc): Use new functions instead of
+ open-coded fchdir/chdir calls with cpp directives.
+
+ * sanity.sh: Change out of TESTDIR before removing it.
+ Some versions of rm fail when asked to delete the current directory.
+
+Wed Jul 12 22:35:04 1995 Jim Meyering (meyering@comco.com)
+
+ * client.c (get_short_pathname): Add const qualifier to parameter dcl.
+ (copy_a_file): Remove set-but-not-used variable, LEN.
+ (handle_clear_static_directory): Likewise: SHORT_PATHNAME.
+ (set_sticky): Likewise: LEN.
+ (handle_set_sticky): Likewise: SHORT_PATHNAME.
+ (handle_clear_sticky): Likewise: SHORT_PATHNAME.
+ (start_rsh_server): Convert perl-style `cond || stmt' to more
+ conventional C-style `if (cond) stmt.' Sheesh.
+ Remove dcl of unused file-static, SEND_CONTENTS.
+
+ * history.c: Remove dcls of set-but-not-used file-statics,
+ HISTSIZE, HISTDATA.
+ (read_hrecs): Don't set them.
+
+ * import.c (add_rev): Remove dcl of set-but-not-used local, RETCODE.
+
+ * repos.c (Name_Repository): Remove dcl of set-but-not-used local,
+ HAS_CVSADM.
+
+ * cvsrc.c (read_cvsrc): Parenthesize assignment used as truth value.
+
+Tue Jul 11 16:49:41 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * hash.h (struct entnode, Entnode): moved from here...
+ * cvs.h: to here.
+
+Wed Jul 12 19:45:24 1995 Dominik Westner (dominik@gowest.ppp.informatik.uni-muenchen.de)
+
+ * client.c (server_user): new var.
+ (parse_cvsroot): set above if repo is "user@host:/dir".
+ (start_rsh_server): if server_user set, then use it.
+
+Wed Jul 12 10:53:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * sanity.sh: remove the TESTDIR after done.
+
+ * cvsbug.sh (GNATS_ADDR): now bug-cvs@prep.ai.mit.edu again.
+
+Tue Jul 11 15:53:08 1995 Greg A. Woods <woods@most.weird.com>
+
+ * options.h.in: depend on configure for grep and diff, now that
+ changes to configure.in are applied.
+
+Tue Jul 11 14:32:14 1995 Michael Shields <shields@tembel.org>
+
+ * Makefile.in (LDFLAGS): Pick up from configure.
+
+Tue Jul 11 14:20:00 1995 Loren James Rittle <rittle@supra.comm.mot.com>
+
+ * import.c (add_rev), commit.c (remove_file, ci_new_rev),
+ checkin.c (Checkin), subr.c (make_message_rcslegal), cvs.h:
+ Always perform sanity check and fix-up on messages to be passed
+ directly to RCS via the '-m' switch. RCS 5.7 requires that a
+ non-total-whitespace, non-null message be provided or it will
+ abort with an error. CVS is not setup to handle any returned
+ error from 'ci' gracefully and, thus, the repository entered a
+ trashed state.
+
+ * sanity.sh: Add regression tests for new code and interactions
+ with RCS 5.7.
+
+Sun Jul 9 19:03:00 1995 Greg A. Woods <woods@most.weird.com>
+
+ * .cvsignore: added new backup file
+
+ * options.h.in: our new configure.in finds the right diff and
+ grep paths now....
+
+ * subr.c: quote the string in run_print() for visibility
+ - indent a comment
+ - Jun Hamano's xchmod() patch to prevent writable files
+ (from previous local changes)
+
+ * logmsg.c: fix a NULL pointer de-reference
+ - clean up some string handling code...
+ (from previous local changes)
+
+ * parseinfo.c: add hack to expand $CVSROOT in an *info file.
+ - document "ALL" and "DEFAULT" in opening comment for Parse_Info()
+ - fix the code to match the comments w.r.t. callbacks for "ALL"
+ - add a line of trace output...
+ (from previous local changes)
+
+ * mkmodules.c: add support for comments in CVSROOT/checkoutlist
+ - add CVSroot used by something other .o, ala main.c
+ (from previous local changes)
+
+ * main.c, cvs.h: add support for $VISUAL as log msg editor
+ (from previous local changes)
+
+ * status.c: add support for -q and -Q (from previous local changes)
+
+
+Sun Jul 9 18:44:32 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * log.c: trivial change to test ChangeLog stuff.
+
+Sat Jul 8 20:33:57 1995 Paul Eggert <eggert@twinsun.com>
+
+ * history.c: (history_write): Don't assume that fopen(..., "a")
+ lets one interleave writes to the history file from different processes
+ without interlocking. Use open's O_APPEND option instead.
+ Throw in an lseek to lessen the race bugs on non-Posix hosts.
+ * cvs.h, subr.c (Fopen): Remove.
+
+ * log.c (log_fileproc): Pass working file name to rlog, so that
+ the name is reported correctly.
+
+Fri Jul 7 18:29:37 1995 Michael Hohmuth <hohmuth@inf.tu-dresden.de>
+
+ * client.c, client.h (client_import_setup): New function.
+ (client_import_done, client_process_import_file): Add comments
+ regarding now-redundant code.
+ * import.c (import): Call client_import_setup.
+
+Tue Jul 4 09:21:26 1995 Bernd Leibing <bernd.leibing@rz.uni-ulm.de>
+
+ * rcs.c (RCS_parsercsfile_i): Rename error to l_error; SunOS4 /bin/cc
+ doesn't like a label and function with the same name.
+
+Sun Jul 2 12:51:33 1995 Fred Appelman <Fred.Appelman@cv.ruu.nl>
+
+ * logmsg.c: Rename strlist to str_list to avoid conflict with
+ Unixware 2.01.
+
+Thu Jun 29 17:37:22 1995 Paul Eggert <eggert@twinsun.com>
+
+ * rcs.c (RCS_check_kflag): Allow RCS 5.7's new -kb option.
+
+Wed Jun 28 09:53:14 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (HEADERS): Remove options.h.in.
+ (DISTFILES): Add options.h.in.
+ Depend on options.h in addition to HEADERS.
+
+Tue Jun 27 22:37:28 1995 Vince Demarco <vdemarco@bou.shl.com>
+
+ * subr.c: Don't try to do fancy waitstatus stuff for NeXT,
+ lib/wait.h is sufficient.
+
+Mon Jun 26 15:17:45 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (DISTFILES): Remove RCS-patches and convert.sh.
+
+Fri Jun 23 13:38:28 1995 J.T. Conklin (jtc@rtl.cygnus.com)
+
+ * server.c (dirswitch, serve_co): Use CVSADM macro instead of
+ literal "CVS".
+
+Fri Jun 23 00:00:51 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * README-rm-add: Do not talk about patching RCS, that only
+ confuses people.
+ * RCS-patches, convert.sh: Removed (likewise).
+
+Thu Jun 22 10:41:41 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * subr.c: Change -1 to (size_t)-1 when comparing against a size_t.
+
+Wed Jun 21 16:51:54 1995 nk@ipgate.col.sw-ley.de (Norbert Kiesel)
+
+ * create_adm.c, entries.c, modules.c: Avoid coredumps if
+ timestamps, tags, etc., are NULL.
+
+Tue Jun 20 15:52:53 1995 Jim Meyering (meyering@comco.com)
+
+ * checkout.c (checkout): Remove dcl of unused variable.
+ * client.c (call_in_directory, handle_clear_static_directory,
+ handle_set_sticky, handle_clear_sticky, send_a_repository,
+ send_modified, send_dirent_proc): Remove dcls of unused variables.
+ * server.c (receive_file, serve_modified, server_cleanup):
+ Remove dcls of unused variables.
+ * subr.c (copy_file): Remove dcl of unused variable.
+ * vers_ts.c (time_stamp_server): Remove dcl of unused variable.
+
+Mon Jun 19 13:49:35 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * sanity.sh: Fix commencement message --- the test suite says
+ "Ok." when it's done.
+
+Fri Jun 16 11:23:44 1995 Jim Meyering (meyering@comco.com)
+
+ * entries.c (fgetentent): Parenthesize assignment in if-conditional.
+
+Thu Jun 15 17:33:28 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * server.c (get_buffer_data, buf_append_char, buf_append_data):
+ Don't conditionalize use of "inline". Autoconf takes care of
+ defining it away on systems that don't grok it.
+
+Thu Jun 15 13:43:38 1995 Jim Kingdon (kingdon@cyclic.com)
+
+ * options.h.in (DIFF): Default to "diff" not "diff -a" since diff
+ might not support the -a option.
+
+Wed Jun 14 11:29:42 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * import.c (import_descend): Initialize dirlist to NULL.
+
+ * subr.c (copy_file): Fix infinite loop.
+
+ * server.c (serve_directory): fix a memory leak.
+
+ * checkout.c, commit.c, diff.c, history.c, import.c, log.c,
+ patch.c, release.c, remove.c, rtag.c, status.c, tag.c, update.c:
+ Use send_arg() to send command line arguments to server.
+
+ * commit.c (fsortcmp), find_names (fsortcmp), hash.c (hashp,
+ findnode), hash.h (findnode), rcs.c (RCS_addnode,
+ RCS_check_kflag, RCS_check_tag, RCS_isdead, RCS_parse,
+ RCS_parsercsfile_i), rcs.h (RCS_addnode, RCS_check_kflag,
+ RCS_check_tag, RCS_parse): Added const qualifiers as
+ appropriate.
+ * rcs.h (RCS_isdead): Added prototype.
+
+ * hash.h (walklist, sortlist): correct function prototypes.
+
+ * ignore.c (ign_setup): don't bother checking to see if file
+ exists before calling ign_add_file.
+
+Fri Jun 9 11:24:06 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * all source files (rcsid): Added const qualifer.
+ * ignore.c (ign_default): Added const qualifier.
+ * subr.c (numdots): Added const qualifier to function argument.
+ * cvs.h (numdots): Added const qualifier to prototype argument.
+
+ * client.c (change_mode): Tied consecutive if statements testing
+ the same variable together with else if.
+
+ * import.c (import_descend): Build list of subdirectories when
+ reading directory, and then process the subdirectories in that
+ list. This change avoids I/O overhead of rereading directory
+ and reloading ignore list (.cvsignore) for each subdirectory.
+
+Thu Jun 8 11:54:24 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * import.c (import_descend): Use 4.4BSD d_type field if it is
+ present.
+
+ * lock.c (set_lockers_name): Use %lu in format and cast st_uid
+ field to unsigned long.
+
+ * import.c (import): Use RCS_check_kflag() to check -k options.
+ (keyword_usage, str2expmode, strn2expmode, expand_names):
+ Removed.
+ * rcs.c (RCS_check_kflag): Added keyword_usage array from import.c
+ for more descriptive error messages.
+
+ * subr.c (run_setup, run_args): Changed variable argument
+ processing to work on machines that use <varargs.h>.
+
+ * subr.c (copy_file, xcmp): Changed to read the file(s) by blocks
+ rather than by reading the whole file into a huge buffer. The
+ claim that this was reasonable because source files tend to be
+ small does not hold up in real world situations. CVS is used
+ to manage non-source files, and mallocs of 400K+ buffers (x2
+ for xcmp) can easily fail due to lack of available memory or
+ even memory pool fragmentation.
+ (block_read): New function, taken from GNU cmp and slightly
+ modified.
+
+ * subr.c (xcmp): Added const qualifier to function arguments.
+ * cvs.h (xcmp): Added const qualifer to prototype arguments.
+
+Wed Jun 7 11:28:31 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * cvs.h (Popen): Added prototype.
+ (Fopen, open_file, isreadable, iswritable, isdir, isfile,
+ islink, make_directory, make_directories, rename_file,
+ link_file, unlink_file, copy_file): Added const qualifer to
+ prototype arguments.
+ * subr.c (Fopen, Popen, open_file, isreadable, iswritable, isdir,
+ isfile, islink, make_directory, make_directories, rename_file,
+ link_file, unlink_file, copy_file): Added const qualifier to
+ function arguments.
+
+ * logmsg.c (logfile_write), recurse.c (do_recursion, addfile):
+ Don't cast void functions to a void expression. There is at
+ least one compiler (MPW) that balks at this.
+
+ * rcs.c (keysize, valsize): Change type to size_t.
+
+ * add.c (add_directory): Don't cast umask() argument to int.
+
+ * import.c (add_rcs_file): Changed type of mode to mode_t.
+
+ * rcscmds.c (RCS_merge): New function.
+ * cvs.h (RCS_merge): Declare.
+ * update.c (merge_file, join_file): Call RCS_merge instead of
+ invoking rcsmerge directly.
+
+ * cvs.h: Include <stdlib.h> if HAVE_STDC_HEADERS, otherwise
+ declared getenv().
+ * cvsrc.c, ignore.c, main.c: Removed getenv() declaration.
+
+ * client.c (mode_to_string): Changed to take mode_t instead of
+ struct statb argument. Simplified implementation, no longer
+ overallocates storage for returned mode string.
+ * client.h (mode_to_string): Updated declaration.
+ * server.c (server_updated): Updated for new calling conventions,
+ pass st_mode instead of pointer to struct statb.
+
+ * cvs.h (CONST): Removed definition, use of const qualifier is
+ determined by autoconf.
+ * history.c, modules.c, parseinfo.c: Use const instead of CONST.
+
+ * add.c, admin.c, checkout.c, commit.c, diff.c, import.c, log.c,
+ main.c, mkmodules.c, patch.c, recurse.c, remove.c, rtag.c,
+ server.c, status.c, subr.c, tag.c, update.c: Changed function
+ arguments "char *argv[]" to "char **argv" to silence lint
+ warnings about performing arithmetic on arrays.
+
+Tue Jun 6 18:57:21 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * version.c: Fix up version string, to say that this is Cyclic
+ CVS.
+
+Tue Jun 6 15:26:16 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * subr.c (run_setup, run_args, run_add_arg, xstrdup): Add const
+ qualifier to format argument.
+ * cvs.h (run_setup, run_args, xstrdup): Likewise.
+
+ * Makefile.in (SOURCES): Added rcscmds.c.
+ (OBJECTS): Added rcscmds.o.
+
+ * rcscmds.c: New file, with new functions RCS_settag, RCS_deltag,
+ RCS_setbranch, RCS_lock, RCS_unlock.
+ * checkin.c, commit.c, import.c, rtag.c, tag.c: Call above
+ functions instead of exec'ing rcs commands.
+ * cvs.h: Declare new functions.
+
+Mon May 29 21:40:54 1995 J.T. Conklin (jtc@rtl.cygnus.com)
+
+ * recurse.c (start_recursion, do_recursion): Set entries to NULL
+ after calling Entries_Close().
+
+Sat May 27 08:08:18 1995 Jim Meyering (meyering@comco.com)
+
+ * Makefile.in (check): Export RCSBIN only if there exists an
+ `rcs' executable in ../../rcs/src. Before, tests would fail when
+ the directory existed but contained no executables.
+ (distclean): Remove options.h, now that it's generated.
+ (Makefile): Regenerate only *this* file when Makefile.in is
+ out of date. Depend on ../config.status.
+
+Fri May 26 14:34:28 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * entries.c (Entries_Open): Added missing fclose().
+ (Entries_Close): Don't write Entries unless Entries.Log exists.
+
+ * entries.c (Entries_Open): Renamed from ParseEntries; changed to
+ process Entries Log files left over from previous crashes or
+ aborted runs.
+ (Entries_Close): New function, write out Entries file if
+ neccessary and erase Log file.
+ (Register): Append changed records to Log file instead of
+ re-writing file.
+ (fgetentent): New function, parse one Entry record from a file.
+ (AddEntryNode): It's no longer an error for two records with the
+ same name to be added to the list. New records replace older
+ ones.
+ * cvs.h (Entries_Open, Entries_Close): Add prototypes.
+ (CVSADM_ENTLOG): New constant, name of Entries Log file.
+ * add.c, checkout.c, client.c, find_names.c, recurse.c: Use
+ Entries_Open()/Entries_Close() instead of ParseEntries()/dellist().
+
+ * add.c, admin.c, checkout.c, client.c, commit.c, diff.c,
+ history.c, import.c, log.c, patch.c, release.c, remove.c,
+ rtag.c, server.c, status.c, tag.c, update.c: Changed
+ conditionals so that return value of *printf is tested less than
+ 0 instead of equal to EOF.
+
+Thu May 25 08:30:12 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * subr.c (xmalloc): Never try to malloc zero bytes; if the user
+ asks for zero bytes, malloc one instead.
+
+Wed May 24 12:44:25 1995 Ken Raeburn <raeburn@cujo.cygnus.com>
+
+ * subr.c (xmalloc): Don't complain about NULL if zero bytes were
+ requested.
+
+Tue May 16 21:49:05 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * subr.c (xmalloc): Never try to malloc zero bytes; if the user
+ asks for zero bytes, malloc one instead.
+
+Mon May 15 14:35:11 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * lock.c (L_LOCK_OWNED): Removed.
+
+ * add.c, checkout.c, client.c, create_adm.c, cvs.h, entries.c,
+ find_names.c modules.c, recurse.c, release.c, repos.c, update.c:
+ removed CVS 1.2 compatibility/upgrade code.
+
+Mon May 8 11:25:07 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * lock.c (write_lock): Missed one instance where rmdir(tmp) should
+ have been changed to clear_lock().
+
+Wed May 3 11:08:32 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * create_adm.c, entries.c, import.c, root.c: Changed conditionals
+ so that return value of *printf is tested less than 0 instead of
+ equal to EOF --- That's all Standard C requires.
+
+Wed May 3 18:03:37 1995 Samuel Tardieu <tardieu@emma.enst.fr>
+
+ * rcs.h: removed #ifdef CVS_PRIVATE and #endif because cvs didn't
+ compile anymore.
+
+Mon May 1 13:58:53 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * rcs.c, rcs.h: Implemented lazy parsing of rcs files.
+ RCS_parsercsfile_i modified to read only the first two records
+ of rcs files, a new function RCS_reparsercsfile is called only
+ when additional information (tags, revision numbers, dates,
+ etc.) is required.
+
+Mon May 1 12:20:02 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * Makefile.in (INCLUDES): Include -I. for options.h.
+
+Fri Apr 28 16:16:33 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * Makefile.in (SOURCES, HEADERS, DISTFILES): Updated.
+ (dist-dir): Renamed from dist; changed to work with DISTDIR
+ variable passed from parent.
+
+ We don't want to include a file the user has to edit in the
+ distribution.
+ * options.h: No longer distributed.
+ * options.h.in: Distribute this instead.
+ * ../INSTALL, ../README: Installation instructions updated.
+
+ * client.c (start_rsh_server): Send the remote command to rsh as a
+ single string.
+
+Fri Apr 28 00:29:49 1995 Noel Cragg <noel@vo.com>
+
+ * commit.c: Added initializer for FORCE_CI
+
+ * sanity.sh: Fix tests added 25 Apr -- they were expecting the
+ server to make noise, but the CVS_SERVER variable had been
+ accidentally set with the `-Q' flag. Ran all tests -- both
+ locally and remotely -- to verify that the change didn't break
+ anything.
+
+Thu Apr 27 12:41:52 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * Makefile.in: Revise comment regarding check vs. remotecheck.
+
+Thu Apr 27 12:52:28 1995 Bryan O'Sullivan <bos@cyclic.com>
+
+ * client.c (start_rsh_server): If the CVS_RSH environment variable
+ is set, use its contents as the name of the program to invoke
+ instead of `rsh'.
+
+Thu Apr 27 12:18:38 1995 Noel Cragg <noel@vo.com>
+
+ * checkout.c (checkout): To fix new bug created by Apr 23 change,
+ re-enabled "expand-module" functionality, because it has the side
+ effect of setting the checkin/update programs for a directory. To
+ solve the local/remote checkout problem that prompted this change
+ in the first place, I performed the next change.
+ * server.c (expand_proc): Now returns expansions for aliases only.
+
+Wed Apr 26 12:07:42 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * rcs.c (getrcskey): Rewritten to process runs of whitespace chars
+ and rcs @ strings instead of using state variables "white" and
+ "funky".
+
+Fri Apr 7 15:49:25 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * lock.c (unlock): Only call stat if we need to.
+
+Wed Apr 26 10:48:44 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (new_entries_line): Don't prototype.
+
+Tue Apr 25 22:19:16 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * sanity.sh: Add new tests to catch bugs in Apr 23 change.
+
+Tue Apr 25 17:10:55 1995 Roland McGrath <roland@baalperazim.frob.com>
+
+ * create_adm.c (Create_Admin): Use getwd instead of getcwd.
+
+Sun Apr 23 20:58:32 1995 Noel Cragg <noel@vo.com>
+
+ * checkout.c (checkout): Disabled "expand-module" functionality on
+ remote checkout, since it makes modules behave like aliases (see
+ longer note there). This change necessitated the change below.
+ Also merged the like parts of a conditional.
+
+ * client.c (call_in_directory): Changed the algorithm that created
+ nested and directories and the "CVS" administration directories
+ therein. The algoithm wrongly assumed that the name of the
+ directory that that was to be created and the repository name were
+ the same, which breaks modules.
+
+ * create_adm.c (Create_Admin), module.c (do_module), server.c
+ (server_register), subr.c, entries.c: Added fprintfs for trace-mode
+ debugging.
+
+ * client.c (client_send_expansions): Argument to function didn't
+ have a type -- added one.
+
+ * server.c (new_entries_line): Arguments to this function are
+ never used -- reoved them and fixed callers.
+
+Sat Apr 22 11:17:20 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * rcs.c (RCS_parse): If we can't open the file, give an error
+ message (except for ENOENT in case callers rely on that).
+
+Wed Apr 19 08:52:37 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (send_repository): Check for CVSADM_ENTSTAT in `dir', not
+ in `.'.
+
+ * sanity.sh: Add TODO list. Revise some comments. Add tests of
+ one working directory adding a file and other updating it.
+
+Sat Apr 8 14:52:55 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * Makefile.in (CFLAGS): Let configure set the default for CFLAGS.
+ Under GCC, we want -g -O.
+
+Fri Apr 7 15:49:25 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * root.c (Name_Root): merge identical adjacent conditionals.
+
+ * create_admin.c (Create_Admin): Rearranged check for CVSADM and
+ OCVSADM directories so that CVSADM pathname is only built once.
+
+ * update.c (update_dirleave_proc): Removed code to remove CVS
+ administration directory if command_name == "export" and to
+ create CVS/Root file if it is not present. Identical code
+ in update_filesdone_proc() will perform these same actions.
+ Also removed code that read and verfied CVS/Root. This is
+ expensive, and if it is necessary should happen in the
+ general recursion processor rather than in the update
+ callbacks.
+
+ * lock.c (masterlock): New variable, pathname of master lockdir.
+ (set_lock): removed lockdir argument, now constructs it itself
+ and stores it in masterlock.
+ (clear_lock): new function, removes master lockdir.
+ (Reader_Lock, write_lock): call clear_lock instead of removing
+ master lockdir.
+ (Reader_Lock, write_lock): #ifdef'd out CVSTFL code.
+
+ * main.c (main): register Lock_Cleanup signal handler.
+ * lock.c (Reader_Lock, write_lock): no longer register
+ Lock_Cleanup.
+
+ * main.c (main): initialize new array hostname.
+ * lock.c (Reader_Lock, write_lock): Use global hostname array.
+ * logmsg.c (logfile_write): Likewise.
+
+ * recurse.c (do_dir_proc, unroll_files_proc): Use open()/fchdir()
+ instead of getwd()/chdir() on systems that support the fchdir()
+ system call.
+
+Fri Apr 7 06:57:20 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c: Include the word "server" in error message for memory
+ exhausted, so the user knows which machine ran out of memory.
+
+ * sanity.sh: For remote, set CVS_SERVER to test the right server,
+ rather than a random one from the PATH.
+
+ * commit.c [DEATH_STATE]: Pass -f to `ci'.
+
+Thu Apr 6 13:05:15 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * commit.c (checkaddfile): If we didn't manage to fopen the file,
+ don't try to fclose it.
+
+ * client.c (handle_m, handle_e): Use fwrite, rather than a loop of
+ putc's. Sometimes these streams are unbuffered.
+
+Tue Apr 4 11:33:56 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * (DISTFILES): Include cvsbug.sh, ChangeLog, NOTES, RCS-patches,
+ README-rm-add, ChangeLog.fsf, sanity.sh, sanity.el, and
+ .cvsignore.
+
+Mon Mar 27 08:58:42 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * rcs.c (RCS_parsercsfile_i): Accept `dead' state regardless of
+ DEATH_STATE define. Revise comments regarding DEATH_STATE versus
+ CVSDEA versus the scheme which uses a patched RCS.
+ * README-rm-add, RCS-patches: Explain what versions of CVS need
+ RCS patches.
+
+Sat Mar 25 18:51:39 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * server.c (server_cleanup): Only do the abysmal kludge of waiting
+ for command and draining the pipe #ifdef sun. The code makes
+ assumptions not valid on all systems, and is only there to
+ workaround a SunOS bug.
+
+Wed Mar 22 21:55:56 1995 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (mkdir_p): Call stat only if we get the EACCES. Faster
+ and more elegant.
+
+Tue Jan 31 20:59:19 1995 Ken Raeburn <raeburn@cujo.cygnus.com>
+
+ * server.c: Try to avoid starting the "rm -rf" at cleanup time
+ until after subprocesses have finished.
+ (command_fds_to_drain, max_command_fd): New variables.
+ (do_cvs_command): Set them.
+ (command_pid_is_dead): New variable.
+ (wait_sig): New function.
+ (server_cleanup): If command_pid is nonzero, wait for it to die,
+ draining output from it in the meantime. If nonzero SIG was
+ passed, send a signal to the subprocess, to encourage it to die
+ soon.
+
+ * main.c (usage): Argument is now `const char *const *'.
+ * cvs.h (usage): Changed prototype.
+ (USE): Make new variable `const'.
+ * add.c (add_usage), admin.c (admin_usage), checkout.c
+ (checkout_usage, export_usage, checkout), commit.c (commit_usage),
+ diff.c (diff_usage), history.c (history_usg), import.c
+ (import_usage, keyword_usage), log.c (log_usage), main.c (usg),
+ patch.c (patch_usage), release.c (release_usage), remove.c
+ (remove_usage), rtag.c (rtag_usage), server.c (server), status.c
+ (status_usage), tag.c (tag_usage), update.c (update_usage): Usage
+ messages are now const arrays of pointers to const char.
+
+ * import.c (comtable): Now const.
+ * main.c (rcsid): Now static.
+ (cmd): Now const.
+ (main): Local variable CM now points to const.
+ * server.c (outbuf_memory_error): Local var MSG now const.
+
+ * client.c (client_commit_usage): Deleted.
+
+Sat Dec 31 15:51:55 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * logmsg.c (do_editor): Allocate enough space for trailing '\0'.
+
+Fri Mar 3 11:59:49 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * cvsbug.sh: Call it "Cyclic CVS" now, not "Remote CVS". Call it
+ version C1.4A, not 1.4A2-remote. Send bugs to cyclic-cvs, not
+ remote-cvs.
+
+ * classify.c (Classify_File): Put check for dead file inside
+ "#ifdef DEATH_SUPPORT".
+
+Thu Feb 23 23:03:43 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * update.c (join_file): Don't pass the -E option to rcsmerge here,
+ either (see Jan 22 change).
+
+Mon Feb 13 13:28:46 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * cvsbug.sh: Send bug reports to remote-cvs@cyclic.com, rather
+ than to the ordinary CVS bug address. This does mean we'll have
+ to wade through GNATS-style bug reports, sigh.
+
+Wed Feb 8 06:42:27 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * server.c: Don't include <sys/stat.h>; system.h already does, and
+ 4.3BSD can't take it twice.
+
+ * subr.c [! HAVE_VPRINTF] (run_setup, run_args): Don't use va_dcl
+ in declaration. Declare the a1..a8 args which are used in the
+ sprintf call.
+ * cvs.h [! HAVE_VPRINTF] (run_setup, run_args): Don't prototype
+ args, to avoid conflicting with the function definitions
+ themselves.
+
+Tue Feb 7 20:10:00 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * client.c (update_entries): Pass the patch subprocess the switch
+ "-b ~", not "-b~"; the latter form seems not to work with patch
+ version 2.0 and earlier --- it takes the next argv element as the
+ backup suffix, and thus doesn't notice that the patch file's name
+ has been specified, thus doesn't find the patch, thus... *aargh*
+
+Fri Feb 3 20:28:21 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * log.c (log_option_with_arg): New function.
+ (cvslog): Use it and send_arg to handle the rlog options that take
+ arguments. The code used to use send_option_string for
+ everything, which assumes that "-d1995/01/02" is equivalent to
+ "-d -1 -9 -9 -5 ...".
+
+Tue Jan 31 15:02:01 1995 Jim Blandy <jimb@floss.life.uiuc.edu>
+
+ * server.c: #include <sys/stat.h> for the new stat call in mkdir_p.
+ (mkdir_p): Don't try to create the intermediate directory if it
+ exists already. Some systems return EEXIST, but others return
+ EACCES, which we can't otherwise distinguish from a real access
+ problem.
+
+Sun Jan 22 15:25:45 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * update.c (merge_file): My rcsmerge doesn't accept a -E option,
+ and it doesn't look too important, so don't pass it.
+
+Fri Jan 20 14:24:58 1995 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * client.c (do_deferred_progs): Don't try to chdir to toplevel_wd
+ if it has not been set.
+ (process_prune_candidates): Likewise.
+
+Mon Nov 28 09:59:14 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (client_commit): Move guts of function from here...
+ * commit.c (commit): ...to here.
+
+Mon Nov 28 15:14:36 1994 Ken Raeburn <raeburn@cujo.cygnus.com>
+
+ * server.c (buf_input_data, buf_send_output): Start cpp directives
+ in column 1, otherwise Sun 4 pcc complains.
+
+Mon Nov 28 09:59:14 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (add_prune_candidate): Don't try to prune ".".
+
+Tue Nov 22 05:27:10 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c, client.c: More formatting cleanups.
+
+ * client.h, client.c: New variable client_prune_dirs.
+ * update.c (update), checkout.c (checkout): Set it.
+ * client.c (add_prune_candidate, process_prune_candidates): New
+ functions.
+ (send_repository, call_in_directory, get_responses_and_close):
+ Call them.
+
+Wed Nov 23 01:17:32 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * server.c (do_cvs_command): Don't select on STDOUT_FILENO unless
+ we have something to write.
+
+Tue Nov 22 05:27:10 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * remove.c (remove_fileproc): Only call server_checked_in if we
+ actually are changing the entries file.
+
+ * server.c (server_write_entries): New function.
+ (dirswitch, do_cvs_command): Call it.
+ (serve_entry, serve_updated): Just update in-memory data
+ structures, don't mess with CVS/Entries file.
+
+Mon Nov 21 10:15:11 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (server_checked_in): Set scratched_file to NULL after
+ using it.
+
+ * checkin.c (Checkin): If the file was changed by the checkin,
+ call server_updated not server_checked_in.
+
+Sun Nov 20 08:01:51 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (send_repository): Move check for update_dir NULL to
+ before where we check last_update_dir. Check for "" here too.
+
+ * client.c (send_repository): Use new argument dir.
+
+ * client.c: Pass new argument dir to send_repository and
+ send_a_repository.
+
+ * server.c, server.h (server_prog): New function.
+ * modules.c (do_modules): Call it if server_expanding.
+ * client.c: Support Set-checkin-prog and Set-update-prog responses.
+ * server.c, client.c: Add Checkin-prog and Update-prog requests.
+
+Fri Nov 18 14:04:38 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (get_short_pathname, is_cvsroot_level,
+ call_in_directory): Base whether this is new-style or
+ old-style based on whether we actually used the Directory request,
+ not based on whether the pathname is absolute. Rename
+ directory_supported to use_directory.
+ * server.c: Rename use_relative_pathnames to use_dir_and_repos.
+ * client.c (send_a_repository): If update_dir is absolute, don't
+ use it to try to reconstruct how far we have recursed.
+
+ * server.c, server.h, client.c, client.h, vers_ts.c, update.h:
+ More cosmetic changes (identation, PARAMS vs. PROTO, eliminate
+ alloca, etc.) to remote CVS to make it more like the rest of CVS.
+
+ * server.c: Make server_temp_dir just the dir name, not the name
+ with "%s" at the end.
+ * server.c, client.c: Add "Max-dotdot" request, and use it to make
+ extra directories in server_temp_dir if needed.
+
+Thu Nov 17 09:03:28 1994 Jim Kingdon <kingdon@cygnus.com>
+
+ * client.c: Fix two cases where NULL was used and 0 was meant.
+
+Mon Nov 14 08:48:41 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (serve_unchanged): Set noexec to 0 when calling Register.
+
+ * update.c (merge_file): Don't call xcmp if noexec.
+
+Fri Nov 11 13:58:22 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (call_in_directory): Deal with it if reposdirname is
+ not a subdirectory of toplevel_repos.
+
+Mon Nov 7 09:12:01 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * patch.c: If file is removed and we don't have a tag or date,
+ just print "current release".
+
+ * classify.c (Classify_File): Treat dead files appropriately.
+
+Fri Nov 4 07:33:03 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * main.c (main) [SERVER_SUPPORT]: Move call to getwd past where we
+ know whether we are the server or not. Set CurDir to "<remote>"
+ if we are the server.
+
+ * client.c: Remove #if 0'd function option_with_arg.
+ Remove #if 0'd code pertaining to the old way of logging the
+ session.
+
+ * client.c (start_rsh_server): Don't invoke the server with the
+ -d option.
+ * server.c (serve_root): Test root for validity, just like main.c
+ does for non-remote CVS.
+ * main.c (main): If `cvs server' happens with a colon in the
+ CVSroot, just handle it normally; don't make it an error.
+
+Wed Nov 2 11:09:38 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (send_dirent_proc): If dir does not exist, just return
+ R_SKIP_ALL.
+
+ * server.c, client.c: Add Directory request and support for
+ local relative pathnames (along with the repository absolute
+ pathnames).
+ * update.c, add.c, checkout.c, checkin.c, cvs.h, create_adm.c,
+ commit.c, modules.c, server.c, server.h, remove.c, client.h:
+ Pass update_dir to server_* functions. Include update_dir in
+ more error messages.
+
+Fri Oct 28 08:54:00 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c: Reformat to bring closer to cvs standards for brace
+ position, comment formatting, etc.
+
+ * sanity.sh: Remove wrong "last mod" line. Convert more tests to
+ put PASS or FAIL in log file. Change it so arguments to the
+ script specify which tests to run.
+
+ * client.c, client.h, server.c, checkout.c: Expand modules in
+ separate step from the checkout itself.
+
+Sat Oct 22 20:33:35 1994 Ken Raeburn (raeburn@kr-pc.cygnus.com)
+
+ * update.c (join_file): When checking for null return from
+ RCS_getversion, still do return even if quiet flag is set.
+
+Thu Oct 13 07:36:11 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (send_files): Call send_repository even if
+ toplevel_repos was NULL.
+
+ * server.c (server_updated): If joining, don't remove file.
+
+ * update.c (join_file): If server and file is unmodified, check it
+ out before joining. After joining, call server_updated. New
+ argument repository.
+
+ * server.c, server.h (server_copy_file): New function.
+ * update.c (update_file_proc, join_file): Call it.
+ * client.c (copy_file, handle_copy_file): New functions.
+ * client.c (responses): Add "Copy-file".
+
+ * client.c, client.h: Make toplevel_wd, failed_patches and
+ failed_patches_count extern.
+ * client.c (client_update): Move guts of function from here...
+ * update.c (update): ...to here.
+
+ * client.c, checkout.c: Likewise for checkout.
+
+ * client.c (is_cvsroot_level): New function.
+ (handle_set_sticky, handle_clear_sticky,
+ handle_clear_static_directory): Call it, instead of checking
+ short_pathname for a slash.
+
+ * client.c, client.h (client_process_import_file,
+ client_import_done): New functions.
+ * import.c (import, import_descend): Use them.
+ * import.c (import_descend): If server, don't mention ignored CVS
+ directories.
+ * import.c (import_descend_dir): If client, don't print warm
+ fuzzies, or make directories in repository. If server, print warm
+ fuzzies to stdout not stderr.
+ * client.c (send_modified): New function, broken out from
+ send_fileproc.
+ (send_fileproc): Call it.
+
+ * client.c (handle_clear_sticky, handle_set_sticky,
+ handle_clear_static_directory, handle_set_static_directory): If
+ command is export, just return.
+ (call_in_directory, update_entries): If command is export, don't
+ create CVS directories, CVS/Entries files, etc.
+ * update.c (update_filesdone_proc): Don't remove CVS directories if
+ client_active.
+
+ * client.c (send_a_repository): Instead of insisting that
+ repository end with update_dir, just strip as many pathname
+ components from the end as there are in update_dir.
+
+ * Makefile.in (remotecheck): New target, pass -r to sanity.sh.
+ * sanity.sh: Accept -r argument which means to test remote cvs.
+
+ * tag.c (tag), rtag.c (rtag), patch.c (patch), import.c (import),
+ admin.c (admin), release.c (release): If client_active, connect to
+ the server and send the right requests.
+ * main.c (cmds): Add these commands.
+ (main): Remove code which would strip hostname off cvsroot and try
+ the command locally. There are no longer any commands which are
+ not supported.
+ * client.c, client.h (client_rdiff, client_tag, client_rtag,
+ client_import, client_admin, client_export, client_history,
+ client_release): New functions.
+ * server.c (serve_rdiff, serve_tag, serve_rtag, serve_import,
+ serve_admin, serve_export, serve_history, serve_release): New
+ functions.
+ (requests): List them.
+ * server.c: Declare cvs commands (add, admin, etc.).
+ * cvs.h, server.h: Don't declare any of them here.
+ * main.c: Restore declarations of cvs commands which were
+ previously removed.
+
+ * cvs.h: New define DEATH_STATE, commented out for now.
+ * rcs.c (RCS_parsercsfile_i), commit.c (remove_file, checkaddfile)
+ [DEATH_STATE]: Use RCS state to record a dead file.
+
+Mon Oct 3 09:44:54 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * status.c (status_fileproc): Now that ts_rcs is just one time,
+ don't try to print the second time from it. (Same as raeburn 20
+ Aug change, it accidentally got lost in 1.4 Alpha-1 merge).
+
+ * cvs.h (CVSDEA): Added (but commented out for now).
+ * rcs.c (RCS_parsercsfile_i) [CVSDEA]: Also look in CVSDEA to see if
+ something is dead.
+ * commit.c (ci_new_rev, mark_file) [CVSDEA]: New functions.
+ (remove_file, checkaddfile) [CVSDEA]: Use them instead of ci -K.
+ * find_names.c (find_dirs) [CVSDEA]: Don't match CVSDEA directories.
+ * update.c (checkout_file): Check RCS_isdead rather than relying
+ on co to not create the file.
+
+ * sanity.sh: Direct output to logfile, not /dev/null.
+
+ * subr.c (run_exec): Print error message if we are unable to exec.
+
+ * commit.c (remove_file): Call Scratch_Entry when removing tag
+ from file. The DEATH_SUPPORT ifdef was erroneous.
+
+Sun Oct 2 20:33:27 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * commit.c (checkaddfile): Instead of calling isdir before
+ attempting to create the directory, just ignore EEXIST errors from
+ mkdir. (This removes some DEATH_SUPPORT ifdefs which actually had
+ nothing to do with death support).
+
+Thu Sep 29 09:23:57 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * diff.c (diff): Search attic too if we have a second tag/date.
+ (diff_fileproc): If we have a second tag/date, don't do all the
+ checking regarding the user file.
+
+Mon Sep 26 12:02:15 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * checkin.c (Checkin): Check for error from unlink_file.
+
+Mon Sep 26 08:51:10 1994 Anthony J. Lill (ajlill@ajlc.waterloo.on.ca)
+
+ * rcs.c (getrcskey): Allocate space for terminating '\0' if
+ necessary.
+
+Sat Sep 24 09:07:37 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * commit.c (commit_fileproc): Set got_message = 1 when calling
+ do_editor (accidentally omitted from last change).
+
+Fri Sep 23 11:59:25 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ Revert buggy parts of Rich's change of 1 Nov 1993 (keeping the
+ dynamic buffer allocation, which was the point of that change).
+ * logmsg.c (do_editor): Reinstate message arg, but make it char
+ **messagep instead of char *message. Change occurances of message
+ to *messagep. Char return type from char * back to void.
+ * cvs.h: Change do_editor declaration.
+ * commit.c: Reinstate got_message variable
+ (commit_filesdoneproc, commit_fileproc, commit_direntproc): Use it.
+ * import.c (import), commit.c (commit_fileproc,
+ commit_direntproc): Pass &message to do_editor; don't expect it to
+ return a value.
+ * client.c (client_commit): Likewise.
+ * import.c (import): Deal with it if message is NULL.
+
+Wed Sep 21 09:43:25 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (server_updated): If the file doesn't exist, skip it.
+
+ * diff.c, client.h, client.c: Rename diff_client_senddate to
+ client_senddate and move from diff.c to client.c.
+ * client.c (client_update, client_checkout): Use it.
+
+Sat Sep 17 08:36:58 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * checkout.c (checkout_proc): Don't pass NULL to Register for
+ version. (should fix "cvs co -r <nonexistent-tag> <file>"
+ coredump on Solaris).
+
+Fri Sep 16 08:38:02 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * diff.c (diff_fileproc): Set top_rev from vn_user, not vn_rcs.
+ Rename it to user_file_rev because it need not be the head of any
+ branch.
+ (diff_file_nodiff): After checking user_file_rev, if we have both
+ use_rev1 and use_rev2, compare them instead of going on to code
+ which assumes use_rev2 == NULL.
+
+Thu Sep 15 08:20:23 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * status.c (status): Return a value in client_active case.
+
+Thu Sep 15 15:02:12 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * server.c (serve_modified): Create the file even if the size is
+ zero.
+
+Thu Sep 15 08:20:23 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * lock.c (readers_exist): Clear errno each time around the loop,
+ not just the first time.
+
+ * client.c (start_server): Don't send Global_option -q twice.
+
+ * no_diff.c (No_Difference): Check for error from unlink.
+
+ * no_diff.c, cvs.h (No_Difference): New args repository,
+ update_dir. Call server_update_entries if needed. Use update_dir
+ in error message.
+ * classify.c (Classify_File): Pass new args to No_Difference.
+
+ * server.c (server_update_entries, server_checked_in,
+ server_updated): Don't do anything if noexec.
+
+ * client.c (send_fileproc): Rather than guessing how big the gzip
+ output may be, just realloc the buffer as needed.
+
+Tue Sep 13 13:22:03 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * lock.c: Check for errors from unlink, readdir, and closedir.
+
+ * classify.c (Classify_File): Pass repository and update_dir to
+ sticky_ck.
+ (sticky_ck): New args repository and update_dir.
+ * server.c, server.h (server_update_entries): New function.
+ * classify.c (sticky_ck): Call it.
+ * client.c: New response "New-entry".
+ * client.c (send_fileproc): Send tag/date from vers->entdata, not
+ from vers itself.
+
+Mon Sep 12 07:07:05 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c: Clean up formatting ("= (errno)" -> "= errno").
+
+ * cvs.h: Declare strerror.
+
+ * client.c: Add code to deal with Set-sticky and Clear-sticky
+ responses, and Sticky request.
+ * server.c: Add code to deal with Sticky request.
+ * server.c, server.h (server_set_sticky): New function.
+ * create_adm.c (Create_Admin), update.c (update, update_dirent_proc),
+ commit.c (commit_dirleaveproc): Call it.
+ * client.c, client.h (send_files): Add parameter aflag.
+ * add.c (add), diff.c (diff), log.c (cvslog), remove.c (cvsremove),
+ status.c (status),
+ client.c (client_commit, client_update, client_checkout): Pass it.
+ * client.c (client_update): Add -A flag.
+
+Fri Sep 9 07:05:35 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * entries.c (WriteTag): Check for error from unlink_file.
+
+ * server.c (server_updated): Initialize size to 0. Previously if
+ the file was zero length, the variable size got used without being
+ set.
+
+Thu Sep 8 14:23:05 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (serve_repository): Check for error from fopen on
+ CVSADM_ENT.
+
+ * update.c (update, update_dirent_proc): Check for errors when
+ removing Entries.Static.
+
+ * client.c: Add code to deal with Set-static-directory and
+ Clear-static-directory responses, and Static-directory request.
+ * server.c, server.h (server_clear_entstat, server_set_entstat):
+ New functions.
+ * update.c, checkout.c, modules.c: Call them.
+ * server.c: Add code to deal with Static-directory request.
+
+ * server.c, client.c: Use strchr and strrchr instead of index and
+ rindex.
+
+ * server.c (serve_unchanged, serve_lost): Change comments which
+ referred to changing timestamp; we don't always change the
+ timestamp in those cases anymore.
+
+Wed Sep 7 10:58:12 1994 J.T. Conklin (jtc@rtl.cygnus.com)
+
+ * cvsrc.c (read_cvsrc): Don't call getenv() three times when one
+ time will do.
+
+ * subr.c (xmalloc, xrealloc): Change type of bytes argument from
+ int to size_t and remove the test that checks if it is less than
+ zero.
+ * cvs.h (xmalloc, xrealloc): Update prototype.
+
+Thu Sep 1 12:22:20 1994 Jim Kingdon (kingdon@cygnus.com)
+
+ * update.c (merge_file, join_file): Pass -E to rcsmerge.
+ (merge_file): If rcsmerge doesn't change the file, say so.
+
+ * recurse.c, cvs.h (start_recursion): New argument wd_is_repos.
+ * recurse.c (start_recursion): Use it instead of checking whether
+ command_name is rtag to find out if we are cd'd to the repository.
+ * client.c, update.c, commit.c, status.c, diff.c, log.c, admin.c,
+ remove.c, tag.c: Pass 0 for wd_is_repos.
+ * rtag.c, patch.c: Pass 1 for wd_is_repos.
+
+ * classify.c, cvs.h (Classify_File): New argument pipeout.
+ * classify.c (Classify_File): If pipeout, don't complain if the
+ file is already there.
+ * update.c, commit.c, status.c: Change callers.
+
+ * mkmodules.c (main): Don't print "reminders" if commitinfo,
+ loginfo, rcsinfo, or editinfo files are missing.
+
+Mon Aug 22 23:22:59 1994 Ken Raeburn (raeburn@kr-pc.cygnus.com)
+
+ * server.c (strerror): Static definition replaced by extern
+ declaration.
+
+Sun Aug 21 07:16:27 1994 Ken Raeburn (raeburn@kr-pc.cygnus.com)
+
+ * client.c (update_entries): Run "patch" with input from
+ /dev/null, so if it's the wrong version, it fails quickly rather
+ than waiting for EOF from terminal before failing.
+
+Sat Aug 20 04:16:33 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * server.c (serve_unchanged): Instead of creating a file with a
+ zero timestamp, rewrite the entries file to have "=" in the
+ timestamp field.
+ * vers_ts.c (mark_lost, mark_unchanged): New macros.
+ (time_stamp_server): Use them, for clarity. Interpret "="
+ timestamp as an unchanged file. A zero-timestamp file should
+ never be encountered now in use_unchanged mode.
+
+ * client.c (start_server): If CVS_CLIENT_PORT indicates a
+ non-positive port number, skip straight to rsh connection.
+
+ * status.c (status_fileproc): Fix ts_rcs reference when printing
+ version info, to correspond to new Entries file format. Don't
+ print it at all if server_active, because it won't have any useful
+ data.
+
+Thu Aug 18 14:38:21 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * cvs.h (status): Declare.
+ * client.c (client_status): New function.
+
+ * client.h (client_status): Declare.
+ * main.c (cmds): Include it.
+ * server.c (serve_status): New function.
+ (requests): Add it.
+ * status.c (status): Do the remote thing if client_active.
+
+ * client.c (supported_request): New function.
+ (start_server): Use it.
+
+ * server.c (receive_partial_file): New function, broken out from
+ serve_modified. Operate with fixed-size local buffer, instead of
+ growing stack frame by entire file size.
+ (receive_file): New function, broken out from serve_modified.
+ (serve_modified): Call it.
+ (server): Print out name of unrecognized request.
+
+ More generic stream-filtering support:
+ * client.c (close_on_exec, filter_stream_through_program): New
+ functions.
+ (server_fd): New variable.
+ (get_responses_and_close): Direct non-rsh connection is now
+ indicated by server_fd being non-negative. File descriptors for
+ to_server and from_server may now be different in case "tee"
+ filtering is being done. Wait for rsh_pid specifically.
+ (start_server): Use filter_stream_through_program for "tee"
+ filter, and enable it for direct Kerberos-authenticated
+ connections. Use dup to create new file descriptors for server
+ connection if logging is enabled.
+ (start_rsh_server): Disable code that deals with logging.
+
+ Per-file compression support:
+ * cvs.h (gzip_level): Declare.
+ * main.c (usg): Describe new -z argument.
+ (main): Recognize it and set gzip_level.
+ * client.c (filter_through_gzip, filter_through_gunzip): New
+ functions to handle compression.
+ (update_entries): If size starts with "z", uncompress
+ (start_server): If gzip_level is non-zero and server supports it,
+ issue gzip-file-contents request.
+ (send_fileproc): Optionally compress file contents. Use a
+ slightly larger buffer, anticipating the worst case.
+ * server.c (gzip_level): Define here.
+ (receive_file): Uncompress file contents if needed.
+ (serve_modified): Recognize "z" in file size and pass receive_file
+ appropriate flag.
+ (buf_read_file_to_eof, buf_chain_length): New functions.
+ (server_updated): Call them when sending a compressed file.
+ (serve_gzip_contents): New function; set gzip_level.
+ (requests): Added gzip-file-contents request.
+
+Wed Aug 17 09:37:44 1994 J.T. Conklin (jtc@cygnus.com)
+
+ * find_names.c (find_dirs): Use 4.4BSD filesystem feature (it
+ contains the file type in the dirent structure) to avoid
+ stat'ing each file.
+
+ * commit.c (remove_file,checkaddfile): Change type of umask
+ variables from int to mode_t.
+ * subr.c (): Likewise.
+
+Tue Aug 16 19:56:34 1994 Mark Eichin (eichin@cygnus.com)
+
+ * diff.c (diff_fileproc): Don't use diff_rev* because they're
+ invariant across calls -- add new variable top_rev.
+ (diff_file_nodiff): After checking possible use_rev* values, if
+ top_rev is set drop it in as well (if we don't already have two
+ versions) and then clear it for next time around.
+
+Wed Aug 10 20:50:47 1994 Mark Eichin (eichin@cygnus.com)
+
+ * diff.c (diff_fileproc): if ts_user and ts_rcs match, then the
+ file is at the top of the tree -- so we might not even have a
+ copy. Put the revision into diff_rev1 or diff_rev2.
+
+Wed Aug 10 14:55:38 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * server.c (do_cvs_command): Use waitpid.
+
+ * subr.c (run_exec): Always use waitpid.
+
+ * Makefile.in (CC, LIBS): Define here, in case "make" is run in
+ this directory instead of top level.
+
+Wed Aug 10 13:57:06 1994 Mark Eichin (eichin@cygnus.com)
+
+ * client.c (krb_get_err_text): use HAVE_KRB_GET_ERR_TEXT to
+ determine if we need to use the array or the function.
+ * main.c: ditto.
+
+Tue Aug 9 16:43:30 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * entries.c (ParseEntries): If timestamp is in old format, rebuild
+ it in the new format. Fudge an unmatchable entry that won't
+ trigger this code next time around, if the file is modified.
+
+ * vers_ts.c (time_stamp): Only put st_mtime field into timestamp,
+ and use GMT time for it. With st_ctime or in local time, copying
+ trees between machines in different time zones makes all the files
+ look modified.
+ (time_stamp_server): Likewise.
+
+Tue Aug 9 19:40:51 1994 Mark Eichin (eichin@cygnus.com)
+
+ * main.c (main): use krb_get_err_text function instead of
+ krb_err_txt array.
+
+Thu Aug 4 15:37:50 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * main.c (main): When invoked as kserver, set LOGNAME and USER
+ environment variables to the remote user name.
+
+Thu Aug 4 07:44:37 1994 Mark Eichin (eichin@cygnus.com)
+
+ * client.c: (handle_valid_requests): if we get an option that has
+ rq_enableme set, then send that option. If it is UseUnchanged, set
+ use_unchanged so that the rest of the client knows about
+ it. (Could become a more general method for dealing with protocol
+ upgrades.)
+ (send_fileproc): if use_unchanged didn't get set, send an
+ old-style "Lost" request, otherwise send an "Unchanged" request.
+ * server.c (serve_unchanged): new function, same as serve_lost,
+ but used in the opposite case.
+ (requests): add new UseUnchanged and Unchanged requests, and make
+ "Lost" optional (there isn't a good way to interlock these.)
+ * server.h (request.status): rq_enableme, new value for detecting
+ compatibility changes.
+ * vers_ts.c (time_stamp_server): swap meaning of zero timestamp if
+ use_unchanged is set.
+
+Tue Jul 26 10:19:30 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * sanity.sh: Separate CVSROOT_FILENAME, which must be the filename
+ of the root, from CVSROOT, which can include a hostname for
+ testing remote CVS. (but the tests aren't yet prepared to deal
+ with the bugs in remote CVS).
+
+ * import.c (update_rcs_file): Change temporary file name in TMPDIR
+ from FILE_HOLDER to cvs-imp<process-id>.
+
+ * sanity.sh: Add ">/dev/null" and "2>/dev/null" many places to
+ suppress spurious output. Comment out tests which don't work (cvs
+ add on top-level directory, cvs diff when non-committed adds or
+ removes have been made, cvs release, test 53 (already commented as
+ broken), retagging without deleting old tag, test 63). Now 'make
+ check' runs without any failures.
+
+Fri Jul 15 12:58:29 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * Makefile.in (install): Do not depend upon installdirs.
+
+Thu Jul 14 15:49:42 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c, server.c: Don't try to handle alloca here; it's
+ handled by cvs.h.
+
+Tue Jul 12 13:32:40 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (update_entries): Reset stored_checksum_valid if we
+ quit early because of a patch failure.
+
+Fri Jul 8 11:13:05 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (responses): Mark "Remove-entry" as optional.
+
+Thu Jul 7 14:07:58 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * server.c (server_updated): Add new checksum argument. If it is
+ not NULL, and the client supports the "Checksum" response, send
+ it.
+ * server.h (server_updated): Update prototype.
+ * update.c: Include md5.h.
+ (update_file_proc): Pass new arguments to patch_file and
+ server_updated.
+ (patch_file): Add new checksum argument. Set it to the MD5
+ checksum of the version of the file being checked out.
+ (merge_file): Pass new argument to server_updated.
+ * client.c: Include md5.h.
+ (stored_checksum_valid, stored_checksum): New static variables.
+ (handle_checksum): New static function.
+ (update_entries): If a checksum was received, check it against the
+ MD5 checksum of the final file.
+ (responses): Add "Checksum".
+ (start_server): Clear stored_checksum_valid.
+ * commit.c (commit_fileproc): Pass new argument to server_updated.
+
+ * client.h (struct response): Move definition in from client.c,
+ add status field.
+ (responses): Declare.
+ * client.c (struct response): Remove definition; moved to
+ client.h.
+ (responses): Make non-static. Initialize status field.
+ * server.c (serve_valid_responses): Check and record valid
+ responses, just as in handle_valid_requests in client.c.
+
+ * diff.c (diff_client_senddate): New function.
+ (diff): Use it to send -D arguments to server.
+
+Wed Jul 6 12:52:37 1994 J.T. Conklin (jtc@phishhead.cygnus.com)
+
+ * rcs.c (RCS_parsercsfile_i): New function, parse RCS file
+ referenced by file ptr argument.
+ (RCS_parsercsfile): Open file and pass its file ptr to above function.
+ (RCS_parse): Likewise.
+
+Wed Jul 6 01:25:38 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * client.c (update_entries): Print message indicating that an
+ unpatchable file will be refetched.
+ (client_update): Print message when refetching unpatchable files.
+
+Fri Jul 1 07:16:29 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * client.c (send_dirent_proc): Don't call send_a_repository if
+ repository is "".
+
+Fri Jul 1 13:58:11 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (last_dirname, last_repos): Move out of function.
+ (failed_patches, failed_patches_count): New static variables.
+ (update_entries): If patch program fails, save short_pathname in
+ failed_patches array, only exit program if retcode is -1, and
+ return out of the function rather than update the Entries line.
+ (start_server): Clear toplevel_repos, last_dirname, last_repos.
+ (client_update): If failed_patches is not NULL after doing first
+ update, do another update, but remove all the failed files first.
+
+Thu Jun 30 09:08:57 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (requests): Add request "Global_option".
+ (serve_global_option): New function, to handle it.
+ * client.c (start_server): Deal with global options. Check for
+ errors from fprintf.
+
+ * client.c (send_fileproc): Split out code which sends repository
+ into new function send_a_repository. Also, deal with update_dir
+ being ".".
+ (send_dirent_proc): Call send_a_repository.
+ * add.c (add): If client_active, do special processing for
+ directories.
+ (add_directory): If server_active, don't try to create CVSADM
+ directory.
+
+Thu Jun 30 11:58:52 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (update_entries): If patch succeeds, remove the backup
+ file.
+ * server.c (server_updated): Add new argument file_info. If it is
+ not NULL, use it rather than sb to get the file mode.
+ * server.h (server_updated): Update prototype for new argument.
+ * update.c (update_file_proc): Pass new arguments to patch_file
+ and server_updated.
+ (patch_file): Add new argument file_info. Don't use -p to check
+ out new version, check it out into file and rename that to file2.
+ If result is not readable, assume file is dead and set docheckout.
+ Call xchmod on file2. Close the patch file after checking for a
+ binary diff. Set file_info to the results of stat on file2.
+ (merge_file): Pass new argument to server_updated.
+ * commit.c (commit_fileproc): Pass new argument to server_updated.
+
+Wed Jun 29 13:00:41 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (krb_realmofhost): Declare, since it's not the current
+ <krb.h>.
+ (start_server): Save the name returned by gethostbyname. Call
+ krb_realmofhost to get the realm. Pass the resulting realm to
+ krb_sendauth. Pass the saved real name to krb_sendauth, rather
+ than server_host.
+
+ * update.c (update_file_proc): Pass &docheckout to patch_file. If
+ it is set to 1, fall through to T_CHECKOUT case.
+ (patch_file): Add docheckout argument. Set it to 1 if we can't
+ make a patch. Check out the files and run diff rather than
+ rcsdiff. If either file does not end in a newline, we can't make
+ a patch. If the patch starts with the string "Binary", assume
+ one or the other is a binary file, and that we can't make a patch.
+
+Tue Jun 28 11:57:29 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * client.c (update_entries): If the patch file is empty, don't run
+ patch program; avoids error message.
+
+ * classify.c (Classify_File): Return T_CHECKOUT, not T_PATCH, if
+ the file is in the Attic.
+
+ * cvs.h (enum classify_type): Add T_PATCH.
+ * config.h (PATCH_PROGRAM): Define.
+ * classify.c (Classify_File): If user file exists and is not
+ modified, and using the same -k options, return T_PATCH instead of
+ T_CHECKOUT.
+ * update.c (patches): New static variable.
+ (update): Add u to gnu_getopt argument. Handle it.
+ (update_file_proc): Handle T_PATCH.
+ (patch_file): New static function.
+ * server.h (enum server_updated_arg4): Add SERVER_PATCHED.
+ * server.c (server_updated): Handle SERVER_PATCHED by sending
+ "Patched" command.
+ (serve_ignore): New static function.
+ (requests): Add "update-patches".
+ (client_update): If the server supports "update-patches", send -u.
+ * client.c (struct update_entries_data): Change contents field
+ from int to an unnamed enum.
+ (update_entries): Correponding change. If contents is
+ UPDATE_ENTRIES_PATCH, pass the input to the patch program.
+ (handle_checked_in): Initialize contents to enum value, not int.
+ (handle_updated, handle_merged): Likewise.
+ (handle_patched): New static function.
+ (responses): Add "Patched".
+ * commit.c (check_fileproc): Handle T_PATCH.
+ * status.c (status_fileproc): Likewise.
+
+ * client.c (start_server): If CVS_CLIENT_PORT is set in the
+ environment, connect to that port, rather than looking up "cvs" in
+ /etc/services. For debugging.
+
+Tue Jun 21 12:48:16 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * update.c (joining): Return result of comparing pointer with
+ NULL, not result of casting (truncating, on Alpha) pointer to int.
+
+ * main.c (main) [HAVE_KERBEROS]: Impose a umask if starting as
+ Kerberos server, so temp directories won't be world-writeable.
+
+ * update.c (update_filesdone_proc) [CVSADM_ROOT]: If environment
+ variable CVS_IGNORE_REMOTE_ROOT is set and repository is remote,
+ don't create CVS/Root file.
+ * main.c (main): If env var CVS_IGNORE_REMOTE_ROOT is set, don't
+ check CVS/Root.
+
+Fri Jun 10 18:48:32 1994 Mark Eichin (eichin@cygnus.com)
+
+ * server.c (O_NDELAY): use POSIX O_NONBLOCK by default, unless it
+ isn't available (in which case substitute O_NDELAY.)
+
+Thu Jun 9 19:17:44 1994 Mark Eichin (eichin@cygnus.com)
+
+ * server.c (server_cleanup): chdir out of server_temp_dir before
+ deleting it (so that it works on non-BSD systems.) Code for choice
+ of directory cloned from server().
+
+Fri May 27 18:16:01 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * client.c (update_entries): Add return type of void.
+ (get_responses_and_close): If using Kerberos and from_server and
+ to_server are using the same file descriptor, use shutdown, not
+ fclose. Close from_server.
+ (start_server): New function; most of old version renamed to
+ start_rsh_server.
+ (start_rsh_server): Mostly renamed from old start_server.
+ (send_fileproc): Use %lu and cast sb.st_size in fprintf call.
+ (send_files): Remove unused variables repos and i.
+ (option_no_arg): Comment out; unused.
+ * main.c (main): Initialize cvs_update_env to 0. If command is
+ "kserver", authenticate and change command to "server". If
+ command is "server", don't call Name_Root, don't check access to
+ history file, and don't assume that CVSroot is not NULL.
+ * server.c (my_memmove): Removed.
+ (strerror): Change check from STRERROR_MISSING to HAVE_STRERROR.
+ (serve_root): Likewise for putenv.
+ (serve_modified): Initialize buf to NULL.
+ (struct output_buffer, buf_try_send): Remove old buffering code.
+ (struct buffer, struct buffer_data, BUFFER_DATA_SIZE,
+ allocate_buffer_datas, get_buffer_data, buf_empty_p,
+ buf_append_char, buf_append_data, buf_read_file, buf_input_data,
+ buf_copy_lines): New buffering code.
+ (buf_output, buf_output0, buf_send_output, set_nonblock,
+ set_block, buf_send_counted, buf_copy_counted): Rewrite for new
+ buffering code.
+ (protocol, protocol_memory_error, outbuf_memory_error,
+ do_cvs_command, server_updated): Rewrite for new buffering code.
+ (input_memory_error): New function.
+ (server): Put Rcsbin at start of PATH in environment.
+ * Makefile.in: Add @includeopt@ to DEFS.
+
+Fri May 20 08:13:10 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * cvs.h, classify.c (Classify_File): New argument update_dir.
+ Include it in user messages.
+ * commit.c (check_fileproc), status.c (status_fileproc), update.c
+ (update_file_proc): Pass update_dir to Classify_File.
+ * commit.c (check_fileproc), update.c (checkout_file):
+ Include update_dir in user messages.
+ * commit.c (check_fileproc) update.c (update_file_proc): Re-word
+ "unknown status" message.
+
+ * server.c (server_checked_in): Deal with the case where
+ scratched_file is set rather than entries_line.
+
+ * entries.c (Register): Write file even if server_active.
+ * add.c (add): Add comment about how we depend on above behavior.
+
+Tue May 17 08:16:42 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * mkmodules.c: Add dummy server_active and server_cleanup, to go
+ with the dummy Lock_Cleanup already there.
+
+ * server.c (server_cleanup): No longer static.
+
+Sat May 7 10:17:17 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ Deal with add and remove:
+ * commit.c (checkaddfile): If CVSEXT_OPT or CVSEXT_LOG file does
+ not exist, just silently keep going.
+ (remove_file): If server_active, remove file before creating
+ temporary file with that name.
+ * server.c (serve_remove, serve_add): New functions.
+ (requests): Add them.
+ * server.c (server_register): If options is NULL, it means there
+ are no options.
+ * server.c, server.h (server_scratch_entry_only): New function.
+ New variable kill_scratched_file.
+ (server_scratch, server_updated): Deal with kill_scratched_file.
+ * commit.c (commit_fileproc): If server_active, call
+ server_scratch_entry_only and server_updated.
+ * add.c (add): Add client_active code.
+ (add): If server_active, call server_checked_in for each file added.
+ * remove.c (remove): Add client_active code.
+ (remove_fileproc): If server_active, call server_checked_in.
+ * main.c (cmds), client.c, client.h: New functions client_add and
+ client_remove.
+ * Move declarations of add, cvsremove, diff, and cvslog from
+ main.c to cvs.h.
+ * client.c (call_in_directory): Update comment regarding Root and
+ Repository files.
+ (send_fileproc): Only send Entries line if Version_TS really finds
+ an entry. If it doesn't find one, send Modified.
+ (update_entries): If version is empty or starts with 0 or -,
+ create a dummy timestamp.
+
+Thu May 5 19:02:51 1994 Per Bothner (bothner@kalessin.cygnus.com)
+
+ * recurse/c (start_recursion): If we're doing rtag, and thus
+ have cd'd to the reporsitory, add ,v to a file name before stat'ing.
+
+Wed Apr 20 15:01:45 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * client.c (client_commit): Call ign_setup.
+ (client_update, client_checkout): Likewise.
+ * diff.c (diff): If client, call ign_setup.
+ * log.c (cvslog): Likewise.
+ * update.h (ignlist): Change definition to declaration to avoid
+ depending upon common semantics (not required by ANSI C, and not
+ the default on Irix 5).
+ * update.c (ignlist): Define.
+
+Tue Apr 19 00:02:54 1994 John Gilmore (gnu@cygnus.com)
+
+ Add support for remote `cvs log'; clean up `cvs diff' a bit.
+
+ * client.c (send_arg): Make external.
+ (send_option_string): New function.
+ (client_diff_usage): Remove, unused.
+ (client_diff): Just call diff, not do_diff.
+ (client_log): Add.
+ * client.h (client_log, send_arg, send_option_string): Declare.
+ * cvs.h (cvslog): Declare.
+ * diff.c (do_diff): Fold back into diff(), distinguish by checking
+ client_active.
+ (diff): Remove `-*' arg parsing crud; use send_option_string.
+ * log.c (cvslog): If a client, start the server, pass options
+ and files, and handle server responses.
+ * main.c (cmds): Add client_log.
+ (main): Remove obnoxious message every time CVS/Root is used.
+ Now CVS will be quiet about it -- unless there is a conflict
+ between $CVSROOT or -d value versus CVS/Root.
+ * server.c (serve_log): Add.
+ (requests): Add "log".
+
+Mon Apr 18 22:07:53 1994 John Gilmore (gnu@cygnus.com)
+
+ Add support for remote `cvs diff'.
+
+ * diff.c (diff): Break guts out into new fn do_diff.
+ Add code to handle starting server, writing args,
+ sending files, and retrieving responses.
+ (includes): Use PARAMS for static function declarations.
+ * client.c (to_server, from_server, rsh_pid,
+ get_responses_and_close, start_server, send_files,
+ option_with_arg): Make external.
+ (send_file_names): New function.
+ (client_diff): New function.
+ * client.h (client_diff, to_server, from_server,
+ rsh_pid, option_with_arg, get_responses_and_close, start_server,
+ send_file_names, send_files): Declare.
+ * cvs.h (diff): Declare.
+ * main.c (cmds): Add client_diff to command table.
+ * server.c (serve_diff): New function.
+ (requests): Add serve_diff.
+ (server): Bug fix: avoid free()ing incremented cmd pointer.
+ * update.h (update_filesdone_proc): Declare with PARAMS.
+
+Sat Apr 16 04:20:09 1994 John Gilmore (gnu@cygnus.com)
+
+ * root.c (Name_root): Fix tyop (CVSroot when root meant).
+
+Sat Apr 16 03:49:36 1994 John Gilmore (gnu@cygnus.com)
+
+ Clean up remote `cvs update' to properly handle ignored
+ files (and files that CVS can't identify), and to create
+ CVS/Root entries on the client side, not the server side.
+
+ * client.c (send_fileproc): Handle the ignore list.
+ (send_dirent_proc): New function for handling ignores.
+ (send_files): Use update_filesdone_proc and send_dirent_proc
+ while recursing through the local filesystem.
+ * update.h: New file.
+ * update.c: Move a few things into update.h so that client.c
+ can use them.
+
+Fri Mar 11 13:13:20 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * server.c: If O_NDELAY is not defined, but O_NONBLOCK is, define
+ O_NDELAY to O_NONBLOCK.
+
+Wed Mar 9 21:08:30 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ Fix some spurious remote CVS errors caused by the CVS/Root patches:
+ * update.c (update_filesdone_proc): If server_active, don't try to
+ create CVS/Root.
+ * root.c (Name_Root): Make error messages which happen if root is
+ not an absolute pathname or if it doesn't exist a bit clearer.
+ Skip them if root contains a colon.
+
+Mon Nov 1 15:54:51 1993 K. Richard Pixley (rich@sendai.cygnus.com)
+
+ * client.c (client_commit): dynamically allocate message.
+
+Tue Jun 1 17:03:05 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com)
+
+ * server.h: remove alloca cruft
+
+ * server.c: replace with better alloca cruft
+
+Mon May 24 11:25:11 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * entries.c (Scratch_Entry): Update our local Entries file even if
+ server_active.
+
+ * server.c (server_scratch, server_register): If both Register
+ and Scratch_Entry happen, use whichever one happened later.
+ If neither happen, silently continue.
+
+ * client.c (client_checkout): Initialize tag and date (eichin and
+ I independently discovered this bug at the same time).
+
+Wed May 19 10:11:51 1993 Mark Eichin (eichin@cygnus.com)
+
+ * client.c (update_entries): handle short reads over the net
+ (SVR4 fread is known to be broken, specifically for short
+ reads off of streams.)
+
+Tue May 18 15:53:44 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * server.c (do_cvs_command): Fix fencepost error in setting
+ num_to_check.
+
+ * server.c (do_cvs_command): If terminated with a core dump, print
+ message and set dont_delete_temp.
+ (server_cleanup): If dont_delete_temp, don't delete it.
+
+ * client.c (get_server_responses): Don't change cmd since we
+ are going to "free (cmd)".
+
+ * server.c: Rename memmove to my_memmove pending a real fix.
+
+ * server.c (do_cvs_command): Set num_to_check to largest descriptor
+ we try to use, rather than using (non-portable) getdtablesize.
+
+Wed May 12 15:31:40 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ Add CVS client feature:
+ * client.{c,h}: New files.
+ * cvs.h: Include client.h.
+ * main.c: If CVSROOT has a colon, use client commands instead.
+ * vers_ts.c (Version_TS): If repository arg is NULL, don't worry
+ about the repository.
+ * logmsg.c (do_editor): If repository or changes is NULL, just don't
+ use those features.
+ * create_adm.c (Create_Admin), callers: Move the test for whether
+ the repository exists from here to callers.
+ * repos.c (Name_Repository): Don't test whether the repository exists
+ if client_active set (might be better to move test to callers).
+
+ Add CVS server feature:
+ * server.{c,h}: New files.
+ * cvs.h: Include server.h.
+ * checkin.c (Checkin): Call server_checked_in.
+ * update.c (update_file_proc, merge_files): Call server_updated.
+ * entries.c (Register): Call server_register.
+ (Scratch_Entry): Call server_scratch.
+ * main.c: Add server to cmds.
+ * vers_ts.c (Version_TS): If server_active, call new function
+ time_stamp_server to set ts_user.
+
diff --git a/gnu/usr.bin/cvs/cvs/README-rm-add b/gnu/usr.bin/cvs/cvs/README-rm-add
new file mode 100644
index 000000000000..17c721fd958a
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/README-rm-add
@@ -0,0 +1,48 @@
+WHAT THE "DEATH SUPPORT" FEATURES DO:
+
+(this really should be in the main manual, but noone has gotten around
+to updating it).
+
+CVS with death support can record when a file is active, or alive, and
+when it is removed, or dead. With this facility you can record the
+history of a file, including the fact that at some point in its life
+the file was removed and then later added.
+
+First, the following now works as expected:
+
+ touch foo
+ cvs add foo ; cvs ci -m "added" foo
+ rm foo
+ cvs rm foo ; cvs ci -m "removed" foo
+ touch foo
+ cvs add foo ; cvs ci -m "resurrected" foo
+
+Second, files can now be added or removed in a branch and later merged
+into the trunk.
+
+ cvs update -A
+ touch a b c
+ cvs add a b c ; cvs ci -m "added" a b c
+ cvs tag -b branchtag
+ cvs update -r branchtag
+ touch d ; cvs add d
+ rm a ; cvs rm a
+ cvs ci -m "added d, removed a"
+ cvs update -A
+ cvs update -jbranchtag
+
+Added and removed files may also be merged between branches.
+
+Files removed in the trunk may be merged into branches.
+
+Files added on the trunk are a special case. They cannot be merged
+into a branch. Instead, simply branch the file by hand.
+
+I also extended the "cvs update -j" semantic slightly. Like before,
+if you use two -j options, the changes made between the first and the
+second will be merged into your working files. This has not changed.
+
+If you use only one -j option, it is used as the second -j option.
+The first is assumed to be the greatest common ancestor revision
+between the revision specified by the -j and the BASE revision of your
+working file.
diff --git a/gnu/usr.bin/cvs/cvs/client.c b/gnu/usr.bin/cvs/cvs/client.c
new file mode 100644
index 000000000000..dd96db8f091d
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/client.c
@@ -0,0 +1,3542 @@
+/* CVS client-related stuff. */
+
+#include "cvs.h"
+
+#ifdef CLIENT_SUPPORT
+
+#include "update.h" /* Things shared with update.c */
+#include "md5.h"
+
+#ifdef AUTH_CLIENT_SUPPORT
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot));
+#endif /* AUTH_CLIENT_SUPPORT */
+
+#if HAVE_KERBEROS
+#define CVS_PORT 1999
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <krb.h>
+
+extern char *krb_realmofhost ();
+#ifndef HAVE_KRB_GET_ERR_TEXT
+#define krb_get_err_text(status) krb_err_txt[status]
+#endif /* HAVE_KRB_GET_ERR_TEXT */
+#endif /* HAVE_KERBEROS */
+
+static void add_prune_candidate PROTO((char *));
+
+/* All the commands. */
+int add PROTO((int argc, char **argv));
+int admin PROTO((int argc, char **argv));
+int checkout PROTO((int argc, char **argv));
+int commit PROTO((int argc, char **argv));
+int diff PROTO((int argc, char **argv));
+int history PROTO((int argc, char **argv));
+int import PROTO((int argc, char **argv));
+int cvslog PROTO((int argc, char **argv));
+int patch PROTO((int argc, char **argv));
+int release PROTO((int argc, char **argv));
+int cvsremove PROTO((int argc, char **argv));
+int rtag PROTO((int argc, char **argv));
+int status PROTO((int argc, char **argv));
+int tag PROTO((int argc, char **argv));
+int update PROTO((int argc, char **argv));
+
+/* All the response handling functions. */
+static void handle_ok PROTO((char *, int));
+static void handle_error PROTO((char *, int));
+static void handle_valid_requests PROTO((char *, int));
+static void handle_checked_in PROTO((char *, int));
+static void handle_new_entry PROTO((char *, int));
+static void handle_checksum PROTO((char *, int));
+static void handle_copy_file PROTO((char *, int));
+static void handle_updated PROTO((char *, int));
+static void handle_merged PROTO((char *, int));
+static void handle_patched PROTO((char *, int));
+static void handle_removed PROTO((char *, int));
+static void handle_remove_entry PROTO((char *, int));
+static void handle_set_static_directory PROTO((char *, int));
+static void handle_clear_static_directory PROTO((char *, int));
+static void handle_set_sticky PROTO((char *, int));
+static void handle_clear_sticky PROTO((char *, int));
+static void handle_set_checkin_prog PROTO((char *, int));
+static void handle_set_update_prog PROTO((char *, int));
+static void handle_module_expansion PROTO((char *, int));
+static void handle_m PROTO((char *, int));
+static void handle_e PROTO((char *, int));
+
+#endif /* CLIENT_SUPPORT */
+
+#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
+
+/* Shared with server. */
+
+/*
+ * Return a malloc'd, '\0'-terminated string
+ * corresponding to the mode in SB.
+ */
+char *
+#ifdef __STDC__
+mode_to_string (mode_t mode)
+#else /* ! __STDC__ */
+mode_to_string (mode)
+ mode_t mode;
+#endif /* __STDC__ */
+{
+ char buf[18], u[4], g[4], o[4];
+ int i;
+
+ i = 0;
+ if (mode & S_IRUSR) u[i++] = 'r';
+ if (mode & S_IWUSR) u[i++] = 'w';
+ if (mode & S_IXUSR) u[i++] = 'x';
+ u[i] = '\0';
+
+ i = 0;
+ if (mode & S_IRGRP) g[i++] = 'r';
+ if (mode & S_IWGRP) g[i++] = 'w';
+ if (mode & S_IXGRP) g[i++] = 'x';
+ g[i] = '\0';
+
+ i = 0;
+ if (mode & S_IROTH) o[i++] = 'r';
+ if (mode & S_IWOTH) o[i++] = 'w';
+ if (mode & S_IXOTH) o[i++] = 'x';
+ o[i] = '\0';
+
+ sprintf(buf, "u=%s,g=%s,o=%s", u, g, o);
+ return xstrdup(buf);
+}
+
+/*
+ * Change mode of FILENAME to MODE_STRING.
+ * Returns 0 for success or errno code.
+ */
+int
+change_mode (filename, mode_string)
+ char *filename;
+ char *mode_string;
+{
+ char *p;
+ mode_t mode = 0;
+
+ p = mode_string;
+ while (*p != '\0')
+ {
+ if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
+ {
+ int can_read = 0, can_write = 0, can_execute = 0;
+ char *q = p + 2;
+ while (*q != ',' && *q != '\0')
+ {
+ if (*q == 'r')
+ can_read = 1;
+ else if (*q == 'w')
+ can_write = 1;
+ else if (*q == 'x')
+ can_execute = 1;
+ ++q;
+ }
+ if (p[0] == 'u')
+ {
+ if (can_read)
+ mode |= S_IRUSR;
+ if (can_write)
+ mode |= S_IWUSR;
+ if (can_execute)
+ {
+#ifdef EXECUTE_PERMISSION_LOSES
+ KFF_DEBUG (printf ("*** S_IXUSR in change_mode().\n"));
+#else /* ! EXECUTE_PERMISSION_LOSES */
+ mode |= S_IXUSR;
+#endif /* EXECUTE_PERMISSION_LOSES */
+ }
+ }
+ else if (p[0] == 'g')
+ {
+ if (can_read)
+ mode |= S_IRGRP;
+ if (can_write)
+ mode |= S_IWGRP;
+ if (can_execute)
+ {
+#ifdef EXECUTE_PERMISSION_LOSES
+ KFF_DEBUG (printf ("*** S_IXGRP in change_mode().\n"));
+#else /* ! EXECUTE_PERMISSION_LOSES */
+ mode |= S_IXGRP;
+#endif /* EXECUTE_PERMISSION_LOSES */
+ }
+ }
+ else if (p[0] == 'o')
+ {
+ if (can_read)
+ mode |= S_IROTH;
+ if (can_write)
+ mode |= S_IWOTH;
+ if (can_execute)
+ {
+#ifdef EXECUTE_PERMISSION_LOSES
+ KFF_DEBUG (printf ("*** S_IXOTH in change_mode().\n"));
+#else /* ! EXECUTE_PERMISSION_LOSES */
+ mode |= S_IXOTH;
+#endif /* EXECUTE_PERMISSION_LOSES */
+ }
+ }
+ }
+ /* Skip to the next field. */
+ while (*p != ',' && *p != '\0')
+ ++p;
+ if (*p == ',')
+ ++p;
+ }
+ if (chmod (filename, mode) < 0)
+ return errno;
+ return 0;
+}
+
+#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
+
+#ifdef CLIENT_SUPPORT
+
+/* The host part of CVSROOT. */
+static char *server_host;
+/* The user part of CVSROOT */
+static char *server_user;
+/* The repository part of CVSROOT. */
+static char *server_cvsroot;
+
+int client_active;
+
+int client_prune_dirs;
+
+/* Set server_host and server_cvsroot. */
+static void
+parse_cvsroot ()
+{
+ char *p;
+
+ server_host = xstrdup (CVSroot);
+ server_cvsroot = strchr (server_host, ':');
+ *server_cvsroot = '\0';
+ ++server_cvsroot;
+
+ if ( (p = strchr (server_host, '@')) == NULL) {
+ server_user = NULL;
+ } else {
+ server_user = server_host;
+ server_host = p;
+ ++server_host;
+ *p = '\0';
+ }
+
+ client_active = 1;
+}
+
+/* Stream to write to the server. */
+FILE *to_server;
+/* Stream to read from the server. */
+FILE *from_server;
+
+#if ! RSH_NOT_TRANSPARENT
+/* Process ID of rsh subprocess. */
+static int rsh_pid = -1;
+#endif /* ! RSH_NOT_TRANSPARENT */
+
+
+/*
+ * Read a line from the server.
+ *
+ * Space for the result is malloc'd and should be freed by the caller.
+ *
+ * Returns number of bytes read. If EOF_OK, then return 0 on end of file,
+ * else end of file is an error.
+ */
+static int
+read_line (resultp, eof_ok)
+ char **resultp;
+ int eof_ok;
+{
+ int c;
+ char *result;
+ int input_index = 0;
+ int result_size = 80;
+
+ fflush (to_server);
+ result = (char *) xmalloc (result_size);
+
+ while (1)
+ {
+ c = getc (from_server);
+
+ if (c == EOF)
+ {
+ free (result);
+ if (ferror (from_server))
+ error (1, errno, "reading from server");
+ /* It's end of file. */
+ if (eof_ok)
+ return 0;
+ else
+ error (1, 0, "premature end of file from server");
+ }
+
+ if (c == '\n')
+ break;
+
+ result[input_index++] = c;
+ while (input_index + 1 >= result_size)
+ {
+ result_size *= 2;
+ result = (char *) xrealloc (result, result_size);
+ }
+ }
+
+ if (resultp)
+ *resultp = result;
+
+ /* Terminate it just for kicks, but we *can* deal with embedded NULs. */
+ result[input_index] = '\0';
+
+ if (resultp == NULL)
+ free (result);
+ return input_index;
+}
+
+#endif /* CLIENT_SUPPORT */
+
+
+#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
+
+/*
+ * Zero if compression isn't supported or requested; non-zero to indicate
+ * a compression level to request from gzip.
+ */
+int gzip_level;
+
+int filter_through_gzip (fd, dir, level, pidp)
+ int fd, dir, level;
+ pid_t *pidp;
+{
+ static char buf[5] = "-";
+ static char *gzip_argv[3] = { "gzip", buf };
+
+ sprintf (buf+1, "%d", level);
+ return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp);
+}
+
+int filter_through_gunzip (fd, dir, pidp)
+ int fd, dir;
+ pid_t *pidp;
+{
+ static char *gunzip_argv[2] = { "gunzip" };
+ return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
+}
+
+#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
+
+#ifdef CLIENT_SUPPORT
+
+/*
+ * The Repository for the top level of this command (not necessarily
+ * the CVSROOT, just the current directory at the time we do it).
+ */
+static char *toplevel_repos;
+
+/* Working directory when we first started. */
+char toplevel_wd[PATH_MAX];
+
+static void
+handle_ok (args, len)
+ char *args;
+ int len;
+{
+ return;
+}
+
+static void
+handle_error (args, len)
+ char *args;
+ int len;
+{
+ int something_printed;
+
+ /*
+ * First there is a symbolic error code followed by a space, which
+ * we ignore.
+ */
+ char *p = strchr (args, ' ');
+ if (p == NULL)
+ {
+ error (0, 0, "invalid data from cvs server");
+ return;
+ }
+ ++p;
+ len -= p - args;
+ something_printed = 0;
+ for (; len > 0; --len)
+ {
+ something_printed = 1;
+ putc (*p++, stderr);
+ }
+ if (something_printed)
+ putc ('\n', stderr);
+}
+
+static void
+handle_valid_requests (args, len)
+ char *args;
+ int len;
+{
+ char *p = args;
+ char *q;
+ struct request *rq;
+ do
+ {
+ q = strchr (p, ' ');
+ if (q != NULL)
+ *q++ = '\0';
+ for (rq = requests; rq->name != NULL; ++rq)
+ {
+ if (strcmp (rq->name, p) == 0)
+ break;
+ }
+ if (rq->name == NULL)
+ /*
+ * It is a request we have never heard of (and thus never
+ * will want to use). So don't worry about it.
+ */
+ ;
+ else
+ {
+ if (rq->status == rq_enableme)
+ {
+ /*
+ * Server wants to know if we have this, to enable the
+ * feature.
+ */
+ if (fprintf(to_server, "%s\n", rq->name) < 0)
+ error (1, errno, "writing to server");
+ if (!strcmp("UseUnchanged",rq->name))
+ use_unchanged = 1;
+ }
+ else
+ rq->status = rq_supported;
+ }
+ p = q;
+ } while (q != NULL);
+ for (rq = requests; rq->name != NULL; ++rq)
+ {
+ if (rq->status == rq_essential)
+ error (1, 0, "request `%s' not supported by server", rq->name);
+ else if (rq->status == rq_optional)
+ rq->status = rq_not_supported;
+ }
+}
+
+static int use_directory = -1;
+
+static char *get_short_pathname PROTO((const char *));
+
+static char *
+get_short_pathname (name)
+ const char *name;
+{
+ const char *retval;
+ if (use_directory)
+ return (char *) name;
+ if (strncmp (name, toplevel_repos, strlen (toplevel_repos)) != 0)
+ error (1, 0, "server bug: name `%s' doesn't specify file in `%s'",
+ name, toplevel_repos);
+ retval = name + strlen (toplevel_repos) + 1;
+ if (retval[-1] != '/')
+ error (1, 0, "server bug: name `%s' doesn't specify file in `%s'",
+ name, toplevel_repos);
+ return (char *) retval;
+}
+
+/*
+ * Do all the processing for PATHNAME, where pathname consists of the
+ * repository and the filename. The parameters we pass to FUNC are:
+ * DATA is just the DATA parameter which was passed to
+ * call_in_directory; ENT_LIST is a pointer to an entries list (which
+ * we manage the storage for); SHORT_PATHNAME is the pathname of the
+ * file relative to the (overall) directory in which the command is
+ * taking place; and FILENAME is the filename portion only of
+ * SHORT_PATHNAME. When we call FUNC, the curent directory points to
+ * the directory portion of SHORT_PATHNAME. */
+
+static char *last_dirname;
+
+static void
+call_in_directory (pathname, func, data)
+ char *pathname;
+ void (*func) PROTO((char *data, List *ent_list, char *short_pathname,
+ char *filename));
+ char *data;
+{
+ static List *last_entries;
+
+ char *dirname;
+ char *filename;
+ /* Just the part of pathname relative to toplevel_repos. */
+ char *short_pathname = get_short_pathname (pathname);
+ char *p;
+
+ /*
+ * Do the whole descent in parallel for the repositories, so we
+ * know what to put in CVS/Repository files. I'm not sure the
+ * full hair is necessary since the server does a similar
+ * computation; I suspect that we only end up creating one
+ * directory at a time anyway.
+ *
+ * Also note that we must *only* worry about this stuff when we
+ * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co
+ * CVSROOT; cvs update' is legitimate, but in this case
+ * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of
+ * foo/bar/CVS/Repository.
+ */
+ char *reposname;
+ char *short_repos;
+ char *reposdirname;
+ char *rdirp;
+ int reposdirname_absolute;
+
+ reposname = NULL;
+ if (use_directory)
+ read_line (&reposname, 0);
+
+ reposdirname_absolute = 0;
+ if (reposname != NULL)
+ {
+ if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
+ {
+ reposdirname_absolute = 1;
+ short_repos = reposname;
+ }
+ else
+ {
+ short_repos = reposname + strlen (toplevel_repos) + 1;
+ if (short_repos[-1] != '/')
+ {
+ reposdirname_absolute = 1;
+ short_repos = reposname;
+ }
+ }
+ }
+ else
+ {
+ short_repos = short_pathname;
+ }
+ reposdirname = xstrdup (short_repos);
+ p = strrchr (reposdirname, '/');
+ if (p == NULL)
+ {
+ reposdirname = xrealloc (reposdirname, 2);
+ reposdirname[0] = '.'; reposdirname[1] = '\0';
+ }
+ else
+ *p = '\0';
+
+ dirname = xstrdup (short_pathname);
+ p = strrchr (dirname, '/');
+ if (p == NULL)
+ {
+ dirname = xrealloc (dirname, 2);
+ dirname[0] = '.'; dirname[1] = '\0';
+ }
+ else
+ *p = '\0';
+ if (client_prune_dirs)
+ add_prune_candidate (dirname);
+
+ filename = strrchr (short_repos, '/');
+ if (filename == NULL)
+ filename = short_repos;
+ else
+ ++filename;
+
+ if (reposname != NULL)
+ {
+ /* This is the use_directory case. */
+
+ short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
+ strcpy (short_pathname, pathname);
+ strcat (short_pathname, filename);
+ }
+
+ if (last_dirname == NULL
+ || strcmp (last_dirname, dirname) != 0)
+ {
+ if (last_dirname)
+ free (last_dirname);
+ last_dirname = dirname;
+
+ if (toplevel_wd[0] == '\0')
+ if (getwd (toplevel_wd) == NULL)
+ error (1, 0,
+ "could not get working directory: %s", toplevel_wd);
+
+ if (chdir (toplevel_wd) < 0)
+ error (1, errno, "could not chdir to %s", toplevel_wd);
+ if (chdir (dirname) < 0)
+ {
+ char *dir;
+ char *dirp;
+
+ if (! existence_error (errno))
+ error (1, errno, "could not chdir to %s", dirname);
+
+ /* Directory does not exist, we need to create it. */
+ dir = xmalloc (strlen (dirname) + 1);
+ dirp = dirname;
+ rdirp = reposdirname;
+
+ /* This algorithm makes nested directories one at a time
+ and create CVS administration files in them. For
+ example, we're checking out foo/bar/baz from the
+ repository:
+
+ 1) create foo, point CVS/Repository to <root>/foo
+ 2) .. foo/bar .. <root>/foo/bar
+ 3) .. foo/bar/baz .. <root>/foo/bar/baz
+
+ As you can see, we're just stepping along DIRNAME (with
+ DIRP) and REPOSDIRNAME (with RDIRP) respectively.
+
+ We need to be careful when we are checking out a
+ module, however, since DIRNAME and REPOSDIRNAME are not
+ going to be the same. Since modules will not have any
+ slashes in their names, we should watch the output of
+ STRCHR to decide whether or not we should use STRCHR on
+ the RDIRP. That is, if we're down to a module name,
+ don't keep picking apart the repository directory name. */
+
+ do
+ {
+ dirp = strchr (dirp, '/');
+ if (dirp)
+ {
+ strncpy (dir, dirname, dirp - dirname);
+ dir[dirp - dirname] = '\0';
+ /* Skip the slash. */
+ ++dirp;
+ if (rdirp == NULL)
+ error (0, 0,
+ "internal error: repository string too short.");
+ else
+ rdirp = strchr (rdirp, '/');
+ }
+ else
+ {
+ /* If there are no more slashes in the dir name,
+ we're down to the most nested directory -OR- to
+ the name of a module. In the first case, we
+ should be down to a DIRP that has no slashes,
+ so it won't help/hurt to do another STRCHR call
+ on DIRP. It will definitely hurt, however, if
+ we're down to a module name, since a module
+ name can point to a nested directory (that is,
+ DIRP will still have slashes in it. Therefore,
+ we should set it to NULL so the routine below
+ copies the contents of REMOTEDIRNAME onto the
+ root repository directory (does this if rdirp
+ is set to NULL, because we used to do an extra
+ STRCHR call here). */
+
+ rdirp = NULL;
+ strcpy (dir, dirname);
+ }
+
+ if (CVS_MKDIR (dir, 0777) < 0)
+ {
+ /* Now, let me get this straight. In IBM C/C++
+ * under OS/2, the error string for EEXIST is:
+ *
+ * "The file already exists",
+ *
+ * and the error string for EACCESS is:
+ *
+ * "The file or directory specified is read-only".
+ *
+ * Nonetheless, mkdir() will set EACCESS if the
+ * directory *exists*, according both to the
+ * documentation and its actual behavior.
+ *
+ * I'm sure that this made sense, to someone,
+ * somewhere, sometime. Just not me, here, now.
+ */
+#ifdef EACCESS
+ if ((errno != EACCESS) && (errno != EEXIST))
+ error (1, errno, "cannot make directory %s", dir);
+#else /* ! defined(EACCESS) */
+ if ((errno != EEXIST))
+ error (1, errno, "cannot make directory %s", dir);
+#endif /* defined(EACCESS) */
+
+ /* It already existed, fine. Just keep going. */
+ }
+ else if (strcmp (command_name, "export") == 0)
+ /* Don't create CVSADM directories if this is export. */
+ ;
+ else
+ {
+ /*
+ * Put repository in CVS/Repository. For historical
+ * (pre-CVS/Root) reasons, this is an absolute pathname,
+ * but what really matters is the part of it which is
+ * relative to cvsroot.
+ */
+ char *repo;
+ char *r;
+
+ repo = xmalloc (strlen (reposdirname)
+ + strlen (toplevel_repos)
+ + 80);
+ if (reposdirname_absolute)
+ r = repo;
+ else
+ {
+ strcpy (repo, toplevel_repos);
+ strcat (repo, "/");
+ r = repo + strlen (repo);
+ }
+
+ if (rdirp)
+ {
+ strncpy (r, reposdirname, rdirp - reposdirname);
+ r[rdirp - reposdirname] = '\0';
+ }
+ else
+ strcpy (r, reposdirname);
+
+ Create_Admin (dir, dir, repo,
+ (char *)NULL, (char *)NULL);
+ free (repo);
+ }
+
+ if (rdirp != NULL)
+ {
+ /* Skip the slash. */
+ ++rdirp;
+ }
+
+ } while (dirp != NULL);
+ free (dir);
+ /* Now it better work. */
+ if (chdir (dirname) < 0)
+ error (1, errno, "could not chdir to %s", dirname);
+ }
+
+ if (strcmp (command_name, "export") != 0)
+ {
+ if (last_entries)
+ Entries_Close (last_entries);
+ last_entries = Entries_Open (0);
+ }
+ }
+ else
+ free (dirname);
+ free (reposdirname);
+ (*func) (data, last_entries, short_pathname, filename);
+ if (reposname != NULL)
+ {
+ free (short_pathname);
+ free (reposname);
+ }
+}
+
+static void
+copy_a_file (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ char *newname;
+
+ read_line (&newname, 0);
+ copy_file (filename, newname);
+ free (newname);
+}
+
+static void
+handle_copy_file (args, len)
+ char *args;
+ int len;
+{
+ call_in_directory (args, copy_a_file, (char *)NULL);
+}
+
+/*
+ * The Checksum response gives the checksum for the file transferred
+ * over by the next Updated, Merged or Patch response. We just store
+ * it here, and then check it in update_entries.
+ */
+
+static int stored_checksum_valid;
+static unsigned char stored_checksum[16];
+
+static void
+handle_checksum (args, len)
+ char *args;
+ int len;
+{
+ char *s;
+ char buf[3];
+ int i;
+
+ if (stored_checksum_valid)
+ error (1, 0, "Checksum received before last one was used");
+
+ s = args;
+ buf[2] = '\0';
+ for (i = 0; i < 16; i++)
+ {
+ char *bufend;
+
+ buf[0] = *s++;
+ buf[1] = *s++;
+ stored_checksum[i] = (char) strtol (buf, &bufend, 16);
+ if (bufend != buf + 2)
+ break;
+ }
+
+ if (i < 16 || *s != '\0')
+ error (1, 0, "Invalid Checksum response: `%s'", args);
+
+ stored_checksum_valid = 1;
+}
+
+/*
+ * If we receive a patch, but the patch program fails to apply it, we
+ * want to request the original file. We keep a list of files whose
+ * patches have failed.
+ */
+
+char **failed_patches;
+int failed_patches_count;
+
+struct update_entries_data
+{
+ enum {
+ /*
+ * We are just getting an Entries line; the local file is
+ * correct.
+ */
+ UPDATE_ENTRIES_CHECKIN,
+ /* We are getting the file contents as well. */
+ UPDATE_ENTRIES_UPDATE,
+ /*
+ * We are getting a patch against the existing local file, not
+ * an entire new file.
+ */
+ UPDATE_ENTRIES_PATCH
+ } contents;
+
+ /*
+ * String to put in the timestamp field or NULL to use the timestamp
+ * of the file.
+ */
+ char *timestamp;
+};
+
+/* Update the Entries line for this file. */
+static void
+update_entries (data_arg, ent_list, short_pathname, filename)
+ char *data_arg;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ char *entries_line;
+ struct update_entries_data *data = (struct update_entries_data *)data_arg;
+
+ char *cp;
+ char *user;
+ char *vn;
+ /* Timestamp field. Always empty according to the protocol. */
+ char *ts;
+ char *options;
+ char *tag;
+ char *date;
+ char *tag_or_date;
+ char *scratch_entries;
+ int bin = 0;
+
+ read_line (&entries_line, 0);
+
+ /*
+ * Parse the entries line.
+ */
+ if (strcmp (command_name, "export") != 0)
+ {
+ scratch_entries = xstrdup (entries_line);
+
+ if (scratch_entries[0] != '/')
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ user = scratch_entries + 1;
+ if ((cp = strchr (user, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ vn = cp;
+ if ((cp = strchr (vn, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+
+ ts = cp;
+ if ((cp = strchr (ts, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ options = cp;
+ if ((cp = strchr (options, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ tag_or_date = cp;
+
+ /* If a slash ends the tag_or_date, ignore everything after it. */
+ cp = strchr (tag_or_date, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ tag = (char *) NULL;
+ date = (char *) NULL;
+ if (*tag_or_date == 'T')
+ tag = tag_or_date + 1;
+ else if (*tag_or_date == 'D')
+ date = tag_or_date + 1;
+ }
+
+ if (data->contents == UPDATE_ENTRIES_UPDATE
+ || data->contents == UPDATE_ENTRIES_PATCH)
+ {
+ char *size_string;
+ char *mode_string;
+ int size;
+ int size_read;
+ int size_left;
+ int fd;
+ char *buf;
+ char *buf2;
+ char *temp_filename;
+ int use_gzip, gzip_status;
+ pid_t gzip_pid = 0;
+
+ read_line (&mode_string, 0);
+
+ read_line (&size_string, 0);
+ if (size_string[0] == 'z')
+ {
+ use_gzip = 1;
+ size = atoi (size_string+1);
+ }
+ else
+ {
+ use_gzip = 0;
+ size = atoi (size_string);
+ }
+ free (size_string);
+
+ temp_filename = xmalloc (strlen (filename) + 80);
+#ifdef _POSIX_NO_TRUNC
+ sprintf (temp_filename, ".new.%.9s", filename);
+#else /* _POSIX_NO_TRUNC */
+ sprintf (temp_filename, ".new.%s", filename);
+#endif /* _POSIX_NO_TRUNC */
+ buf = xmalloc (size);
+
+ /* Some systems, like OS/2 and Windows NT, end lines with CRLF
+ instead of just LF. Format translation is done in the C
+ library I/O funtions. Here we tell them whether or not to
+ convert -- if this file is marked "binary" with the RCS -kb
+ flag, then we don't want to convert, else we do (because
+ CVS assumes text files by default). */
+
+ bin = !(strcmp (options, "-kb"));
+ fd = open (temp_filename,
+ O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0),
+ 0777);
+
+ if (fd < 0)
+ error (1, errno, "writing %s", short_pathname);
+
+ if (use_gzip)
+ fd = filter_through_gunzip (fd, 0, &gzip_pid);
+
+ if (size > 0)
+ {
+ buf2 = buf;
+ size_left = size;
+ while ((size_read = fread (buf2, 1, size_left, from_server)) != size_left)
+ {
+ if (feof (from_server))
+ /* FIXME: Should delete temp_filename. */
+ error (1, 0, "unexpected end of file from server");
+ else if (ferror (from_server))
+ /* FIXME: Should delete temp_filename. */
+ error (1, errno, "reading from server");
+ else
+ {
+ /* short reads are ok if we keep trying */
+ buf2 += size_read;
+ size_left -= size_read;
+ }
+ }
+ if (write (fd, buf, size) != size)
+ error (1, errno, "writing %s", short_pathname);
+ }
+ if (close (fd) < 0)
+ error (1, errno, "writing %s", short_pathname);
+ if (gzip_pid > 0)
+ {
+ if (waitpid (gzip_pid, &gzip_status, 0) == -1)
+ error (1, errno, "waiting for gzip process %d", gzip_pid);
+ else if (gzip_status != 0)
+ error (1, 0, "gzip process exited %d", gzip_status);
+ }
+
+ gzip_pid = -1;
+
+ /* Since gunzip writes files without converting LF to CRLF
+ (a reasonable behavior), we now have a patch file in LF
+ format. Leave the file as is if we're just going to feed
+ it to patch; patch can handle it. However, if it's the
+ final source file, convert it. */
+
+ if (data->contents == UPDATE_ENTRIES_UPDATE)
+ {
+#ifdef LINES_CRLF_TERMINATED
+
+ /* `bin' is non-zero iff `options' contains "-kb", meaning
+ treat this file as binary. */
+
+ if (use_gzip && (! bin))
+ {
+ convert_file (temp_filename, O_RDONLY | OPEN_BINARY,
+ filename, O_WRONLY | O_CREAT | O_TRUNC);
+ if (unlink (temp_filename) < 0)
+ error (0, errno, "warning: couldn't delete %s",
+ temp_filename);
+ }
+ else
+ rename_file (temp_filename, filename);
+
+#else /* ! LINES_CRLF_TERMINATED */
+ rename_file (temp_filename, filename);
+#endif /* LINES_CRLF_TERMINATED */
+ }
+ else
+ {
+ int retcode;
+ char backup[PATH_MAX];
+ struct stat s;
+
+ (void) sprintf (backup, "%s~", filename);
+ (void) unlink_file (backup);
+ if (!isfile (filename))
+ error (1, 0, "patch original file %s does not exist",
+ short_pathname);
+ if (stat (temp_filename, &s) < 0)
+ error (1, 1, "can't stat patch file %s", temp_filename);
+ if (s.st_size == 0)
+ retcode = 0;
+ else
+ {
+ run_setup ("%s -f -s -b ~ %s %s", PATCH_PROGRAM,
+ filename, temp_filename);
+ retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ /* FIXME: should we really be silently ignoring errors? */
+ (void) unlink_file (temp_filename);
+ if (retcode == 0)
+ {
+ /* FIXME: should we really be silently ignoring errors? */
+ (void) unlink_file (backup);
+ }
+ else
+ {
+ int old_errno = errno;
+ char *path_tmp;
+
+ if (isfile (backup))
+ rename_file (backup, filename);
+
+ /* Get rid of the patch reject file. */
+ path_tmp = xmalloc (strlen (filename + 10));
+ strcpy (path_tmp, filename);
+ strcat (path_tmp, ".rej");
+ /* FIXME: should we really be silently ignoring errors? */
+ (void) unlink_file (path_tmp);
+ free (path_tmp);
+
+ /* Save this file to retrieve later. */
+ failed_patches =
+ (char **) xrealloc ((char *) failed_patches,
+ ((failed_patches_count + 1)
+ * sizeof (char *)));
+ failed_patches[failed_patches_count] =
+ xstrdup (short_pathname);
+ ++failed_patches_count;
+
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
+ "could not patch %s%s", filename,
+ retcode == -1 ? "" : "; will refetch");
+
+ stored_checksum_valid = 0;
+
+ return;
+ }
+ }
+ free (temp_filename);
+
+ if (stored_checksum_valid)
+ {
+ FILE *e;
+ struct MD5Context context;
+ unsigned char buf[8192];
+ unsigned len;
+ unsigned char checksum[16];
+
+ /*
+ * Compute the MD5 checksum. This will normally only be
+ * used when receiving a patch, so we always compute it
+ * here on the final file, rather than on the received
+ * data.
+ *
+ * Note that if the file is a text file, we should read it
+ * here using text mode, so its lines will be terminated the same
+ * way they were transmitted.
+ */
+ e = fopen (filename, "r");
+ if (e == NULL)
+ error (1, errno, "could not open %s", short_pathname);
+
+ MD5Init (&context);
+ while ((len = fread (buf, 1, sizeof buf, e)) != 0)
+ MD5Update (&context, buf, len);
+ if (ferror (e))
+ error (1, errno, "could not read %s", short_pathname);
+ MD5Final (checksum, &context);
+
+ fclose (e);
+
+ stored_checksum_valid = 0;
+
+ if (memcmp (checksum, stored_checksum, 16) != 0)
+ {
+ if (data->contents != UPDATE_ENTRIES_PATCH)
+ error (1, 0, "checksum failure on %s",
+ short_pathname);
+
+ error (0, 0,
+ "checksum failure after patch to %s; will refetch",
+ short_pathname);
+
+ /* Save this file to retrieve later. */
+ failed_patches =
+ (char **) xrealloc ((char *) failed_patches,
+ ((failed_patches_count + 1)
+ * sizeof (char *)));
+ failed_patches[failed_patches_count] =
+ xstrdup (short_pathname);
+ ++failed_patches_count;
+
+ return;
+ }
+ }
+
+ {
+ /* FIXME: we should be respecting the umask. */
+ int status = change_mode (filename, mode_string);
+ if (status != 0)
+ error (0, status, "cannot change mode of %s", short_pathname);
+ }
+
+ free (mode_string);
+ free (buf);
+ }
+
+ /*
+ * Process the entries line. Do this after we've written the file,
+ * since we need the timestamp.
+ */
+ if (strcmp (command_name, "export") != 0)
+ {
+ char *local_timestamp;
+ char *file_timestamp;
+
+ local_timestamp = data->timestamp;
+ if (local_timestamp == NULL || ts[0] == '+')
+ file_timestamp = time_stamp (filename);
+ else
+ file_timestamp = NULL;
+
+ /*
+ * These special version numbers signify that it is not up to
+ * date. Create a dummy timestamp which will never compare
+ * equal to the timestamp of the file.
+ */
+ if (vn[0] == '\0' || vn[0] == '0' || vn[0] == '-')
+ local_timestamp = "dummy timestamp";
+ else if (local_timestamp == NULL)
+ local_timestamp = file_timestamp;
+
+ Register (ent_list, filename, vn, local_timestamp,
+ options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
+
+ if (file_timestamp)
+ free (file_timestamp);
+
+ free (scratch_entries);
+ }
+ free (entries_line);
+}
+
+static void
+handle_checked_in (args, len)
+ char *args;
+ int len;
+{
+ struct update_entries_data dat;
+ dat.contents = UPDATE_ENTRIES_CHECKIN;
+ dat.timestamp = NULL;
+ call_in_directory (args, update_entries, (char *)&dat);
+}
+
+static void
+handle_new_entry (args, len)
+ char *args;
+ int len;
+{
+ struct update_entries_data dat;
+ dat.contents = UPDATE_ENTRIES_CHECKIN;
+ dat.timestamp = "dummy timestamp from new-entry";
+ call_in_directory (args, update_entries, (char *)&dat);
+}
+
+static void
+handle_updated (args, len)
+ char *args;
+ int len;
+{
+ struct update_entries_data dat;
+ dat.contents = UPDATE_ENTRIES_UPDATE;
+ dat.timestamp = NULL;
+ call_in_directory (args, update_entries, (char *)&dat);
+}
+
+static void
+handle_merged (args, len)
+ char *args;
+ int len;
+{
+ struct update_entries_data dat;
+ dat.contents = UPDATE_ENTRIES_UPDATE;
+ dat.timestamp = "Result of merge";
+ call_in_directory (args, update_entries, (char *)&dat);
+}
+
+static void
+handle_patched (args, len)
+ char *args;
+ int len;
+{
+ struct update_entries_data dat;
+ dat.contents = UPDATE_ENTRIES_PATCH;
+ dat.timestamp = NULL;
+ call_in_directory (args, update_entries, (char *)&dat);
+}
+
+static void
+remove_entry (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ Scratch_Entry (ent_list, filename);
+}
+
+static void
+handle_remove_entry (args, len)
+ char *args;
+ int len;
+{
+ call_in_directory (args, remove_entry, (char *)NULL);
+}
+
+static void
+remove_entry_and_file (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ Scratch_Entry (ent_list, filename);
+ if (unlink_file (filename) < 0)
+ error (0, errno, "unable to remove %s", short_pathname);
+}
+
+static void
+handle_removed (args, len)
+ char *args;
+ int len;
+{
+ call_in_directory (args, remove_entry_and_file, (char *)NULL);
+}
+
+/* Is this the top level (directory containing CVSROOT)? */
+static int
+is_cvsroot_level (pathname)
+ char *pathname;
+{
+ char *short_pathname;
+
+ if (strcmp (toplevel_repos, server_cvsroot) != 0)
+ return 0;
+
+ if (!use_directory)
+ {
+ if (strncmp (pathname, server_cvsroot, strlen (server_cvsroot)) != 0)
+ error (1, 0,
+ "server bug: pathname `%s' doesn't specify file in `%s'",
+ pathname, server_cvsroot);
+ short_pathname = pathname + strlen (server_cvsroot) + 1;
+ if (short_pathname[-1] != '/')
+ error (1, 0,
+ "server bug: pathname `%s' doesn't specify file in `%s'",
+ pathname, server_cvsroot);
+ return strchr (short_pathname, '/') == NULL;
+ }
+ else
+ {
+ return strchr (pathname, '/') == NULL;
+ }
+}
+
+static void
+set_static (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ FILE *fp;
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose (fp) == EOF)
+ error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
+}
+
+static void
+handle_set_static_directory (args, len)
+ char *args;
+ int len;
+{
+ if (strcmp (command_name, "export") == 0)
+ {
+ /* Swallow the repository. */
+ read_line (NULL, 0);
+ return;
+ }
+ call_in_directory (args, set_static, (char *)NULL);
+}
+
+static void
+clear_static (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
+ error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
+}
+
+static void
+handle_clear_static_directory (pathname, len)
+ char *pathname;
+ int len;
+{
+ if (strcmp (command_name, "export") == 0)
+ {
+ /* Swallow the repository. */
+ read_line (NULL, 0);
+ return;
+ }
+
+ if (is_cvsroot_level (pathname))
+ {
+ /*
+ * Top level (directory containing CVSROOT). This seems to normally
+ * lack a CVS directory, so don't try to create files in it.
+ */
+ return;
+ }
+ call_in_directory (pathname, clear_static, (char *)NULL);
+}
+
+static void
+set_sticky (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ char *tagspec;
+ FILE *f;
+
+ read_line (&tagspec, 0);
+ f = open_file (CVSADM_TAG, "w+");
+ if (fprintf (f, "%s\n", tagspec) < 0)
+ error (1, errno, "writing %s", CVSADM_TAG);
+ if (fclose (f) == EOF)
+ error (1, errno, "closing %s", CVSADM_TAG);
+ free (tagspec);
+}
+
+static void
+handle_set_sticky (pathname, len)
+ char *pathname;
+ int len;
+{
+ if (strcmp (command_name, "export") == 0)
+ {
+ /* Swallow the repository. */
+ read_line (NULL, 0);
+ /* Swallow the tag line. */
+ (void) read_line (NULL, 0);
+ return;
+ }
+ if (is_cvsroot_level (pathname))
+ {
+ /*
+ * Top level (directory containing CVSROOT). This seems to normally
+ * lack a CVS directory, so don't try to create files in it.
+ */
+
+ /* Swallow the repository. */
+ read_line (NULL, 0);
+ /* Swallow the tag line. */
+ (void) read_line (NULL, 0);
+ return;
+ }
+
+ call_in_directory (pathname, set_sticky, (char *)NULL);
+}
+
+static void
+clear_sticky (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
+ error (1, errno, "cannot remove %s", CVSADM_TAG);
+}
+
+static void
+handle_clear_sticky (pathname, len)
+ char *pathname;
+ int len;
+{
+ if (strcmp (command_name, "export") == 0)
+ {
+ /* Swallow the repository. */
+ read_line (NULL, 0);
+ return;
+ }
+
+ if (is_cvsroot_level (pathname))
+ {
+ /*
+ * Top level (directory containing CVSROOT). This seems to normally
+ * lack a CVS directory, so don't try to create files in it.
+ */
+ return;
+ }
+
+ call_in_directory (pathname, clear_sticky, (char *)NULL);
+}
+
+struct save_prog {
+ char *name;
+ char *dir;
+ struct save_prog *next;
+};
+
+static struct save_prog *checkin_progs;
+static struct save_prog *update_progs;
+
+/*
+ * Unlike some requests this doesn't include the repository. So we can't
+ * just call call_in_directory and have the right thing happen; we save up
+ * the requests and do them at the end.
+ */
+static void
+handle_set_checkin_prog (args, len)
+ char *args;
+ int len;
+{
+ char *prog;
+ struct save_prog *p;
+ read_line (&prog, 0);
+ p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
+ p->next = checkin_progs;
+ p->dir = xstrdup (args);
+ p->name = prog;
+ checkin_progs = p;
+}
+
+static void
+handle_set_update_prog (args, len)
+ char *args;
+ int len;
+{
+ char *prog;
+ struct save_prog *p;
+ read_line (&prog, 0);
+ p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
+ p->next = update_progs;
+ p->dir = xstrdup (args);
+ p->name = prog;
+ update_progs = p;
+}
+
+static void do_deferred_progs PROTO((void));
+
+static void
+do_deferred_progs ()
+{
+ struct save_prog *p;
+ struct save_prog *q;
+
+ char fname[PATH_MAX];
+ FILE *f;
+ if (toplevel_wd[0] != '\0')
+ {
+ if (chdir (toplevel_wd) < 0)
+ error (1, errno, "could not chdir to %s", toplevel_wd);
+ }
+ for (p = checkin_progs; p != NULL; )
+ {
+ sprintf (fname, "%s/%s", p->dir, CVSADM_CIPROG);
+ f = open_file (fname, "w");
+ if (fprintf (f, "%s\n", p->name) < 0)
+ error (1, errno, "writing %s", fname);
+ if (fclose (f) == EOF)
+ error (1, errno, "closing %s", fname);
+ free (p->name);
+ free (p->dir);
+ q = p->next;
+ free (p);
+ p = q;
+ }
+ checkin_progs = NULL;
+ for (p = update_progs; p != NULL; p = p->next)
+ {
+ sprintf (fname, "%s/%s", p->dir, CVSADM_UPROG);
+ f = open_file (fname, "w");
+ if (fprintf (f, "%s\n", p->name) < 0)
+ error (1, errno, "writing %s", fname);
+ if (fclose (f) == EOF)
+ error (1, errno, "closing %s", fname);
+ free (p->name);
+ free (p->dir);
+ free (p);
+ }
+ update_progs = NULL;
+}
+
+static int client_isemptydir PROTO((char *));
+
+/*
+ * Returns 1 if the argument directory exists and is completely empty,
+ * other than the existence of the CVS directory entry. Zero otherwise.
+ */
+static int
+client_isemptydir (dir)
+ char *dir;
+{
+ DIR *dirp;
+ struct dirent *dp;
+
+ if ((dirp = opendir (dir)) == NULL)
+ {
+ if (! existence_error (errno))
+ error (0, errno, "cannot open directory %s for empty check", dir);
+ return (0);
+ }
+ errno = 0;
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 &&
+ strcmp (dp->d_name, CVSADM) != 0)
+ {
+ (void) closedir (dirp);
+ return (0);
+ }
+ }
+ if (errno != 0)
+ {
+ error (0, errno, "cannot read directory %s", dir);
+ (void) closedir (dirp);
+ return (0);
+ }
+ (void) closedir (dirp);
+ return (1);
+}
+
+struct save_dir {
+ char *dir;
+ struct save_dir *next;
+};
+
+struct save_dir *prune_candidates;
+
+static void
+add_prune_candidate (dir)
+ char *dir;
+{
+ struct save_dir *p;
+
+ if (dir[0] == '.' && dir[1] == '\0')
+ return;
+ p = (struct save_dir *) xmalloc (sizeof (struct save_dir));
+ p->dir = xstrdup (dir);
+ p->next = prune_candidates;
+ prune_candidates = p;
+}
+
+static void process_prune_candidates PROTO((void));
+
+static void
+process_prune_candidates ()
+{
+ struct save_dir *p;
+ struct save_dir *q;
+
+ if (toplevel_wd[0] != '\0')
+ {
+ if (chdir (toplevel_wd) < 0)
+ error (1, errno, "could not chdir to %s", toplevel_wd);
+ }
+ for (p = prune_candidates; p != NULL; )
+ {
+ if (client_isemptydir (p->dir))
+ {
+ unlink_file_dir (p->dir);
+ }
+ free (p->dir);
+ q = p->next;
+ free (p);
+ p = q;
+ }
+}
+
+/* Send a Repository line. */
+
+static char *last_repos;
+static char *last_update_dir;
+
+static void send_repository PROTO((char *, char *, char *));
+
+static void
+send_repository (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ char *adm_name;
+
+ if (update_dir == NULL || update_dir[0] == '\0')
+ update_dir = ".";
+
+ if (last_repos != NULL
+ && strcmp (repos, last_repos) == 0
+ && last_update_dir != NULL
+ && strcmp (update_dir, last_update_dir) == 0)
+ /* We've already sent it. */
+ return;
+
+ if (client_prune_dirs)
+ add_prune_candidate (update_dir);
+
+ /* 80 is large enough for any of CVSADM_*. */
+ adm_name = xmalloc (strlen (dir) + 80);
+
+ if (use_directory == -1)
+ use_directory = supported_request ("Directory");
+
+ if (use_directory)
+ {
+ if (fprintf (to_server, "Directory ") < 0)
+ error (1, errno, "writing to server");
+ if (fprintf (to_server, "%s", update_dir) < 0)
+ error (1, errno, "writing to server");
+
+ if (fprintf (to_server, "\n%s\n", repos)
+ < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ {
+ if (fprintf (to_server, "Repository %s\n", repos) < 0)
+ error (1, errno, "writing to server");
+ }
+ if (supported_request ("Static-directory"))
+ {
+ adm_name[0] = '\0';
+ if (dir[0] != '\0')
+ {
+ strcat (adm_name, dir);
+ strcat (adm_name, "/");
+ }
+ strcat (adm_name, CVSADM_ENTSTAT);
+ if (isreadable (adm_name))
+ {
+ if (fprintf (to_server, "Static-directory\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ }
+ if (supported_request ("Sticky"))
+ {
+ FILE *f;
+ if (dir[0] == '\0')
+ strcpy (adm_name, CVSADM_TAG);
+ else
+ sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
+
+ f = fopen (adm_name, "r");
+ if (f == NULL)
+ {
+ if (! existence_error (errno))
+ error (1, errno, "reading %s", adm_name);
+ }
+ else
+ {
+ char line[80];
+ char *nl;
+ if (fprintf (to_server, "Sticky ") < 0)
+ error (1, errno, "writing to server");
+ while (fgets (line, sizeof (line), f) != NULL)
+ {
+ if (fprintf (to_server, "%s", line) < 0)
+ error (1, errno, "writing to server");
+ nl = strchr (line, '\n');
+ if (nl != NULL)
+ break;
+ }
+ if (nl == NULL)
+ if (fprintf (to_server, "\n") < 0)
+ error (1, errno, "writing to server");
+ if (fclose (f) == EOF)
+ error (0, errno, "closing %s", adm_name);
+ }
+ }
+ if (supported_request ("Checkin-prog"))
+ {
+ FILE *f;
+ if (dir[0] == '\0')
+ strcpy (adm_name, CVSADM_CIPROG);
+ else
+ sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG);
+
+ f = fopen (adm_name, "r");
+ if (f == NULL)
+ {
+ if (! existence_error (errno))
+ error (1, errno, "reading %s", adm_name);
+ }
+ else
+ {
+ char line[80];
+ char *nl;
+ if (fprintf (to_server, "Checkin-prog ") < 0)
+ error (1, errno, "writing to server");
+ while (fgets (line, sizeof (line), f) != NULL)
+ {
+ if (fprintf (to_server, "%s", line) < 0)
+ error (1, errno, "writing to server");
+ nl = strchr (line, '\n');
+ if (nl != NULL)
+ break;
+ }
+ if (nl == NULL)
+ if (fprintf (to_server, "\n") < 0)
+ error (1, errno, "writing to server");
+ if (fclose (f) == EOF)
+ error (0, errno, "closing %s", adm_name);
+ }
+ }
+ if (supported_request ("Update-prog"))
+ {
+ FILE *f;
+ if (dir[0] == '\0')
+ strcpy (adm_name, CVSADM_UPROG);
+ else
+ sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG);
+
+ f = fopen (adm_name, "r");
+ if (f == NULL)
+ {
+ if (! existence_error (errno))
+ error (1, errno, "reading %s", adm_name);
+ }
+ else
+ {
+ char line[80];
+ char *nl;
+ if (fprintf (to_server, "Update-prog ") < 0)
+ error (1, errno, "writing to server");
+ while (fgets (line, sizeof (line), f) != NULL)
+ {
+ if (fprintf (to_server, "%s", line) < 0)
+ error (1, errno, "writing to server");
+ nl = strchr (line, '\n');
+ if (nl != NULL)
+ break;
+ }
+ if (nl == NULL)
+ if (fprintf (to_server, "\n") < 0)
+ error (1, errno, "writing to server");
+ if (fclose (f) == EOF)
+ error (0, errno, "closing %s", adm_name);
+ }
+ }
+ if (last_repos != NULL)
+ free (last_repos);
+ if (last_update_dir != NULL)
+ free (last_update_dir);
+ last_repos = xstrdup (repos);
+ last_update_dir = xstrdup (update_dir);
+}
+
+/* Send a Repository line and set toplevel_repos. */
+static void send_a_repository PROTO((char *, char *, char *));
+
+static void
+send_a_repository (dir, repository, update_dir)
+ char *dir;
+ char *repository;
+ char *update_dir;
+{
+ if (toplevel_repos == NULL && repository != NULL)
+ {
+ if (update_dir[0] == '\0'
+ || (update_dir[0] == '.' && update_dir[1] == '\0'))
+ toplevel_repos = xstrdup (repository);
+ else
+ {
+ /*
+ * Get the repository from a CVS/Repository file if update_dir
+ * is absolute. This is not correct in general, because
+ * the CVS/Repository file might not be the top-level one.
+ * This is for cases like "cvs update /foo/bar" (I'm not
+ * sure it matters what toplevel_repos we get, but it does
+ * matter that we don't hit the "internal error" code below).
+ */
+ if (update_dir[0] == '/')
+ toplevel_repos = Name_Repository (update_dir, update_dir);
+ else
+ {
+ /*
+ * Guess the repository of that directory by looking at a
+ * subdirectory and removing as many pathname components
+ * as are in update_dir. I think that will always (or at
+ * least almost always) be 1.
+ *
+ * So this deals with directories which have been
+ * renamed, though it doesn't necessarily deal with
+ * directories which have been put inside other
+ * directories (and cvs invoked on the containing
+ * directory). I'm not sure the latter case needs to
+ * work.
+ */
+ /*
+ * This gets toplevel_repos wrong for "cvs update ../foo"
+ * but I'm not sure toplevel_repos matters in that case.
+ */
+ int slashes_in_update_dir;
+ int slashes_skipped;
+ char *p;
+
+ slashes_in_update_dir = 0;
+ for (p = update_dir; *p != '\0'; ++p)
+ if (*p == '/')
+ ++slashes_in_update_dir;
+
+ slashes_skipped = 0;
+ p = repository + strlen (repository);
+ while (1)
+ {
+ if (p == repository)
+ error (1, 0,
+ "internal error: not enough slashes in %s",
+ repository);
+ if (*p == '/')
+ ++slashes_skipped;
+ if (slashes_skipped < slashes_in_update_dir + 1)
+ --p;
+ else
+ break;
+ }
+ toplevel_repos = xmalloc (p - repository + 1);
+ /* Note that we don't copy the trailing '/'. */
+ strncpy (toplevel_repos, repository, p - repository);
+ toplevel_repos[p - repository] = '\0';
+ }
+ }
+ }
+
+ send_repository (dir, repository, update_dir);
+}
+
+static int modules_count;
+static int modules_allocated;
+static char **modules_vector;
+
+static void
+handle_module_expansion (args, len)
+ char *args;
+ int len;
+{
+ if (modules_vector == NULL)
+ {
+ modules_allocated = 1; /* Small for testing */
+ modules_vector = (char **) xmalloc
+ (modules_allocated * sizeof (modules_vector[0]));
+ }
+ else if (modules_count >= modules_allocated)
+ {
+ modules_allocated *= 2;
+ modules_vector = (char **) xrealloc
+ ((char *) modules_vector,
+ modules_allocated * sizeof (modules_vector[0]));
+ }
+ modules_vector[modules_count] = xmalloc (strlen (args) + 1);
+ strcpy (modules_vector[modules_count], args);
+ ++modules_count;
+}
+
+void
+client_expand_modules (argc, argv, local)
+ int argc;
+ char **argv;
+ int local;
+{
+ int errs;
+ int i;
+
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ send_a_repository ("", server_cvsroot, "");
+ if (fprintf (to_server, "expand-modules\n") < 0)
+ error (1, errno, "writing to server");
+ errs = get_server_responses ();
+ if (last_repos != NULL)
+ free (last_repos);
+ last_repos = NULL;
+ if (last_update_dir != NULL)
+ free (last_update_dir);
+ last_update_dir = NULL;
+ if (errs)
+ error (errs, 0, "");
+}
+
+void
+client_send_expansions (local)
+ int local;
+{
+ int i;
+ char *argv[1];
+ for (i = 0; i < modules_count; ++i)
+ {
+ argv[0] = modules_vector[i];
+ if (isfile (argv[0]))
+ send_files (1, argv, local, 0);
+ else
+ send_file_names (1, argv);
+ }
+ send_a_repository ("", server_cvsroot, "");
+}
+
+void
+client_nonexpanded_setup ()
+{
+ send_a_repository ("", server_cvsroot, "");
+}
+
+static void
+handle_m (args, len)
+ char *args;
+ int len;
+{
+ fwrite (args, len, sizeof (*args), stdout);
+ putc ('\n', stdout);
+}
+
+static void
+handle_e (args, len)
+ char *args;
+ int len;
+{
+ fwrite (args, len, sizeof (*args), stderr);
+ putc ('\n', stderr);
+}
+
+#endif /* CLIENT_SUPPORT */
+#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
+
+/* This table must be writeable if the server code is included. */
+struct response responses[] =
+{
+#ifdef CLIENT_SUPPORT
+#define RSP_LINE(n, f, t, s) {n, f, t, s}
+#else /* ! CLIENT_SUPPORT */
+#define RSP_LINE(n, f, t, s) {n, s}
+#endif /* CLIENT_SUPPORT */
+
+ RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
+ RSP_LINE("error", handle_error, response_type_error, rs_essential),
+ RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
+ rs_essential),
+ RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
+ rs_essential),
+ RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
+ RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
+ RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
+ RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
+ RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
+ RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
+ RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
+ RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
+ rs_optional),
+ RSP_LINE("Set-static-directory", handle_set_static_directory,
+ response_type_normal,
+ rs_optional),
+ RSP_LINE("Clear-static-directory", handle_clear_static_directory,
+ response_type_normal,
+ rs_optional),
+ RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
+ rs_optional),
+ RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
+ rs_optional),
+ RSP_LINE("Set-checkin-prog", handle_set_checkin_prog, response_type_normal,
+ rs_optional),
+ RSP_LINE("Set-update-prog", handle_set_update_prog, response_type_normal,
+ rs_optional),
+ RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
+ rs_optional),
+ RSP_LINE("M", handle_m, response_type_normal, rs_essential),
+ RSP_LINE("E", handle_e, response_type_normal, rs_essential),
+ /* Possibly should be response_type_error. */
+ RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
+
+#undef RSP_LINE
+};
+
+#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
+#ifdef CLIENT_SUPPORT
+
+/*
+ * Get some server responses and process them. Returns nonzero for
+ * error, 0 for success.
+ */
+int
+get_server_responses ()
+{
+ struct response *rs;
+ do
+ {
+ char *cmd;
+ int len;
+
+ len = read_line (&cmd, 0);
+ for (rs = responses; rs->name != NULL; ++rs)
+ if (strncmp (cmd, rs->name, strlen (rs->name)) == 0)
+ {
+ int cmdlen = strlen (rs->name);
+ if (cmd[cmdlen] == '\0')
+ ;
+ else if (cmd[cmdlen] == ' ')
+ ++cmdlen;
+ else
+ /*
+ * The first len characters match, but it's a different
+ * response. e.g. the response is "oklahoma" but we
+ * matched "ok".
+ */
+ continue;
+ (*rs->func) (cmd + cmdlen, len - cmdlen);
+ break;
+ }
+ if (rs->name == NULL)
+ /* It's OK to print just to the first '\0'. */
+ error (0, 0,
+ "warning: unrecognized response `%s' from cvs server",
+ cmd);
+ free (cmd);
+ } while (rs->type == response_type_normal);
+ return rs->type == response_type_error ? 1 : 0;
+}
+
+/* Get the responses and then close the connection. */
+int server_fd = -1;
+
+int
+get_responses_and_close ()
+{
+ int errs = get_server_responses ();
+
+ do_deferred_progs ();
+
+ if (client_prune_dirs)
+ process_prune_candidates ();
+
+#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT)
+ if (server_fd != -1)
+ {
+ if (shutdown (server_fd, 1) < 0)
+ error (1, errno, "shutting down connection to %s", server_host);
+ /*
+ * In this case, both sides of the net connection will use the
+ * same fd.
+ */
+ if (fileno (from_server) != fileno (to_server))
+ {
+ if (fclose (to_server) != 0)
+ error (1, errno, "closing down connection to %s", server_host);
+ }
+ }
+ else
+#endif /* HAVE_KERBEROS || AUTH_CLIENT_SUPPORT */
+
+#ifdef SHUTDOWN_SERVER
+ SHUTDOWN_SERVER (fileno (to_server));
+#else /* ! SHUTDOWN_SERVER */
+ {
+
+#ifdef START_RSH_WITH_POPEN_RW
+ if (pclose (to_server) == EOF)
+#else /* ! START_RSH_WITH_POPEN_RW */
+ if (fclose (to_server) == EOF)
+#endif /* START_RSH_WITH_POPEN_RW */
+ {
+ error (1, errno, "closing connection to %s", server_host);
+ }
+ }
+
+ if (getc (from_server) != EOF)
+ error (0, 0, "dying gasps from %s unexpected", server_host);
+ else if (ferror (from_server))
+ error (0, errno, "reading from %s", server_host);
+
+ fclose (from_server);
+#endif /* SHUTDOWN_SERVER */
+
+#if ! RSH_NOT_TRANSPARENT
+ if (rsh_pid != -1
+ && waitpid (rsh_pid, (int *) 0, 0) == -1)
+ if (errno != ECHILD)
+ error (1, errno, "waiting for process %d", rsh_pid);
+#endif /* ! RSH_NOT_TRANSPARENT */
+
+ return errs;
+}
+
+#ifndef RSH_NOT_TRANSPARENT
+static void start_rsh_server PROTO((int *, int *));
+#endif /* RSH_NOT_TRANSPARENT */
+
+int
+supported_request (name)
+ char *name;
+{
+ struct request *rq;
+
+ for (rq = requests; rq->name; rq++)
+ if (!strcmp (rq->name, name))
+ return rq->status == rq_supported;
+ error (1, 0, "internal error: testing support for unknown option?");
+}
+
+
+#ifdef AUTH_CLIENT_SUPPORT
+void
+init_sockaddr (name, hostname, port)
+ struct sockaddr_in *name;
+ const char *hostname;
+ unsigned short int port;
+{
+ struct hostent *hostinfo;
+
+ name->sin_family = AF_INET;
+ name->sin_port = htons (port);
+ hostinfo = gethostbyname (hostname);
+ if (hostinfo == NULL)
+ {
+ fprintf (stderr, "Unknown host %s.\n", hostname);
+ exit (EXIT_FAILURE);
+ }
+ name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
+}
+
+void
+connect_to_pserver (tofdp, fromfdp, log)
+ int *tofdp, *fromfdp;
+ char *log;
+{
+ int sock;
+ int tofd, fromfd;
+ struct hostent *host;
+ struct sockaddr_in client_sai;
+
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+ if (sock == -1)
+ {
+ fprintf (stderr, "socket() failed\n");
+ exit (1);
+ }
+ init_sockaddr (&client_sai, server_host, CVS_AUTH_PORT);
+ connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai));
+
+ /* Run the authorization mini-protocol before anything else. */
+ {
+ int i;
+ char ch, read_buf[PATH_MAX];
+ char *begin = "BEGIN AUTH REQUEST\n";
+ char *repository = server_cvsroot;
+ char *username = server_user;
+ char *password = NULL;
+ char *end = "END AUTH REQUEST\n";
+
+ /* Get the password, probably from ~/.cvspass. */
+ password = get_cvs_password (server_user, server_host, server_cvsroot);
+
+ /* Announce that we're starting the authorization protocol. */
+ write (sock, begin, strlen (begin));
+
+ /* Send the data the server needs. */
+ write (sock, repository, strlen (repository));
+ write (sock, "\n", 1);
+ write (sock, username, strlen (username));
+ write (sock, "\n", 1);
+ write (sock, password, strlen (password));
+ write (sock, "\n", 1);
+
+ /* Announce that we're ending the authorization protocol. */
+ write (sock, end, strlen (end));
+
+ /* Paranoia. */
+ memset (password, 0, strlen (password));
+
+ /* Get ACK or NACK from the server.
+ *
+ * We could avoid this careful read-char loop by having the ACK
+ * and NACK cookies be of the same length, so we'd simply read
+ * that length and see what we got. But then there'd be Yet
+ * Another Protocol Requirement floating around, and someday
+ * someone would make a change that breaks it and spend a hellish
+ * day tracking it down. Therefore, we use "\n" to mark off the
+ * end of both ACK and NACK, and we read until "\n".
+ */
+ ch = 0;
+ memset (read_buf, 0, PATH_MAX);
+ for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++)
+ {
+ read (sock, &ch, 1);
+ read_buf[i] = ch;
+ }
+
+ if (strcmp (read_buf, "I HATE YOU\n") == 0)
+ {
+ /* Authorization not granted. */
+ if (shutdown (sock, 2) < 0)
+ error (1, errno, "shutdown() failed (server %s)", server_host);
+ error (1, 0,
+ "authorization failed: server %s rejected access",
+ server_host);
+ }
+ else if (strcmp (read_buf, "I LOVE YOU\n") != 0)
+ {
+ /* Unrecognized response from server. */
+ if (shutdown (sock, 2) < 0)
+ error (1, errno, "shutdown() failed (server %s)", server_host);
+ error (1, 0,
+ "unrecognized auth response from %s: %s",
+ server_host, read_buf);
+ }
+ /* Else authorization granted, so we can go on... */
+ }
+
+ /* This was stolen straight from start_kerberos_server(). */
+ {
+ server_fd = sock;
+ close_on_exec (server_fd);
+ /*
+ * If we do any filtering, TOFD and FROMFD will be
+ * closed. So make sure they're copies of SERVER_FD,
+ * and not the same fd number.
+ */
+ if (log)
+ {
+ tofd = dup (sock);
+ fromfd = dup (sock);
+ }
+ else
+ tofd = fromfd = sock;
+ }
+
+ /* Hand them back to the caller. */
+ *tofdp = tofd;
+ *fromfdp = fromfd;
+}
+#endif /* AUTH_CLIENT_SUPPORT */
+
+
+#if HAVE_KERBEROS
+void
+start_kerberos_server (tofdp, fromfdp, log)
+ int *tofdp, *fromfdp;
+ char *log;
+{
+ int tofd, fromfd;
+
+ struct hostent *hp;
+ char *hname;
+ const char *realm;
+ const char *portenv;
+ int port;
+ struct sockaddr_in sin;
+ int s;
+ KTEXT_ST ticket;
+ int status;
+
+ /*
+ * We look up the host to give a better error message if it
+ * does not exist. However, we then pass server_host to
+ * krb_sendauth, rather than the canonical name, because
+ * krb_sendauth is going to do its own canonicalization anyhow
+ * and that lets us not worry about the static storage used by
+ * gethostbyname.
+ */
+ hp = gethostbyname (server_host);
+ if (hp == NULL)
+ error (1, 0, "%s: unknown host", server_host);
+ hname = xmalloc (strlen (hp->h_name) + 1);
+ strcpy (hname, hp->h_name);
+
+ realm = krb_realmofhost (hname);
+
+ portenv = getenv ("CVS_CLIENT_PORT");
+ if (portenv != NULL)
+ {
+ port = atoi (portenv);
+ if (port <= 0)
+ goto try_rsh_no_message;
+ port = htons (port);
+ }
+ else
+ {
+ struct servent *sp;
+
+ sp = getservbyname ("cvs", "tcp");
+ if (sp == NULL)
+ port = htons (CVS_PORT);
+ else
+ port = sp->s_port;
+ }
+
+ s = socket (AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ error (1, errno, "socket");
+
+ memset (&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = 0;
+
+ if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ error (1, errno, "bind");
+
+ memcpy (&sin.sin_addr, hp->h_addr, hp->h_length);
+ sin.sin_port = port;
+
+ tofd = -1;
+ if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ {
+ error (0, errno, "connect");
+ close (s);
+ }
+ else
+ {
+ struct sockaddr_in laddr;
+ int laddrlen;
+ MSG_DAT msg_data;
+ CREDENTIALS cred;
+ Key_schedule sched;
+
+ laddrlen = sizeof (laddr);
+ if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
+ error (1, errno, "getsockname");
+
+ /* We don't care about the checksum, and pass it as zero. */
+ status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd",
+ hname, realm, (unsigned long) 0, &msg_data,
+ &cred, sched, &laddr, &sin, "KCVSV1.0");
+ if (status != KSUCCESS)
+ {
+ error (0, 0, "kerberos: %s", krb_get_err_text(status));
+ close (s);
+ }
+ else
+ {
+ server_fd = s;
+ close_on_exec (server_fd);
+ /*
+ * If we do any filtering, TOFD and FROMFD will be
+ * closed. So make sure they're copies of SERVER_FD,
+ * and not the same fd number.
+ */
+ if (log)
+ {
+ tofd = dup (s);
+ fromfd = dup (s);
+ }
+ else
+ tofd = fromfd = s;
+ }
+ }
+
+ if (tofd == -1)
+ {
+ error (0, 0, "trying to start server using rsh");
+ try_rsh_no_message:
+ server_fd = -1;
+#if ! RSH_NOT_TRANSPARENT
+ start_rsh_server (&tofd, &fromfd);
+#else /* RSH_NOT_TRANSPARENT */
+#if defined (START_SERVER)
+ START_SERVER (&tofd, &fromfd, getcaller (),
+ server_user, server_host, server_cvsroot);
+#endif /* defined (START_SERVER) */
+#endif /* ! RSH_NOT_TRANSPARENT */
+ }
+ free (hname);
+
+ /* Give caller the values it wants. */
+ *tofdp = tofd;
+ *fromfdp = fromfd;
+}
+
+#endif /* HAVE_KERBEROS */
+
+/* Contact the server. */
+void
+start_server ()
+{
+ int tofd, fromfd;
+ char *log = getenv ("CVS_CLIENT_LOG");
+
+#if HAVE_KERBEROS
+ start_kerberos_server (&tofd, &fromfd, log);
+
+#else /* ! HAVE_KERBEROS */
+
+#ifdef AUTH_CLIENT_SUPPORT
+ if (use_authenticating_server)
+ {
+ connect_to_pserver (&tofd, &fromfd, log);
+ }
+ else
+#endif /* AUTH_CLIENT_SUPPORT */
+ {
+#if ! RSH_NOT_TRANSPARENT
+ start_rsh_server (&tofd, &fromfd);
+#else /* RSH_NOT_TRANSPARENT */
+
+#if defined(START_SERVER)
+ START_SERVER (&tofd, &fromfd, getcaller (),
+ server_user, server_host, server_cvsroot);
+#endif /* defined(START_SERVER) */
+#endif /* ! RSH_NOT_TRANSPARENT */
+ }
+#endif /* HAVE_KERBEROS */
+
+ /* todo: some OS's don't need these calls... */
+ close_on_exec (tofd);
+ close_on_exec (fromfd);
+
+ if (log)
+ {
+ int len = strlen (log);
+ char *buf = xmalloc (5 + len);
+ char *p;
+ static char *teeprog[3] = { "tee" };
+
+ teeprog[1] = buf;
+ strcpy (buf, log);
+ p = buf + len;
+
+ strcpy (p, ".in");
+ tofd = filter_stream_through_program (tofd, 0, teeprog, 0);
+
+ strcpy (p, ".out");
+ fromfd = filter_stream_through_program (fromfd, 1, teeprog, 0);
+
+ free (buf);
+ }
+
+ /* These will use binary mode on systems which have it. */
+ to_server = fdopen (tofd, FOPEN_BINARY_WRITE);
+ if (to_server == NULL)
+ error (1, errno, "cannot fdopen %d for write", tofd);
+ from_server = fdopen (fromfd, FOPEN_BINARY_READ);
+ if (from_server == NULL)
+ error (1, errno, "cannot fdopen %d for read", fromfd);
+
+ /* Clear static variables. */
+ if (toplevel_repos != NULL)
+ free (toplevel_repos);
+ toplevel_repos = NULL;
+ if (last_dirname != NULL)
+ free (last_dirname);
+ last_dirname = NULL;
+ if (last_repos != NULL)
+ free (last_repos);
+ last_repos = NULL;
+ if (last_update_dir != NULL)
+ free (last_update_dir);
+ last_update_dir = NULL;
+ stored_checksum_valid = 0;
+
+ if (fprintf (to_server, "Root %s\n", server_cvsroot) < 0)
+ error (1, errno, "writing to server");
+ {
+ struct response *rs;
+ if (fprintf (to_server, "Valid-responses") < 0)
+ error (1, errno, "writing to server");
+ for (rs = responses; rs->name != NULL; ++rs)
+ {
+ if (fprintf (to_server, " %s", rs->name) < 0)
+ error (1, errno, "writing to server");
+ }
+ if (fprintf (to_server, "\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ if (fprintf (to_server, "valid-requests\n") < 0)
+ error (1, errno, "writing to server");
+ if (get_server_responses ())
+ exit (1);
+
+ /*
+ * Now handle global options.
+ *
+ * -H, -f, -d, -e should be handled OK locally.
+ *
+ * -b we ignore (treating it as a server installation issue).
+ * FIXME: should be an error message.
+ *
+ * -v we print local version info; FIXME: Add a protocol request to get
+ * the version from the server so we can print that too.
+ *
+ * -l -t -r -w -q -n and -Q need to go to the server.
+ */
+
+ {
+ int have_global = supported_request ("Global_option");
+
+ if (noexec)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -n\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -n option.");
+ }
+ if (quiet)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -q\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -q option.");
+ }
+ if (really_quiet)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -Q\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -Q option.");
+ }
+ if (!cvswrite)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -r\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -r option.");
+ }
+ if (trace)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -t\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -t option.");
+ }
+ if (logoff)
+ {
+ if (have_global)
+ {
+ if (fprintf (to_server, "Global_option -l\n") < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ error (1, 0,
+ "This server does not support the global -l option.");
+ }
+ }
+ if (gzip_level)
+ {
+ if (supported_request ("gzip-file-contents"))
+ {
+ if (fprintf (to_server, "gzip-file-contents %d\n", gzip_level) < 0)
+ error (1, 0, "writing to server");
+ }
+ else
+ {
+ fprintf (stderr, "server doesn't support gzip-file-contents\n");
+ gzip_level = 0;
+ }
+ }
+}
+
+#ifndef RSH_NOT_TRANSPARENT
+/* Contact the server by starting it with rsh. */
+
+/* Right now, we have two different definitions for this function,
+ depending on whether we start the rsh server using popenRW or not.
+ This isn't ideal, and the best thing would probably be to change
+ the OS/2 port to be more like the regular Unix client (i.e., by
+ implementing piped_child)... but I'm doing something else at the
+ moment, and wish to make only one change at a time. -Karl */
+
+#ifdef START_RSH_WITH_POPEN_RW
+
+static void
+start_rsh_server (tofdp, fromfdp)
+ int *tofdp, *fromfdp;
+{
+ int pipes[2];
+
+ /* If you're working through firewalls, you can set the
+ CVS_RSH environment variable to a script which uses rsh to
+ invoke another rsh on a proxy machine. */
+ char *cvs_rsh = getenv ("CVS_RSH");
+ char *cvs_server = getenv ("CVS_SERVER");
+ char command[PATH_MAX];
+ int i = 0;
+ /* This needs to fit "rsh", "-b", "-l", "USER", "host",
+ "cmd (w/ args)", and NULL. We leave some room to grow. */
+ char *rsh_argv[10];
+
+ if (!cvs_rsh)
+ cvs_rsh = "rsh";
+ if (!cvs_server)
+ cvs_server = "cvs";
+
+ /* If you are running a very old (Nov 3, 1994, before 1.5)
+ * version of the server, you need to make sure that your .bashrc
+ * on the server machine does not set CVSROOT to something
+ * containing a colon (or better yet, upgrade the server). */
+
+ /* The command line starts out with rsh. */
+ rsh_argv[i++] = cvs_rsh;
+
+ /* "-b" for binary, under OS/2. */
+ rsh_argv[i++] = "-b";
+
+ /* Then we strcat more things on the end one by one. */
+ if (server_user != NULL)
+ {
+ rsh_argv[i++] = "-l";
+ rsh_argv[i++] = server_user;
+ }
+
+ rsh_argv[i++] = server_host;
+ rsh_argv[i++] = cvs_server;
+ rsh_argv[i++] = "server";
+
+ /* Mark the end of the arg list. */
+ rsh_argv[i] = (char *) NULL;
+
+ if (trace)
+ {
+ fprintf (stderr, " -> Starting server: ");
+ fprintf (stderr, "%s", command);
+ putc ('\n', stderr);
+ }
+
+ /* Do the deed. */
+ rsh_pid = popenRW (rsh_argv, pipes);
+ if (rsh_pid < 0)
+ error (1, errno, "cannot start server via rsh");
+
+ /* Give caller the file descriptors. */
+ *tofdp = pipes[0];
+ *fromfdp = pipes[1];
+}
+
+#else /* ! START_RSH_WITH_POPEN_RW */
+
+static void
+start_rsh_server (tofdp, fromfdp)
+ int *tofdp;
+ int *fromfdp;
+{
+ /* If you're working through firewalls, you can set the
+ CVS_RSH environment variable to a script which uses rsh to
+ invoke another rsh on a proxy machine. */
+ char *cvs_rsh = getenv ("CVS_RSH");
+ char *cvs_server = getenv ("CVS_SERVER");
+ char *command;
+
+ if (!cvs_rsh)
+ cvs_rsh = "rsh";
+ if (!cvs_server)
+ cvs_server = "cvs";
+
+ /* Pass the command to rsh as a single string. This shouldn't
+ affect most rsh servers at all, and will pacify some buggy
+ versions of rsh that grab switches out of the middle of the
+ command (they're calling the GNU getopt routines incorrectly). */
+ command = xmalloc (strlen (cvs_server)
+ + strlen (server_cvsroot)
+ + 50);
+
+ /* If you are running a very old (Nov 3, 1994, before 1.5)
+ * version of the server, you need to make sure that your .bashrc
+ * on the server machine does not set CVSROOT to something
+ * containing a colon (or better yet, upgrade the server). */
+ sprintf (command, "%s server", cvs_server);
+
+ {
+ char *argv[10];
+ char **p = argv;
+
+ *p++ = cvs_rsh;
+ *p++ = server_host;
+
+ /* If the login names differ between client and server
+ * pass it on to rsh.
+ */
+ if (server_user != NULL)
+ {
+ *p++ = "-l";
+ *p++ = server_user;
+ }
+
+ *p++ = command;
+ *p++ = NULL;
+
+ if (trace)
+ {
+ int i;
+
+ fprintf (stderr, " -> Starting server: ");
+ for (i = 0; argv[i]; i++)
+ fprintf (stderr, "%s ", argv[i]);
+ putc ('\n', stderr);
+ }
+ rsh_pid = piped_child (argv, tofdp, fromfdp);
+
+ if (rsh_pid < 0)
+ error (1, errno, "cannot start server via rsh");
+ }
+}
+
+#endif /* START_RSH_WITH_POPEN_RW */
+#endif /* ! RSH_NOT_TRANSPARENT */
+
+
+
+/* Send an argument STRING. */
+void
+send_arg (string)
+ char *string;
+{
+ char *p = string;
+ if (fprintf (to_server, "Argument ") < 0)
+ error (1, errno, "writing to server");
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ if (fprintf (to_server, "\nArgumentx ") < 0)
+ error (1, errno, "writing to server");
+ }
+ else if (putc (*p, to_server) == EOF)
+ error (1, errno, "writing to server");
+ ++p;
+ }
+ if (putc ('\n', to_server) == EOF)
+ error (1, errno, "writing to server");
+}
+
+static void send_modified PROTO ((char *, char *, Vers_TS *));
+
+static void
+send_modified (file, short_pathname, vers)
+ char *file;
+ char *short_pathname;
+ Vers_TS *vers;
+{
+ /* File was modified, send it. */
+ struct stat sb;
+ int fd;
+ char *buf;
+ char *mode_string;
+ int bufsize;
+ int bin = 0;
+
+ /* Don't think we can assume fstat exists. */
+ if (stat (file, &sb) < 0)
+ error (1, errno, "reading %s", short_pathname);
+
+ mode_string = mode_to_string (sb.st_mode);
+
+ /* Beware: on systems using CRLF line termination conventions,
+ the read and write functions will convert CRLF to LF, so the
+ number of characters read is not the same as sb.st_size. Text
+ files should always be transmitted using the LF convention, so
+ we don't want to disable this conversion. */
+ bufsize = sb.st_size;
+ buf = xmalloc (bufsize);
+
+ /* Is the file marked as containing binary data by the "-kb" flag?
+ If so, make sure to open it in binary mode: */
+
+ bin = !(strcmp (vers->options, "-kb"));
+ fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
+
+ if (fd < 0)
+ error (1, errno, "reading %s", short_pathname);
+
+ if (gzip_level && sb.st_size > 100)
+ {
+ int nread, newsize = 0, gzip_status;
+ pid_t gzip_pid;
+ char *bufp = buf;
+ int readsize = 8192;
+#ifdef LINES_CRLF_TERMINATED
+ char tempfile[L_tmpnam];
+ int converting;
+#endif /* LINES_CRLF_TERMINATED */
+
+#ifdef LINES_CRLF_TERMINATED
+ /* Assume everything in a "cvs import" is text. */
+ if (vers == NULL)
+ converting = 1;
+ else
+ /* Otherwise, we convert things unless they're binary. */
+ converting = (! bin);
+
+ if (converting)
+ {
+ /* gzip reads and writes files without munging CRLF
+ sequences, as it should, but files should be
+ transmitted in LF form. Convert CRLF to LF before
+ gzipping, on systems where this is necessary.
+
+ If Windows NT supported fork, we could do this by
+ pushing another filter on in front of gzip. But it
+ doesn't. I'd have to write a trivial little program to
+ do the conversion and have CVS spawn it off. But
+ little executables like that always get lost.
+
+ Alternatively, this cruft could go away if we switched
+ to a gzip library instead of a subprocess; then we
+ could tell gzip to open the file with CRLF translation
+ enabled. */
+ if (close (fd) < 0)
+ error (0, errno, "warning: can't close %s", short_pathname);
+
+ tmpnam (tempfile);
+ convert_file (file, O_RDONLY,
+ tempfile,
+ O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
+
+ /* This OPEN_BINARY doesn't make any difference, I think, because
+ gzip will deal with the inherited handle as it pleases. But I
+ do remember something obscure in the manuals about propagating
+ the translation mode to created processes via environment
+ variables, ick. */
+ fd = open (tempfile, O_RDONLY | OPEN_BINARY);
+ if (fd < 0)
+ error (1, errno, "reading %s", short_pathname);
+ }
+#endif /* LINES_CRLF_TERMINATED */
+
+ fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
+ while (1)
+ {
+ if ((bufp - buf) + readsize >= bufsize)
+ {
+ /*
+ * We need to expand the buffer if gzip ends up expanding
+ * the file.
+ */
+ newsize = bufp - buf;
+ while (newsize + readsize >= bufsize)
+ bufsize *= 2;
+ buf = xrealloc (buf, bufsize);
+ bufp = buf + newsize;
+ }
+ nread = read (fd, bufp, readsize);
+ if (nread < 0)
+ error (1, errno, "reading from gzip pipe");
+ else if (nread == 0)
+ /* eof */
+ break;
+ bufp += nread;
+ }
+ newsize = bufp - buf;
+ if (close (fd) < 0)
+ error (0, errno, "warning: can't close %s", short_pathname);
+
+ if (waitpid (gzip_pid, &gzip_status, 0) != gzip_pid)
+ error (1, errno, "waiting for gzip proc %d", gzip_pid);
+ else if (gzip_status != 0)
+ error (1, errno, "gzip exited %d", gzip_status);
+
+#if LINES_CRLF_TERMINATED
+ if (converting)
+ {
+ if (unlink (tempfile) < 0)
+ error (0, errno,
+ "warning: can't remove temp file %s", tempfile);
+ }
+#endif /* LINES_CRLF_TERMINATED */
+
+ fprintf (to_server, "Modified %s\n%s\nz%lu\n", file, mode_string,
+ (unsigned long) newsize);
+ fwrite (buf, newsize, 1, to_server);
+ if (feof (to_server) || ferror (to_server))
+ error (1, errno, "writing to server");
+ }
+ else
+ {
+ int newsize;
+
+ {
+ char *bufp = buf;
+ int len;
+
+ while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
+ bufp += len;
+
+ if (len < 0)
+ error (1, errno, "reading %s", short_pathname);
+
+ newsize = bufp - buf;
+ }
+ if (close (fd) < 0)
+ error (0, errno, "warning: can't close %s", short_pathname);
+
+ if (fprintf (to_server, "Modified %s\n%s\n%lu\n", file,
+ mode_string, (unsigned long) newsize) < 0)
+ error (1, errno, "writing to server");
+
+ /*
+ * Note that this only ends with a newline if the file ended with
+ * one.
+ */
+ if (newsize > 0)
+ if (fwrite (buf, newsize, 1, to_server) != 1)
+ error (1, errno, "writing to server");
+ }
+ free (buf);
+ free (mode_string);
+}
+
+/* Deal with one file. */
+static int
+send_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Vers_TS *vers;
+ int update_dir_len = strlen (update_dir);
+ char *short_pathname = xmalloc (update_dir_len + strlen (file) + 40);
+ strcpy (short_pathname, update_dir);
+ if (update_dir[0] != '\0')
+ strcat (short_pathname, "/");
+ strcat (short_pathname, file);
+
+ send_a_repository ("", repository, update_dir);
+
+ vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL,
+ file, 0, 0, entries, (List *)NULL);
+
+ if (vers->vn_user != NULL)
+ {
+ /* The Entries request. */
+ /* Not sure about whether this deals with -k and stuff right. */
+ if (fprintf (to_server, "Entry /%s/%s/%s%s/%s/", file, vers->vn_user,
+ vers->ts_conflict == NULL ? "" : "+",
+ (vers->ts_conflict == NULL ? ""
+ : (vers->ts_user != NULL &&
+ strcmp (vers->ts_conflict, vers->ts_user) == 0
+ ? "="
+ : "modified")),
+ vers->options) < 0)
+ error (1, errno, "writing to server");
+ if (vers->entdata != NULL && vers->entdata->tag)
+ {
+ if (fprintf (to_server, "T%s", vers->entdata->tag) < 0)
+ error (1, errno, "writing to server");
+ }
+ else if (vers->entdata != NULL && vers->entdata->date)
+ if (fprintf (to_server, "D%s", vers->entdata->date) < 0)
+ error (1, errno, "writing to server");
+ if (fprintf (to_server, "\n") < 0)
+ error (1, errno, "writing to server");
+ }
+
+ if (vers->ts_user == NULL)
+ {
+ /*
+ * Do we want to print "file was lost" like normal CVS?
+ * Would it always be appropriate?
+ */
+ /* File no longer exists. */
+ if (!use_unchanged)
+ {
+ /* if the server is old, use the old request... */
+ if (fprintf (to_server, "Lost %s\n", file) < 0)
+ error (1, errno, "writing to server");
+ /*
+ * Otherwise, don't do anything for missing files,
+ * they just happen.
+ */
+ }
+ }
+ else if (vers->ts_rcs == NULL
+ || strcmp (vers->ts_user, vers->ts_rcs) != 0)
+ {
+ send_modified (file, short_pathname, vers);
+ }
+ else
+ {
+ /* Only use this request if the server supports it... */
+ if (use_unchanged)
+ if (fprintf (to_server, "Unchanged %s\n", file) < 0)
+ error (1, errno, "writing to server");
+ }
+
+ /* if this directory has an ignore list, add this file to it */
+ if (ignlist)
+ {
+ Node *p;
+
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (file);
+ (void) addnode (ignlist, p);
+ }
+
+ freevers_ts (&vers);
+ free (short_pathname);
+ return 0;
+}
+
+/*
+ * send_dirent_proc () is called back by the recursion processor before a
+ * sub-directory is processed for update.
+ * A return code of 0 indicates the directory should be
+ * processed by the recursion code. A return of non-zero indicates the
+ * recursion code should skip this directory.
+ *
+ */
+static Dtype
+send_dirent_proc (dir, repository, update_dir)
+ char *dir;
+ char *repository;
+ char *update_dir;
+{
+ int dir_exists;
+ char *cvsadm_repos_name;
+
+ /*
+ * If the directory does not exist yet (e.g. "cvs update -d
+ * foo"), no need to send any files from it.
+ */
+ dir_exists = isdir (dir);
+
+ if (ignore_directory (update_dir))
+ {
+ /* print the warm fuzzy message */
+ if (!quiet)
+ error (0, 0, "Ignoring %s", update_dir);
+ return (R_SKIP_ALL);
+ }
+
+ /* initialize the ignore list for this directory */
+ ignlist = getlist ();
+
+ /*
+ * If there is an empty directory (e.g. we are doing `cvs add' on a
+ * newly-created directory), the server still needs to know about it.
+ */
+
+ cvsadm_repos_name = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 80);
+ sprintf (cvsadm_repos_name, "%s/%s", dir, CVSADM_REP);
+ if (dir_exists && isreadable (cvsadm_repos_name))
+ {
+ /*
+ * Get the repository from a CVS/Repository file whenever possible.
+ * The repository variable is wrong if the names in the local
+ * directory don't match the names in the repository.
+ */
+ char *repos = Name_Repository (dir, update_dir);
+ send_a_repository (dir, repos, update_dir);
+ free (repos);
+ }
+ else
+ send_a_repository (dir, repository, update_dir);
+ free (cvsadm_repos_name);
+
+ return (dir_exists ? R_PROCESS : R_SKIP_ALL);
+}
+
+/*
+ * Send each option in a string to the server, one by one.
+ * This assumes that the options are single characters. For
+ * more complex parsing, do it yourself.
+ */
+
+void
+send_option_string (string)
+ char *string;
+{
+ char *p;
+ char it[3];
+
+ for (p = string; p[0]; p++) {
+ if (p[0] == ' ')
+ continue;
+ if (p[0] == '-')
+ continue;
+ it[0] = '-';
+ it[1] = p[0];
+ it[2] = '\0';
+ send_arg (it);
+ }
+}
+
+
+/* Send the names of all the argument files to the server. */
+
+void
+send_file_names (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ char *p;
+ char *q;
+ int level;
+ int max_level;
+
+ /* Send Max-dotdot if needed. */
+ max_level = 0;
+ for (i = 0; i < argc; ++i)
+ {
+ p = argv[i];
+ level = 0;
+ do
+ {
+ q = strchr (p, '/');
+ if (q != NULL)
+ ++q;
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || p[2] == '/'))
+ {
+ --level;
+ if (-level > max_level)
+ max_level = -level;
+ }
+ else if (p[0] == '.' && (p[1] == '\0' || p[1] == '/'))
+ ;
+ else
+ ++level;
+ p = q;
+ } while (p != NULL);
+ }
+ if (max_level > 0)
+ {
+ if (supported_request ("Max-dotdot"))
+ {
+ if (fprintf (to_server, "Max-dotdot %d\n", max_level) < 0)
+ error (1, errno, "writing to server");
+ }
+ else
+ /*
+ * "leading .." is not strictly correct, as this also includes
+ * cases like "foo/../..". But trying to explain that in the
+ * error message would probably just confuse users.
+ */
+ error (1, 0,
+ "leading .. not supported by old (pre-Max-dotdot) servers");
+ }
+
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+}
+
+
+/*
+ * Send Repository, Modified and Entry. argc and argv contain only
+ * the files to operate on (or empty for everything), not options.
+ * local is nonzero if we should not recurse (-l option). Also sends
+ * Argument lines for argc and argv, so should be called after options
+ * are sent.
+ */
+void
+send_files (argc, argv, local, aflag)
+ int argc;
+ char **argv;
+ int local;
+ int aflag;
+{
+ int err;
+
+ send_file_names (argc, argv);
+
+ /*
+ * aflag controls whether the tag/date is copied into the vers_ts.
+ * But we don't actually use it, so I don't think it matters what we pass
+ * for aflag here.
+ */
+ err = start_recursion
+ (send_fileproc, update_filesdone_proc,
+ send_dirent_proc, (DIRLEAVEPROC)NULL,
+ argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0);
+ if (err)
+ exit (1);
+ if (toplevel_repos == NULL)
+ /*
+ * This happens if we are not processing any files,
+ * or for checkouts in directories without any existing stuff
+ * checked out. The following assignment is correct for the
+ * latter case; I don't think toplevel_repos matters for the
+ * former.
+ */
+ toplevel_repos = xstrdup (server_cvsroot);
+ send_repository ("", toplevel_repos, ".");
+}
+
+void
+client_import_setup (repository)
+ char *repository;
+{
+ if (toplevel_repos == NULL) /* should always be true */
+ send_a_repository ("", repository, "");
+}
+
+/*
+ * Process the argument import file.
+ */
+int
+client_process_import_file (message, vfile, vtag, targc, targv, repository)
+ char *message;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+ char *repository;
+{
+ char *short_pathname;
+ int first_time;
+
+ /* FIXME: I think this is always false now that we call
+ client_import_setup at the start. */
+
+ first_time = toplevel_repos == NULL;
+
+ if (first_time)
+ send_a_repository ("", repository, "");
+
+ if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)) != 0)
+ error (1, 0,
+ "internal error: pathname `%s' doesn't specify file in `%s'",
+ repository, toplevel_repos);
+ short_pathname = repository + strlen (toplevel_repos) + 1;
+
+ if (!first_time)
+ {
+ send_a_repository ("", repository, short_pathname);
+ }
+ send_modified (vfile, short_pathname, NULL);
+ return 0;
+}
+
+void
+client_import_done ()
+{
+ if (toplevel_repos == NULL)
+ /*
+ * This happens if we are not processing any files,
+ * or for checkouts in directories without any existing stuff
+ * checked out. The following assignment is correct for the
+ * latter case; I don't think toplevel_repos matters for the
+ * former.
+ */
+ /* FIXME: "can't happen" now that we call client_import_setup
+ at the beginning. */
+ toplevel_repos = xstrdup (server_cvsroot);
+ send_repository ("", toplevel_repos, ".");
+}
+
+/*
+ * Send an option with an argument, dealing correctly with newlines in
+ * the argument. If ARG is NULL, forget the whole thing.
+ */
+void
+option_with_arg (option, arg)
+ char *option;
+ char *arg;
+{
+ if (arg == NULL)
+ return;
+ if (fprintf (to_server, "Argument %s\n", option) < 0)
+ error (1, errno, "writing to server");
+ send_arg (arg);
+}
+
+/*
+ * Send a date to the server. This will passed a string which is the
+ * result of Make_Date, and looks like YY.MM.DD.HH.MM.SS, where all
+ * the letters are single digits. The time will be GMT. getdate on
+ * the server can't parse that, so we turn it back into something
+ * which it can parse.
+ */
+
+void
+client_senddate (date)
+ const char *date;
+{
+ int year, month, day, hour, minute, second;
+ char buf[100];
+
+ if (sscanf (date, DATEFORM, &year, &month, &day, &hour, &minute, &second)
+ != 6)
+ {
+ error (1, 0, "diff_client_senddate: sscanf failed on date");
+ }
+
+#ifndef HAVE_RCS5
+ /* We need to fix the timezone in this case; see Make_Date. */
+ abort ();
+#endif /* HAVE_RCS5 */
+
+ sprintf (buf, "%d/%d/%d %d:%d:%d GMT", month, day, year,
+ hour, minute, second);
+ option_with_arg ("-D", buf);
+}
+
+int
+client_commit (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return commit (argc, argv);
+}
+
+int
+client_update (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return update (argc, argv);
+}
+
+int
+client_checkout (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return checkout (argc, argv);
+}
+
+int
+client_diff (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return diff (argc, argv); /* Call real code */
+}
+
+int
+client_status (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+ return status (argc, argv);
+}
+
+int
+client_log (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return cvslog (argc, argv); /* Call real code */
+}
+
+int
+client_add (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return add (argc, argv); /* Call real code */
+}
+
+int
+client_remove (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return cvsremove (argc, argv); /* Call real code */
+}
+
+int
+client_rdiff (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return patch (argc, argv); /* Call real code */
+}
+
+int
+client_tag (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return tag (argc, argv); /* Call real code */
+}
+
+int
+client_rtag (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return rtag (argc, argv); /* Call real code */
+}
+
+int
+client_import (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return import (argc, argv); /* Call real code */
+}
+
+int
+client_admin (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return admin (argc, argv); /* Call real code */
+}
+
+int
+client_export (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return checkout (argc, argv); /* Call real code */
+}
+
+int
+client_history (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return history (argc, argv); /* Call real code */
+}
+
+int
+client_release (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return release (argc, argv); /* Call real code */
+}
+
+#endif /* CLIENT_SUPPORT */
diff --git a/gnu/usr.bin/cvs/cvs/client.h b/gnu/usr.bin/cvs/cvs/client.h
new file mode 100644
index 000000000000..0602900aaf50
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/client.h
@@ -0,0 +1,163 @@
+/* Interface between the client and the rest of CVS. */
+
+/* Stuff shared with the server. */
+extern char *mode_to_string PROTO((mode_t));
+extern int change_mode PROTO((char *, char *));
+
+extern int gzip_level;
+extern int filter_through_gzip PROTO((int, int, int, pid_t *));
+extern int filter_through_gunzip PROTO((int, int, pid_t *));
+
+#ifdef CLIENT_SUPPORT
+/*
+ * Functions to perform CVS commands via the protocol. argc and argv
+ * are the arguments and the return value is the exit status (zero success
+ * nonzero failure).
+ */
+extern int client_commit PROTO((int argc, char **argv));
+extern int client_update PROTO((int argc, char **argv));
+extern int client_checkout PROTO((int argc, char **argv));
+extern int client_diff PROTO((int argc, char **argv));
+extern int client_log PROTO((int argc, char **argv));
+extern int client_add PROTO((int argc, char **argv));
+extern int client_remove PROTO((int argc, char **argv));
+extern int client_status PROTO((int argc, char **argv));
+extern int client_rdiff PROTO((int argc, char **argv));
+extern int client_tag PROTO((int argc, char **argv));
+extern int client_rtag PROTO((int argc, char **argv));
+extern int client_import PROTO((int argc, char **argv));
+extern int client_admin PROTO((int argc, char **argv));
+extern int client_export PROTO((int argc, char **argv));
+extern int client_history PROTO((int argc, char **argv));
+extern int client_release PROTO((int argc, char **argv));
+
+/*
+ * Flag variable for seeing whether common code is running as a client
+ * or to do a local operation.
+ */
+extern int client_active;
+
+/* Is the -P option to checkout or update specified? */
+extern int client_prune_dirs;
+
+/* Stream to write to the server. */
+extern FILE *to_server;
+/* Stream to read from the server. */
+extern FILE *from_server;
+
+/* Internal functions that handle client communication to server, etc. */
+int supported_request PROTO ((char *));
+void option_with_arg PROTO((char *option, char *arg));
+
+/* Get the responses and then close the connection. */
+extern int get_responses_and_close PROTO((void));
+
+extern int get_server_responses PROTO((void));
+
+/* Start up the connection to the server on the other end. */
+void
+start_server PROTO((void));
+
+/* Send the names of all the argument files to the server. */
+void
+send_file_names PROTO((int argc, char **argv));
+
+/*
+ * Send Repository, Modified and Entry. argc and argv contain only
+ * the files to operate on (or empty for everything), not options.
+ * local is nonzero if we should not recurse (-l option). Also sends
+ * Argument lines for argc and argv, so should be called after options
+ * are sent.
+ */
+void
+send_files PROTO((int argc, char **argv, int local, int aflag));
+
+/*
+ * Like send_files but never send "Unchanged"--just send the contents of the
+ * file in that case. This is used to fix it if you import a directory which
+ * happens to have CVS directories (yes it is obscure but the testsuite tests
+ * it).
+ */
+void
+send_files_contents PROTO((int argc, char **argv, int local, int aflag));
+
+/* Send an argument to the remote server. */
+void
+send_arg PROTO((char *string));
+
+/* Send a string of single-char options to the remote server, one by one. */
+void
+send_option_string PROTO((char *string));
+
+#endif /* CLIENT_SUPPORT */
+
+/*
+ * This structure is used to catalog the responses the client is
+ * prepared to see from the server.
+ */
+
+struct response
+{
+ /* Name of the response. */
+ char *name;
+
+#ifdef CLIENT_SUPPORT
+ /*
+ * Function to carry out the response. ARGS is the text of the
+ * command after name and, if present, a single space, have been
+ * stripped off. The function can scribble into ARGS if it wants.
+ */
+ void (*func) PROTO((char *args, int len));
+
+ /*
+ * ok and error are special; they indicate we are at the end of the
+ * responses, and error indicates we should exit with nonzero
+ * exitstatus.
+ */
+ enum {response_type_normal, response_type_ok, response_type_error} type;
+#endif
+
+ /* Used by the server to indicate whether response is supported by
+ the client, as set by the Valid-responses request. */
+ enum {
+ /*
+ * Failure to implement this response can imply a fatal
+ * error. This should be set only for responses which were in the
+ * original version of the protocol; it should not be set for new
+ * responses.
+ */
+ rs_essential,
+
+ /* Some clients might not understand this response. */
+ rs_optional,
+
+ /*
+ * Set by the server to one of the following based on what this
+ * client actually supports.
+ */
+ rs_supported,
+ rs_not_supported
+ } status;
+};
+
+/* Table of responses ending in an entry with a NULL name. */
+
+extern struct response responses[];
+
+#ifdef CLIENT_SUPPORT
+
+extern void client_senddate PROTO((const char *date));
+extern void client_expand_modules PROTO((int argc, char **argv, int local));
+extern void client_send_expansions PROTO((int local));
+extern void client_nonexpanded_setup PROTO((void));
+
+extern char **failed_patches;
+extern int failed_patches_count;
+extern char toplevel_wd[];
+extern void client_import_setup PROTO((char *repository));
+extern int client_process_import_file
+ PROTO((char *message, char *vfile, char *vtag,
+ int targc, char *targv[], char *repository));
+extern void client_import_done PROTO((void));
+
+#endif /* CLIENT_SUPPORT */
diff --git a/gnu/usr.bin/cvs/cvs/expand_path.c b/gnu/usr.bin/cvs/cvs/expand_path.c
new file mode 100644
index 000000000000..7cc939fa269f
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/expand_path.c
@@ -0,0 +1,139 @@
+/* expand_path.c -- expand environmental variables in passed in string
+ The main routine is expand_pathname, it is the routine
+ that handles the '~' character in four forms:
+ ~name
+ ~name/
+ ~/
+ ~
+ and handles environment variables contained within the pathname
+ which are defined by:
+ c is some character
+ ${var_name} var_name is the name of the environ variable
+ $var_name var_name ends with a non ascii character
+ char *expand_pathname(char *name)
+ This routine will expand the pathname to account for ~
+ and $ characters as described above.If an error occurs, NULL
+ is returned.
+ Will only expand Built in CVS variables all others are ignored.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "cvs.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+static char *expand_variable PROTO((char *env));
+extern char *xmalloc ();
+extern void free ();
+char *
+expand_path (name)
+ char *name;
+{
+ char *s;
+ char *d;
+ char mybuf[PATH_MAX];
+ char buf[PATH_MAX];
+ char *result;
+ s = name;
+ d = mybuf;
+ while ((*d++ = *s))
+ if (*s++ == '$')
+ {
+ char *p = d;
+ char *e;
+ int flag = (*s == '{');
+
+ for (; (*d++ = *s); s++)
+ if (flag ? *s =='}' :
+ isalnum (*s) == 0 && *s!='_' )
+ break;
+ *--d = 0;
+ e = expand_variable (&p[flag]);
+
+ if (e)
+ {
+ for (d = &p[-1]; (*d++ = *e++);)
+ ;
+ --d;
+ if (flag && *s)
+ s++;
+ }
+ else
+ return NULL; /* no env variable */
+ }
+ *d = 0;
+ s = mybuf;
+ d = buf;
+ /* If you don't want ~username ~/ to be expanded simply remove
+ * This entire if statement including the else portion
+ */
+ if (*s++ == '~')
+ {
+ char *t;
+ char *p=s;
+ if (*s=='/' || *s==0)
+ t = getenv ("HOME");
+ else
+ {
+ struct passwd *ps;
+ for (; *p!='/' && *p; p++)
+ ;
+ *p = 0;
+ ps = getpwnam (s);
+ if (ps == 0)
+ return NULL; /* no such user */
+ t = ps->pw_dir;
+ }
+ while ((*d++ = *t++))
+ ;
+ --d;
+ if (*p == 0)
+ *p = '/'; /* always add / */
+ s=p;
+ }
+ else
+ --s;
+ /* Kill up to here */
+ while ((*d++ = *s++))
+ ;
+ *d=0;
+ result = xmalloc (sizeof(char) * strlen(buf)+1);
+ strcpy (result, buf);
+ return result;
+}
+static char *
+expand_variable (name)
+ char *name;
+{
+ /* There is nothing expanding this function to allow it
+ * to read a file in the $CVSROOT/CVSROOT directory that
+ * says which environmental variables could be expanded
+ * or just say everything is fair game to be expanded
+ */
+ if ( strcmp (name, CVSROOT_ENV) == 0 )
+ return CVSroot;
+ else
+ if ( strcmp (name, RCSBIN_ENV) == 0 )
+ return Rcsbin;
+ else
+ if ( strcmp (name, EDITOR1_ENV) == 0 )
+ return Editor;
+ else
+ if ( strcmp (name, EDITOR2_ENV) == 0 )
+ return Editor;
+ else
+ if ( strcmp (name, EDITOR3_ENV) == 0 )
+ return Editor;
+ else
+ return NULL;
+ /* The code here could also just
+ * return whatever getenv would
+ * return.
+ */
+}
diff --git a/gnu/usr.bin/cvs/cvs/login.c b/gnu/usr.bin/cvs/cvs/login.c
new file mode 100644
index 000000000000..ccb757ca3452
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/login.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with CVS.
+ *
+ * Allow user to log in for an authenticating server.
+ */
+
+#include "cvs.h"
+
+#ifdef AUTH_CLIENT_SUPPORT /* This covers the rest of the file. */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#ifndef lint
+static const char rcsid[] = "$CVSid: @(#)login.c 1.1 95/10/01 $";
+USE(rcsid);
+#endif
+
+#ifndef CVS_PASSWORD_FILE
+#define CVS_PASSWORD_FILE ".cvspass"
+#endif
+
+
+/* The return value will need to be freed. */
+char *
+construct_cvspass_filename ()
+{
+ char *homedir;
+ char *passfile;
+
+ /* Construct absolute pathname to user's password file. */
+ /* todo: does this work under Win-NT and OS/2 ? */
+ homedir = getenv ("HOME");
+ if (! homedir)
+ {
+ error (1, errno, "could not find out home directory");
+ return (char *) NULL;
+ }
+
+ passfile =
+ (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3);
+ strcpy (passfile, homedir);
+ strcat (passfile, "/");
+ strcat (passfile, CVS_PASSWORD_FILE);
+
+ /* Safety first and last, Scouts. */
+ if (isfile (passfile))
+ /* xchmod() is too polite. */
+ chmod (passfile, 0600);
+
+ return passfile;
+}
+
+
+/* Prompt for a password, and store it in the file "CVS/.cvspass".
+ *
+ * Because the user might be accessing multiple repositories, with
+ * different passwords for each one, the format of ~/.cvspass is:
+ *
+ * user@host:/path cleartext_password
+ * user@host:/path cleartext_password
+ * ...
+ *
+ * Of course, the "user@" might be left off -- it's just based on the
+ * value of CVSroot.
+ *
+ * Like .netrc, the file's permissions are the only thing preventing
+ * it from being read by others. Unlike .netrc, we will not be
+ * fascist about it, at most issuing a warning, and never refusing to
+ * work.
+ */
+int
+login (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *username;
+ int i;
+ char *passfile;
+ FILE *fp;
+ char *typed_password, *found_password;
+ char linebuf[MAXLINELEN];
+ int root_len, already_entered = 0;
+
+ /* Make this a "fully-qualified" CVSroot if necessary. */
+ if (! strchr (CVSroot, '@'))
+ {
+ /* We need to prepend "user@host:". */
+ char *tmp;
+
+ printf ("Repository \"%s\" not fully-qualified.\n", CVSroot);
+ printf ("Please enter \"user@host:/path\": ");
+ fflush (stdout);
+ fgets (linebuf, MAXLINELEN, stdin);
+
+ tmp = xmalloc (strlen (linebuf) + 1);
+
+ strcpy (tmp, linebuf);
+ tmp[strlen (linebuf) - 1] = '\0';
+ CVSroot = tmp;
+ }
+
+ /* Check to make sure it's fully-qualified before going on. */
+ if (! CVSroot)
+ {
+ error (1, 0, "CVSroot is NULL");
+ }
+ else if ((! strchr (CVSroot, '@')) && (! strchr (CVSroot, ':')))
+ {
+ error (1, 0, "CVSroot not fully-qualified: %s", CVSroot);
+ }
+
+
+ passfile = construct_cvspass_filename ();
+ typed_password = getpass ("Enter CVS password: ");
+
+ /* IF we have a password for this "[user@]host:/path" already
+ * THEN
+ * IF it's the same as the password we read from the prompt
+ * THEN
+ * do nothing
+ * ELSE
+ * replace the old password with the new one
+ * ELSE
+ * append new entry to the end of the file.
+ */
+
+ root_len = strlen (CVSroot);
+
+ /* Yes, the method below reads the user's password file twice. It's
+ inefficient, but we're not talking about a gig of data here. */
+
+ fp = fopen (passfile, "r");
+ if (fp == NULL)
+ {
+ error (1, errno, "unable to open %s", passfile);
+ return 1;
+ }
+
+ /* Check each line to see if we have this entry already. */
+ while (fgets (linebuf, MAXLINELEN, fp) != NULL)
+ {
+ if (! strncmp (CVSroot, linebuf, root_len))
+ {
+ already_entered = 1;
+ break;
+ }
+ }
+ fclose (fp);
+
+
+ if (already_entered)
+ {
+ /* This user/host has a password in the file already. */
+
+ /* todo: what about these charsets??? */
+ strtok (linebuf, " \n");
+ found_password = strtok (NULL, " \n");
+ if (strcmp (found_password, typed_password))
+ {
+ /* typed_password and found_password don't match, so we'll
+ * have to update passfile. We replace the old password
+ * with the new one by writing a tmp file whose contents are
+ * exactly the same as passfile except that this one entry
+ * gets typed_password instead of found_password. Then we
+ * rename the tmp file on top of passfile.
+ */
+ char *tmp_name;
+ FILE *tmp_fp;
+
+ tmp_name = tmpnam (NULL);
+ if ((tmp_fp = fopen (tmp_name, "w")) == NULL)
+ {
+ error (1, errno, "unable to open temp file %s", tmp_name);
+ return 1;
+ }
+ chmod (tmp_name, 0600);
+
+ fp = fopen (passfile, "r");
+ if (fp == NULL)
+ {
+ error (1, errno, "unable to open %s", passfile);
+ return 1;
+ }
+ /* I'm not paranoid, they really ARE out to get me: */
+ chmod (passfile, 0600);
+
+ while (fgets (linebuf, MAXLINELEN, fp) != NULL)
+ {
+ if (strncmp (CVSroot, linebuf, root_len))
+ fprintf (tmp_fp, "%s", linebuf);
+ else
+ fprintf (tmp_fp, "%s %s\n", CVSroot, typed_password);
+ }
+ fclose (tmp_fp);
+ fclose (fp);
+ rename_file (tmp_name, passfile);
+ chmod (passfile, 0600);
+ }
+ }
+ else
+ {
+ if ((fp = fopen (passfile, "a")) == NULL)
+ {
+ error (1, errno, "could not open %s", passfile);
+ free (passfile);
+ return 1;
+ }
+
+ /* It's safer this way, and blank lines in the file are OK. */
+ fprintf (fp, "\n%s %s\n", CVSroot, typed_password);
+ fclose (fp);
+ }
+
+ /* Utter, total, raving paranoia, I know. */
+ chmod (passfile, 0600);
+ memset (typed_password, 0, strlen (typed_password));
+
+ free (passfile);
+ return 0;
+}
+
+/* todo: "cvs logout" could erase an entry from the file.
+ * But to what purpose?
+ */
+
+
+char *
+get_cvs_password (user, host, cvsroot)
+{
+ int root_len;
+ int found_it = 0;
+ char *password;
+ char linebuf[MAXLINELEN];
+ FILE *fp;
+ char *passfile;
+
+ passfile = construct_cvspass_filename ();
+ fp = fopen (passfile, "r");
+ if (fp == NULL)
+ {
+ error (0, errno, "could not open %s", passfile);
+ free (passfile);
+ goto prompt_for_it;
+ }
+
+ root_len = strlen (CVSroot);
+
+ /* Check each line to see if we have this entry already. */
+ while (fgets (linebuf, MAXLINELEN, fp) != NULL)
+ {
+ if (strncmp (CVSroot, linebuf, root_len) == 0)
+ {
+ /* This is it! So break out and deal with linebuf. */
+ found_it = 1;
+ break;
+ }
+ }
+
+ if (found_it)
+ {
+ /* linebuf now contains the line with the password. */
+ char *tmp;
+
+ strtok (linebuf, " ");
+ password = strtok (NULL, "\n");
+
+ /* Give it permanent storage. */
+ tmp = xmalloc (strlen (password) + 1);
+ strcpy (tmp, password);
+ tmp[strlen (password)] = '\0';
+ memset (password, 0, strlen (password));
+ return tmp;
+ }
+ else
+ {
+ prompt_for_it:
+ return getpass ("CVS password: ");
+ }
+}
+
+#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
+
diff --git a/gnu/usr.bin/cvs/cvs/rcscmds.c b/gnu/usr.bin/cvs/cvs/rcscmds.c
new file mode 100644
index 000000000000..af32cea1d6d9
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/rcscmds.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.4 kit.
+ *
+ * The functions in this file provide an interface for performing
+ * operations directly on RCS files.
+ */
+
+#include "cvs.h"
+
+int
+RCS_settag(path, tag, rev)
+ const char *path;
+ const char *tag;
+ const char *rev;
+{
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, tag, rev);
+ run_arg (path);
+ return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+}
+
+/* NOERR is 1 to suppress errors--FIXME it would
+ be better to avoid the errors or some cleaner solution. */
+int
+RCS_deltag(path, tag, noerr)
+ const char *path;
+ const char *tag;
+ int noerr;
+{
+ run_setup ("%s%s -q -N%s", Rcsbin, RCS, tag);
+ run_arg (path);
+ return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL);
+}
+
+/* set RCS branch to REV */
+int
+RCS_setbranch(path, rev)
+ const char *path;
+ const char *rev;
+{
+ run_setup ("%s%s -q -b%s", Rcsbin, RCS, rev ? rev : "");
+ run_arg (path);
+ return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+}
+
+/* Lock revision REV. NOERR is 1 to suppress errors--FIXME it would
+ be better to avoid the errors or some cleaner solution. */
+int
+RCS_lock(path, rev, noerr)
+ const char *path;
+ const char *rev;
+ int noerr;
+{
+ run_setup ("%s%s -q -l%s", Rcsbin, RCS, rev ? rev : "");
+ run_arg (path);
+ return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL);
+}
+
+/* Unlock revision REV. NOERR is 1 to suppress errors--FIXME it would
+ be better to avoid the errors or some cleaner solution. */
+int
+RCS_unlock(path, rev, noerr)
+ const char *path;
+ const char *rev;
+ int noerr;
+{
+ run_setup ("%s%s -q -u%s", Rcsbin, RCS, rev ? rev : "");
+ run_arg (path);
+ return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL);
+}
+
+/* Merge revisions REV1 and REV2. */
+int
+RCS_merge(path, options, rev1, rev2)
+ const char *path;
+ const char *options;
+ const char *rev1;
+ const char *rev2;
+{
+ int status;
+
+ /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
+
+ run_setup ("%s%s %s -r%s -r%s %s", Rcsbin, RCS_RCSMERGE,
+ options, rev1, rev2, path);
+ status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+#ifndef HAVE_RCS5
+ if (status == 0)
+ {
+ /* Run GREP to see if there appear to be conflicts in the file */
+ run_setup ("%s", GREP);
+ run_arg (RCS_MERGE_PAT);
+ run_arg (path);
+ status = (run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_NORMAL) == 0);
+
+ }
+#endif
+ return status;
+}
diff --git a/gnu/usr.bin/cvs/cvs/server.c b/gnu/usr.bin/cvs/cvs/server.c
new file mode 100644
index 000000000000..fbfca86d393d
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/server.c
@@ -0,0 +1,3992 @@
+#include "cvs.h"
+
+#ifdef SERVER_SUPPORT
+
+/* for select */
+#include <sys/types.h>
+#ifdef HAVE_SYS_BSDTYPES_H
+#include <sys/bsdtypes.h>
+#endif
+#include <sys/time.h>
+
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+
+/* Functions which the server calls. */
+int add PROTO((int argc, char **argv));
+int admin PROTO((int argc, char **argv));
+int checkout PROTO((int argc, char **argv));
+int commit PROTO((int argc, char **argv));
+int diff PROTO((int argc, char **argv));
+int history PROTO((int argc, char **argv));
+int import PROTO((int argc, char **argv));
+int cvslog PROTO((int argc, char **argv));
+int patch PROTO((int argc, char **argv));
+int release PROTO((int argc, char **argv));
+int cvsremove PROTO((int argc, char **argv));
+int rtag PROTO((int argc, char **argv));
+int status PROTO((int argc, char **argv));
+int tag PROTO((int argc, char **argv));
+int update PROTO((int argc, char **argv));
+
+
+/*
+ * This is where we stash stuff we are going to use. Format string
+ * which expects a single directory within it, starting with a slash.
+ */
+static char *server_temp_dir;
+
+/* Nonzero if we should keep the temp directory around after we exit. */
+static int dont_delete_temp;
+
+static char no_mem_error;
+#define NO_MEM_ERROR (&no_mem_error)
+
+static void server_write_entries PROTO((void));
+
+/*
+ * Read a line from the stream "instream" without command line editing.
+ *
+ * Action is compatible with "readline", e.g. space for the result is
+ * malloc'd and should be freed by the caller.
+ *
+ * A NULL return means end of file. A return of NO_MEM_ERROR means
+ * that we are out of memory.
+ */
+static char *read_line PROTO((FILE *));
+
+static char *
+read_line (stream)
+ FILE *stream;
+{
+ int c;
+ char *result;
+ int input_index = 0;
+ int result_size = 80;
+
+ fflush (stdout);
+ result = (char *) malloc (result_size);
+ if (result == NULL)
+ return NO_MEM_ERROR;
+
+ while (1)
+ {
+ c = fgetc (stream);
+
+ if (c == EOF)
+ {
+ free (result);
+ return NULL;
+ }
+
+ if (c == '\n')
+ break;
+
+ result[input_index++] = c;
+ while (input_index >= result_size)
+ {
+ result_size *= 2;
+ result = (char *) realloc (result, result_size);
+ if (result == NULL)
+ return NO_MEM_ERROR;
+ }
+ }
+
+ result[input_index++] = '\0';
+ return result;
+}
+
+/*
+ * Make directory DIR, including all intermediate directories if necessary.
+ * Returns 0 for success or errno code.
+ */
+static int mkdir_p PROTO((char *));
+
+static int
+mkdir_p (dir)
+ char *dir;
+{
+ char *p;
+ char *q = malloc (strlen (dir) + 1);
+ int retval;
+
+ if (q == NULL)
+ return ENOMEM;
+
+ /*
+ * Skip over leading slash if present. We won't bother to try to
+ * make '/'.
+ */
+ p = dir + 1;
+ while (1)
+ {
+ while (*p != '/' && *p != '\0')
+ ++p;
+ if (*p == '/')
+ {
+ strncpy (q, dir, p - dir);
+ q[p - dir] = '\0';
+ if (CVS_MKDIR (q, 0777) < 0)
+ {
+ if (errno != EEXIST
+ && (errno != EACCES || !isdir(q)))
+ {
+ retval = errno;
+ goto done;
+ }
+ }
+ ++p;
+ }
+ else
+ {
+ if (CVS_MKDIR (dir, 0777) < 0)
+ retval = errno;
+ else
+ retval = 0;
+ goto done;
+ }
+ }
+ done:
+ free (q);
+ return retval;
+}
+
+/*
+ * Print the error response for error code STATUS. The caller is
+ * reponsible for making sure we get back to the command loop without
+ * any further output occuring.
+ */
+static void
+print_error (status)
+ int status;
+{
+ char *msg;
+ printf ("error ");
+ msg = strerror (status);
+ if (msg)
+ printf ("%s", msg);
+ printf ("\n");
+}
+
+static int pending_error;
+/*
+ * Malloc'd text for pending error. Each line must start with "E ". The
+ * last line should not end with a newline.
+ */
+static char *pending_error_text;
+
+/* If an error is pending, print it and return 1. If not, return 0. */
+static int
+print_pending_error ()
+{
+ if (pending_error_text)
+ {
+ printf ("%s\n", pending_error_text);
+ if (pending_error)
+ print_error (pending_error);
+ else
+ printf ("error \n");
+ pending_error = 0;
+ free (pending_error_text);
+ pending_error_text = NULL;
+ return 1;
+ }
+ else if (pending_error)
+ {
+ print_error (pending_error);
+ pending_error = 0;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Is an error pending? */
+#define error_pending() (pending_error || pending_error_text)
+
+int
+supported_response (name)
+ char *name;
+{
+ struct response *rs;
+
+ for (rs = responses; rs->name != NULL; ++rs)
+ if (strcmp (rs->name, name) == 0)
+ return rs->status == rs_supported;
+ error (1, 0, "internal error: testing support for unknown response?");
+}
+
+static void
+serve_valid_responses (arg)
+ char *arg;
+{
+ char *p = arg;
+ char *q;
+ struct response *rs;
+ do
+ {
+ q = strchr (p, ' ');
+ if (q != NULL)
+ *q++ = '\0';
+ for (rs = responses; rs->name != NULL; ++rs)
+ {
+ if (strcmp (rs->name, p) == 0)
+ break;
+ }
+ if (rs->name == NULL)
+ /*
+ * It is a response we have never heard of (and thus never
+ * will want to use). So don't worry about it.
+ */
+ ;
+ else
+ rs->status = rs_supported;
+ p = q;
+ } while (q != NULL);
+ for (rs = responses; rs->name != NULL; ++rs)
+ {
+ if (rs->status == rs_essential)
+ {
+ printf ("E response `%s' not supported by client\nerror \n",
+ rs->name);
+ exit (1);
+ }
+ else if (rs->status == rs_optional)
+ rs->status = rs_not_supported;
+ }
+}
+
+static int use_dir_and_repos = 0;
+
+static void
+serve_root (arg)
+ char *arg;
+{
+ char *env;
+ extern char *CVSroot;
+ char path[PATH_MAX];
+ int save_errno;
+
+ if (error_pending()) return;
+
+ (void) sprintf (path, "%s/%s", arg, CVSROOTADM);
+ if (!isaccessible (path, R_OK | X_OK))
+ {
+ save_errno = errno;
+ pending_error_text = malloc (80 + strlen (path));
+ if (pending_error_text != NULL)
+ sprintf (pending_error_text, "E Cannot access %s", path);
+ pending_error = save_errno;
+ }
+ (void) strcat (path, "/");
+ (void) strcat (path, CVSROOTADM_HISTORY);
+ if (isfile (path) && !isaccessible (path, R_OK | W_OK))
+ {
+ save_errno = errno;
+ pending_error_text = malloc (80 + strlen (path));
+ if (pending_error_text != NULL)
+ sprintf (pending_error_text, "E \
+Sorry, you don't have read/write access to the history file %s", path);
+ pending_error = save_errno;
+ }
+
+ CVSroot = malloc (strlen (arg) + 1);
+ if (CVSroot == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (CVSroot, arg);
+#ifdef HAVE_PUTENV
+ env = malloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1);
+ if (env == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ (void) sprintf (env, "%s=%s", CVSROOT_ENV, arg);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
+#endif
+}
+
+/*
+ * Add as many directories to the temp directory as the client tells us it
+ * will use "..", so we never try to access something outside the temp
+ * directory via "..".
+ */
+static void
+serve_max_dotdot (arg)
+ char *arg;
+{
+ int lim = atoi (arg);
+ int i;
+ char *p;
+
+ if (lim < 0)
+ return;
+ p = malloc (strlen (server_temp_dir) + 2 * lim + 10);
+ if (p == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (p, server_temp_dir);
+ for (i = 0; i < lim; ++i)
+ strcat (p, "/d");
+ free (server_temp_dir);
+ server_temp_dir = p;
+}
+
+static void
+dirswitch (dir, repos)
+ char *dir;
+ char *repos;
+{
+ char *dirname;
+ int status;
+ FILE *f;
+
+ server_write_entries ();
+
+ if (error_pending()) return;
+
+ dirname = malloc (strlen (server_temp_dir) + strlen (dir) + 40);
+ if (dirname == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+
+ strcpy (dirname, server_temp_dir);
+ strcat (dirname, "/");
+ strcat (dirname, dir);
+
+ status = mkdir_p (dirname);
+ if (status != 0
+ && status != EEXIST)
+ {
+ pending_error = status;
+ pending_error_text = malloc (80 + strlen(dirname));
+ sprintf(pending_error_text, "E cannot mkdir %s", dirname);
+ return;
+ }
+ if (chdir (dirname) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(dirname));
+ sprintf(pending_error_text, "E cannot change to %s", dirname);
+ return;
+ }
+ /*
+ * This is pretty much like calling Create_Admin, but Create_Admin doesn't
+ * report errors in the right way for us.
+ */
+ if (CVS_MKDIR (CVSADM, 0777) < 0)
+ {
+ if (errno == EEXIST)
+ /* Don't create the files again. */
+ return;
+ pending_error = errno;
+ return;
+ }
+ f = fopen (CVSADM_REP, "w");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ return;
+ }
+ if (fprintf (f, "%s\n", repos) < 0)
+ {
+ pending_error = errno;
+ fclose (f);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ return;
+ }
+ f = fopen (CVSADM_ENT, "w+");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENT));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_ENT);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENT));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_ENT);
+ return;
+ }
+ free (dirname);
+}
+
+static void
+serve_repository (arg)
+ char *arg;
+{
+ dirswitch (arg + 1, arg);
+}
+
+static void
+serve_directory (arg)
+ char *arg;
+{
+ char *repos;
+ use_dir_and_repos = 1;
+ repos = read_line (stdin);
+ if (repos == NULL)
+ {
+ pending_error_text = malloc (80 + strlen (arg));
+ if (pending_error_text)
+ {
+ if (feof (stdin))
+ sprintf (pending_error_text,
+ "E end of file reading mode for %s", arg);
+ else
+ {
+ sprintf (pending_error_text,
+ "E error reading mode for %s", arg);
+ pending_error = errno;
+ }
+ }
+ else
+ pending_error = ENOMEM;
+ }
+ else if (repos == NO_MEM_ERROR)
+ {
+ pending_error = ENOMEM;
+ }
+ else
+ {
+ dirswitch (arg, repos);
+ free (repos);
+ }
+}
+
+static void
+serve_static_directory (arg)
+ char *arg;
+{
+ FILE *f;
+ f = fopen (CVSADM_ENTSTAT, "w+");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENTSTAT));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_ENTSTAT);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENTSTAT));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_ENTSTAT);
+ return;
+ }
+}
+
+static void
+serve_sticky (arg)
+ char *arg;
+{
+ FILE *f;
+ f = fopen (CVSADM_TAG, "w+");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_TAG));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_TAG);
+ return;
+ }
+ if (fprintf (f, "%s\n", arg) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_TAG));
+ sprintf(pending_error_text, "E cannot write to %s", CVSADM_TAG);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_TAG));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_TAG);
+ return;
+ }
+}
+
+/*
+ * Read SIZE bytes from stdin, write them to FILE.
+ *
+ * Currently this isn't really used for receiving parts of a file --
+ * the file is still sent over in one chunk. But if/when we get
+ * spiffy in-process gzip support working, perhaps the compressed
+ * pieces could be sent over as they're ready, if the network is fast
+ * enough. Or something.
+ */
+static void
+receive_partial_file (size, file)
+ int size;
+ int file;
+{
+ char buf[16*1024], *bufp;
+ int toread, nread, nwrote;
+ while (size > 0)
+ {
+ toread = sizeof (buf);
+ if (toread > size)
+ toread = size;
+
+ nread = fread (buf, 1, toread, stdin);
+ if (nread <= 0)
+ {
+ if (feof (stdin))
+ {
+ pending_error_text = malloc (80);
+ if (pending_error_text)
+ {
+ sprintf (pending_error_text,
+ "E premature end of file from client");
+ pending_error = 0;
+ }
+ else
+ pending_error = ENOMEM;
+ }
+ else if (ferror (stdin))
+ {
+ pending_error_text = malloc (40);
+ if (pending_error_text)
+ sprintf (pending_error_text,
+ "E error reading from client");
+ pending_error = errno;
+ }
+ else
+ {
+ pending_error_text = malloc (40);
+ if (pending_error_text)
+ sprintf (pending_error_text,
+ "E short read from client");
+ pending_error = 0;
+ }
+ return;
+ }
+ size -= nread;
+ bufp = buf;
+ while (nread)
+ {
+ nwrote = write (file, bufp, nread);
+ if (nwrote < 0)
+ {
+ pending_error_text = malloc (40);
+ if (pending_error_text)
+ sprintf (pending_error_text, "E unable to write");
+ pending_error = errno;
+ return;
+ }
+ nread -= nwrote;
+ bufp += nwrote;
+ }
+ }
+}
+
+/* Receive SIZE bytes, write to filename FILE. */
+static void
+receive_file (size, file, gzipped)
+ int size;
+ char *file;
+ int gzipped;
+{
+ int fd;
+ char *arg = file;
+ pid_t gzip_pid = 0;
+ int gzip_status;
+
+ /* Write the file. */
+ fd = open (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0)
+ {
+ pending_error_text = malloc (40 + strlen (arg));
+ if (pending_error_text)
+ sprintf (pending_error_text, "E cannot open %s", arg);
+ pending_error = errno;
+ return;
+ }
+
+ /*
+ * FIXME: This doesn't do anything reasonable with gunzip's stderr, which
+ * means that if gunzip writes to stderr, it will cause all manner of
+ * protocol violations.
+ */
+ if (gzipped)
+ fd = filter_through_gunzip (fd, 0, &gzip_pid);
+
+ receive_partial_file (size, fd);
+
+ if (pending_error_text)
+ {
+ char *p = realloc (pending_error_text,
+ strlen (pending_error_text) + strlen (arg) + 30);
+ if (p)
+ {
+ pending_error_text = p;
+ sprintf (p + strlen (p), ", file %s", arg);
+ }
+ /* else original string is supposed to be unchanged */
+ }
+
+ if (close (fd) < 0 && !error_pending ())
+ {
+ pending_error_text = malloc (40 + strlen (arg));
+ if (pending_error_text)
+ sprintf (pending_error_text, "E cannot close %s", arg);
+ pending_error = errno;
+ if (gzip_pid)
+ waitpid (gzip_pid, (int *) 0, 0);
+ return;
+ }
+
+ if (gzip_pid)
+ {
+ if (waitpid (gzip_pid, &gzip_status, 0) != gzip_pid)
+ error (1, errno, "waiting for gunzip process %d", gzip_pid);
+ else if (gzip_status != 0)
+ error (1, 0, "gunzip exited %d", gzip_status);
+ }
+}
+
+static void
+serve_modified (arg)
+ char *arg;
+{
+ int size;
+ char *size_text;
+ char *mode_text;
+
+ int gzipped = 0;
+
+ if (error_pending ()) return;
+
+ mode_text = read_line (stdin);
+ if (mode_text == NULL)
+ {
+ pending_error_text = malloc (80 + strlen (arg));
+ if (pending_error_text)
+ {
+ if (feof (stdin))
+ sprintf (pending_error_text,
+ "E end of file reading mode for %s", arg);
+ else
+ {
+ sprintf (pending_error_text,
+ "E error reading mode for %s", arg);
+ pending_error = errno;
+ }
+ }
+ else
+ pending_error = ENOMEM;
+ return;
+ }
+ else if (mode_text == NO_MEM_ERROR)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ size_text = read_line (stdin);
+ if (size_text == NULL)
+ {
+ pending_error_text = malloc (80 + strlen (arg));
+ if (pending_error_text)
+ {
+ if (feof (stdin))
+ sprintf (pending_error_text,
+ "E end of file reading size for %s", arg);
+ else
+ {
+ sprintf (pending_error_text,
+ "E error reading size for %s", arg);
+ pending_error = errno;
+ }
+ }
+ else
+ pending_error = ENOMEM;
+ return;
+ }
+ else if (size_text == NO_MEM_ERROR)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ if (size_text[0] == 'z')
+ {
+ gzipped = 1;
+ size = atoi (size_text + 1);
+ }
+ else
+ size = atoi (size_text);
+ free (size_text);
+
+ if (size >= 0)
+ {
+ receive_file (size, arg, gzipped);
+ if (error_pending ()) return;
+ }
+
+ {
+ int status = change_mode (arg, mode_text);
+ free (mode_text);
+ if (status)
+ {
+ pending_error_text = malloc (40 + strlen (arg));
+ if (pending_error_text)
+ sprintf (pending_error_text,
+ "E cannot change mode for %s", arg);
+ pending_error = status;
+ return;
+ }
+ }
+}
+
+#endif /* SERVER_SUPPORT */
+
+#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
+
+int use_unchanged = 0;
+
+#endif
+#ifdef SERVER_SUPPORT
+
+static void
+serve_enable_unchanged (arg)
+ char *arg;
+{
+ use_unchanged = 1;
+}
+
+static void
+serve_lost (arg)
+ char *arg;
+{
+ if (use_unchanged)
+ {
+ /* A missing file already indicates it is nonexistent. */
+ return;
+ }
+ else
+ {
+ struct utimbuf ut;
+ int fd = open (arg, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0 || close (fd) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(arg));
+ sprintf(pending_error_text, "E cannot open %s", arg);
+ return;
+ }
+ /*
+ * Set the times to the beginning of the epoch to tell time_stamp()
+ * that the file was lost.
+ */
+ ut.actime = 0;
+ ut.modtime = 0;
+ if (utime (arg, &ut) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(arg));
+ sprintf(pending_error_text, "E cannot utime %s", arg);
+ return;
+ }
+ }
+}
+
+struct an_entry {
+ struct an_entry *next;
+ char *entry;
+};
+
+static struct an_entry *entries;
+
+static void
+serve_unchanged (arg)
+ char *arg;
+{
+ if (error_pending ())
+ return;
+ if (!use_unchanged)
+ {
+ /* A missing file already indicates it is unchanged. */
+ return;
+ }
+ else
+ {
+ struct an_entry *p;
+ char *name;
+ char *cp;
+ char *timefield;
+
+ /* Rewrite entries file to have `=' in timestamp field. */
+ for (p = entries; p != NULL; p = p->next)
+ {
+ name = p->entry + 1;
+ cp = strchr (name, '/');
+ if (cp != NULL
+ && strlen (arg) == cp - name
+ && strncmp (arg, name, cp - name) == 0)
+ {
+ timefield = strchr (cp + 1, '/') + 1;
+ if (*timefield != '=')
+ {
+ cp = timefield + strlen (timefield);
+ cp[1] = '\0';
+ while (cp > timefield)
+ {
+ *cp = cp[-1];
+ --cp;
+ }
+ *timefield = '=';
+ }
+ break;
+ }
+ }
+ }
+}
+
+static void
+serve_entry (arg)
+ char *arg;
+{
+ struct an_entry *p;
+ char *cp;
+ if (error_pending()) return;
+ p = (struct an_entry *) malloc (sizeof (struct an_entry));
+ if (p == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ /* Leave space for serve_unchanged to write '=' if it wants. */
+ cp = malloc (strlen (arg) + 2);
+ if (cp == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (cp, arg);
+ p->next = entries;
+ p->entry = cp;
+ entries = p;
+}
+
+static void
+server_write_entries ()
+{
+ FILE *f;
+ struct an_entry *p;
+ struct an_entry *q;
+
+ if (entries == NULL)
+ return;
+
+ f = NULL;
+ /* Note that we free all the entries regardless of errors. */
+ if (!error_pending ())
+ {
+ f = fopen (CVSADM_ENT, "w");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENT));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_ENT);
+ }
+ }
+ for (p = entries; p != NULL;)
+ {
+ if (!error_pending ())
+ {
+ if (fprintf (f, "%s\n", p->entry) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENT));
+ sprintf(pending_error_text, "E cannot write to %s", CVSADM_ENT);
+ }
+ }
+ free (p->entry);
+ q = p->next;
+ free (p);
+ p = q;
+ }
+ entries = NULL;
+ if (f != NULL && fclose (f) == EOF && !error_pending ())
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_ENT));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_ENT);
+ }
+}
+
+static int argument_count;
+static char **argument_vector;
+static int argument_vector_size;
+
+static void
+serve_argument (arg)
+ char *arg;
+{
+ char *p;
+
+ if (error_pending()) return;
+
+ if (argument_vector_size <= argument_count)
+ {
+ argument_vector_size *= 2;
+ argument_vector =
+ (char **) realloc ((char *)argument_vector,
+ argument_vector_size * sizeof (char *));
+ if (argument_vector == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ }
+ p = malloc (strlen (arg) + 1);
+ if (p == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (p, arg);
+ argument_vector[argument_count++] = p;
+}
+
+static void
+serve_argumentx (arg)
+ char *arg;
+{
+ char *p;
+
+ if (error_pending()) return;
+
+ p = argument_vector[argument_count - 1];
+ p = realloc (p, strlen (p) + 1 + strlen (arg) + 1);
+ if (p == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcat (p, "\n");
+ strcat (p, arg);
+ argument_vector[argument_count - 1] = p;
+}
+
+static void
+serve_global_option (arg)
+ char *arg;
+{
+ if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0')
+ {
+ error_return:
+ pending_error_text = malloc (strlen (arg) + 80);
+ sprintf (pending_error_text, "E Protocol error: bad global option %s",
+ arg);
+ return;
+ }
+ switch (arg[1])
+ {
+ case 'n':
+ noexec = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'r':
+ cvswrite = 0;
+ break;
+ case 'Q':
+ really_quiet = 1;
+ break;
+ case 'l':
+ logoff = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ default:
+ goto error_return;
+ }
+}
+
+/*
+ * We must read data from a child process and send it across the
+ * network. We do not want to block on writing to the network, so we
+ * store the data from the child process in memory. A BUFFER
+ * structure holds the status of one communication, and uses a linked
+ * list of buffer_data structures to hold data.
+ */
+
+struct buffer
+{
+ /* Data. */
+ struct buffer_data *data;
+
+ /* Last buffer on data chain. */
+ struct buffer_data *last;
+
+ /* File descriptor to write to or read from. */
+ int fd;
+
+ /* Nonzero if this is an output buffer (sanity check). */
+ int output;
+
+ /* Nonzero if the file descriptor is in nonblocking mode. */
+ int nonblocking;
+
+ /* Function to call if we can't allocate memory. */
+ void (*memory_error) PROTO((struct buffer *));
+};
+
+/* Data is stored in lists of these structures. */
+
+struct buffer_data
+{
+ /* Next buffer in linked list. */
+ struct buffer_data *next;
+
+ /*
+ * A pointer into the data area pointed to by the text field. This
+ * is where to find data that has not yet been written out.
+ */
+ char *bufp;
+
+ /* The number of data bytes found at BUFP. */
+ int size;
+
+ /*
+ * Actual buffer. This never changes after the structure is
+ * allocated. The buffer is BUFFER_DATA_SIZE bytes.
+ */
+ char *text;
+};
+
+/* The size we allocate for each buffer_data structure. */
+#define BUFFER_DATA_SIZE (4096)
+
+#ifdef SERVER_FLOWCONTROL
+/* The maximum we'll queue to the remote client before blocking. */
+# ifndef SERVER_HI_WATER
+# define SERVER_HI_WATER (2 * 1024 * 1024)
+# endif /* SERVER_HI_WATER */
+/* When the buffer drops to this, we restart the child */
+# ifndef SERVER_LO_WATER
+# define SERVER_LO_WATER (1 * 1024 * 1024)
+# endif /* SERVER_LO_WATER */
+#endif /* SERVER_FLOWCONTROL */
+
+/* Linked list of available buffer_data structures. */
+static struct buffer_data *free_buffer_data;
+
+static void allocate_buffer_datas PROTO((void));
+static inline struct buffer_data *get_buffer_data PROTO((void));
+static int buf_empty_p PROTO((struct buffer *));
+static void buf_output PROTO((struct buffer *, const char *, int));
+static void buf_output0 PROTO((struct buffer *, const char *));
+static inline void buf_append_char PROTO((struct buffer *, int));
+static int buf_send_output PROTO((struct buffer *));
+static int set_nonblock PROTO((struct buffer *));
+static int set_block PROTO((struct buffer *));
+static int buf_send_counted PROTO((struct buffer *));
+static inline void buf_append_data PROTO((struct buffer *,
+ struct buffer_data *,
+ struct buffer_data *));
+static int buf_read_file PROTO((FILE *, long, struct buffer_data **,
+ struct buffer_data **));
+static int buf_input_data PROTO((struct buffer *, int *));
+static void buf_copy_lines PROTO((struct buffer *, struct buffer *, int));
+static int buf_copy_counted PROTO((struct buffer *, struct buffer *));
+
+#ifdef SERVER_FLOWCONTROL
+static int buf_count_mem PROTO((struct buffer *));
+static int set_nonblock_fd PROTO((int));
+#endif /* SERVER_FLOWCONTROL */
+
+/* Allocate more buffer_data structures. */
+
+static void
+allocate_buffer_datas ()
+{
+ struct buffer_data *alc;
+ char *space;
+ int i;
+
+ /* Allocate buffer_data structures in blocks of 16. */
+#define ALLOC_COUNT (16)
+
+ alc = ((struct buffer_data *)
+ malloc (ALLOC_COUNT * sizeof (struct buffer_data)));
+ space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE);
+ if (alc == NULL || space == NULL)
+ return;
+ for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE)
+ {
+ alc->next = free_buffer_data;
+ free_buffer_data = alc;
+ alc->text = space;
+ }
+}
+
+/* Get a new buffer_data structure. */
+
+static inline struct buffer_data *
+get_buffer_data ()
+{
+ struct buffer_data *ret;
+
+ if (free_buffer_data == NULL)
+ {
+ allocate_buffer_datas ();
+ if (free_buffer_data == NULL)
+ return NULL;
+ }
+
+ ret = free_buffer_data;
+ free_buffer_data = ret->next;
+ return ret;
+}
+
+/* See whether a buffer is empty. */
+
+static int
+buf_empty_p (buf)
+ struct buffer *buf;
+{
+ struct buffer_data *data;
+
+ for (data = buf->data; data != NULL; data = data->next)
+ if (data->size > 0)
+ return 0;
+ return 1;
+}
+
+#ifdef SERVER_FLOWCONTROL
+/*
+ * Count how much data is stored in the buffer..
+ * Note that each buffer is a malloc'ed chunk BUFFER_DATA_SIZE.
+ */
+
+static int
+buf_count_mem (buf)
+ struct buffer *buf;
+{
+ struct buffer_data *data;
+ int mem = 0;
+
+ for (data = buf->data; data != NULL; data = data->next)
+ mem += BUFFER_DATA_SIZE;
+
+ return mem;
+}
+#endif /* SERVER_FLOWCONTROL */
+
+/* Add data DATA of length LEN to BUF. */
+
+static void
+buf_output (buf, data, len)
+ struct buffer *buf;
+ const char *data;
+ int len;
+{
+ if (! buf->output)
+ abort ();
+
+ if (buf->data != NULL
+ && (((buf->last->text + BUFFER_DATA_SIZE)
+ - (buf->last->bufp + buf->last->size))
+ >= len))
+ {
+ memcpy (buf->last->bufp + buf->last->size, data, len);
+ buf->last->size += len;
+ return;
+ }
+
+ while (1)
+ {
+ struct buffer_data *newdata;
+
+ newdata = get_buffer_data ();
+ if (newdata == NULL)
+ {
+ (*buf->memory_error) (buf);
+ return;
+ }
+
+ if (buf->data == NULL)
+ buf->data = newdata;
+ else
+ buf->last->next = newdata;
+ newdata->next = NULL;
+ buf->last = newdata;
+
+ newdata->bufp = newdata->text;
+
+ if (len <= BUFFER_DATA_SIZE)
+ {
+ newdata->size = len;
+ memcpy (newdata->text, data, len);
+ return;
+ }
+
+ newdata->size = BUFFER_DATA_SIZE;
+ memcpy (newdata->text, data, BUFFER_DATA_SIZE);
+
+ data += BUFFER_DATA_SIZE;
+ len -= BUFFER_DATA_SIZE;
+ }
+
+ /*NOTREACHED*/
+}
+
+/* Add a '\0' terminated string to BUF. */
+
+static void
+buf_output0 (buf, string)
+ struct buffer *buf;
+ const char *string;
+{
+ buf_output (buf, string, strlen (string));
+}
+
+/* Add a single character to BUF. */
+
+static inline void
+buf_append_char (buf, ch)
+ struct buffer *buf;
+ int ch;
+{
+ if (buf->data != NULL
+ && (buf->last->text + BUFFER_DATA_SIZE
+ != buf->last->bufp + buf->last->size))
+ {
+ *(buf->last->bufp + buf->last->size) = ch;
+ ++buf->last->size;
+ }
+ else
+ {
+ char b;
+
+ b = ch;
+ buf_output (buf, &b, 1);
+ }
+}
+
+/*
+ * Send all the output we've been saving up. Returns 0 for success or
+ * errno code. If the buffer has been set to be nonblocking, this
+ * will just write until the write would block.
+ */
+
+static int
+buf_send_output (buf)
+ struct buffer *buf;
+{
+ if (! buf->output)
+ abort ();
+
+ while (buf->data != NULL)
+ {
+ struct buffer_data *data;
+
+ data = buf->data;
+ while (data->size > 0)
+ {
+ int nbytes;
+
+ nbytes = write (buf->fd, data->bufp, data->size);
+ if (nbytes <= 0)
+ {
+ int status;
+
+ if (buf->nonblocking
+ && (nbytes == 0
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ || errno == EAGAIN))
+ {
+ /*
+ * A nonblocking write failed to write any data.
+ * Just return.
+ */
+ return 0;
+ }
+
+ /*
+ * An error, or EOF. Throw away all the data and
+ * return.
+ */
+ if (nbytes == 0)
+ status = EIO;
+ else
+ status = errno;
+
+ buf->last->next = free_buffer_data;
+ free_buffer_data = buf->data;
+ buf->data = NULL;
+ buf->last = NULL;
+
+ return status;
+ }
+
+ data->size -= nbytes;
+ data->bufp += nbytes;
+ }
+
+ buf->data = data->next;
+ data->next = free_buffer_data;
+ free_buffer_data = data;
+ }
+
+ buf->last = NULL;
+
+ return 0;
+}
+
+#ifdef SERVER_FLOWCONTROL
+/*
+ * Set buffer BUF to non-blocking I/O. Returns 0 for success or errno
+ * code.
+ */
+
+static int
+set_nonblock_fd (fd)
+ int fd;
+{
+ int flags;
+
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags < 0)
+ return errno;
+ if (fcntl (fd, F_SETFL, flags | O_NONBLOCK) < 0)
+ return errno;
+ return 0;
+}
+#endif /* SERVER_FLOWCONTROL */
+
+static int
+set_nonblock (buf)
+ struct buffer *buf;
+{
+ int flags;
+
+ if (buf->nonblocking)
+ return 0;
+ flags = fcntl (buf->fd, F_GETFL, 0);
+ if (flags < 0)
+ return errno;
+ if (fcntl (buf->fd, F_SETFL, flags | O_NONBLOCK) < 0)
+ return errno;
+ buf->nonblocking = 1;
+ return 0;
+}
+
+/*
+ * Set buffer BUF to blocking I/O. Returns 0 for success or errno
+ * code.
+ */
+
+static int
+set_block (buf)
+ struct buffer *buf;
+{
+ int flags;
+
+ if (! buf->nonblocking)
+ return 0;
+ flags = fcntl (buf->fd, F_GETFL, 0);
+ if (flags < 0)
+ return errno;
+ if (fcntl (buf->fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
+ return errno;
+ buf->nonblocking = 0;
+ return 0;
+}
+
+/*
+ * Send a character count and some output. Returns errno code or 0 for
+ * success.
+ *
+ * Sending the count in binary is OK since this is only used on a pipe
+ * within the same system.
+ */
+
+static int
+buf_send_counted (buf)
+ struct buffer *buf;
+{
+ int size;
+ struct buffer_data *data;
+
+ if (! buf->output)
+ abort ();
+
+ size = 0;
+ for (data = buf->data; data != NULL; data = data->next)
+ size += data->size;
+
+ data = get_buffer_data ();
+ if (data == NULL)
+ {
+ (*buf->memory_error) (buf);
+ return ENOMEM;
+ }
+
+ data->next = buf->data;
+ buf->data = data;
+ if (buf->last == NULL)
+ buf->last = data;
+
+ data->bufp = data->text;
+ data->size = sizeof (int);
+
+ *((int *) data->text) = size;
+
+ return buf_send_output (buf);
+}
+
+/* Append a list of buffer_data structures to an buffer. */
+
+static inline void
+buf_append_data (buf, data, last)
+ struct buffer *buf;
+ struct buffer_data *data;
+ struct buffer_data *last;
+{
+ if (data != NULL)
+ {
+ if (buf->data == NULL)
+ buf->data = data;
+ else
+ buf->last->next = data;
+ buf->last = last;
+ }
+}
+
+/*
+ * Copy the contents of file F into buffer_data structures. We can't
+ * copy directly into an buffer, because we want to handle failure and
+ * succeess differently. Returns 0 on success, or -2 if out of
+ * memory, or a status code on error. Since the caller happens to
+ * know the size of the file, it is passed in as SIZE. On success,
+ * this function sets *RETP and *LASTP, which may be passed to
+ * buf_append_data.
+ */
+
+static int
+buf_read_file (f, size, retp, lastp)
+ FILE *f;
+ long size;
+ struct buffer_data **retp;
+ struct buffer_data **lastp;
+{
+ int status;
+
+ *retp = NULL;
+ *lastp = NULL;
+
+ while (size > 0)
+ {
+ struct buffer_data *data;
+ int get;
+
+ data = get_buffer_data ();
+ if (data == NULL)
+ {
+ status = -2;
+ goto error_return;
+ }
+
+ if (*retp == NULL)
+ *retp = data;
+ else
+ (*lastp)->next = data;
+ data->next = NULL;
+ *lastp = data;
+
+ data->bufp = data->text;
+ data->size = 0;
+
+ if (size > BUFFER_DATA_SIZE)
+ get = BUFFER_DATA_SIZE;
+ else
+ get = size;
+
+ errno = EIO;
+ if (fread (data->text, get, 1, f) != 1)
+ {
+ status = errno;
+ goto error_return;
+ }
+
+ data->size += get;
+ size -= get;
+ }
+
+ return 0;
+
+ error_return:
+ if (*retp != NULL)
+ {
+ (*lastp)->next = free_buffer_data;
+ free_buffer_data = *retp;
+ }
+ return status;
+}
+
+static int
+buf_read_file_to_eof (f, retp, lastp)
+ FILE *f;
+ struct buffer_data **retp;
+ struct buffer_data **lastp;
+{
+ int status;
+
+ *retp = NULL;
+ *lastp = NULL;
+
+ while (!feof (f))
+ {
+ struct buffer_data *data;
+ int get, nread;
+
+ data = get_buffer_data ();
+ if (data == NULL)
+ {
+ status = -2;
+ goto error_return;
+ }
+
+ if (*retp == NULL)
+ *retp = data;
+ else
+ (*lastp)->next = data;
+ data->next = NULL;
+ *lastp = data;
+
+ data->bufp = data->text;
+ data->size = 0;
+
+ get = BUFFER_DATA_SIZE;
+
+ errno = EIO;
+ nread = fread (data->text, 1, get, f);
+ if (nread == 0 && !feof (f))
+ {
+ status = errno;
+ goto error_return;
+ }
+
+ data->size = nread;
+ }
+
+ return 0;
+
+ error_return:
+ if (*retp != NULL)
+ {
+ (*lastp)->next = free_buffer_data;
+ free_buffer_data = *retp;
+ }
+ return status;
+}
+
+static int
+buf_chain_length (buf)
+ struct buffer_data *buf;
+{
+ int size = 0;
+ while (buf)
+ {
+ size += buf->size;
+ buf = buf->next;
+ }
+ return size;
+}
+
+/*
+ * Read an arbitrary amount of data from a file descriptor into an
+ * input buffer. The file descriptor will be in nonblocking mode, and
+ * we just grab what we can. Return 0 on success, or -1 on end of
+ * file, or -2 if out of memory, or an error code. If COUNTP is not
+ * NULL, *COUNTP is set to the number of bytes read.
+ */
+
+static int
+buf_input_data (buf, countp)
+ struct buffer *buf;
+ int *countp;
+{
+ if (buf->output)
+ abort ();
+
+ if (countp != NULL)
+ *countp = 0;
+
+ while (1)
+ {
+ int get;
+ int nbytes;
+
+ if (buf->data == NULL
+ || (buf->last->bufp + buf->last->size
+ == buf->last->text + BUFFER_DATA_SIZE))
+ {
+ struct buffer_data *data;
+
+ data = get_buffer_data ();
+ if (data == NULL)
+ {
+ (*buf->memory_error) (buf);
+ return -2;
+ }
+
+ if (buf->data == NULL)
+ buf->data = data;
+ else
+ buf->last->next = data;
+ data->next = NULL;
+ buf->last = data;
+
+ data->bufp = data->text;
+ data->size = 0;
+ }
+
+ get = ((buf->last->text + BUFFER_DATA_SIZE)
+ - (buf->last->bufp + buf->last->size));
+ nbytes = read (buf->fd, buf->last->bufp + buf->last->size, get);
+ if (nbytes <= 0)
+ {
+ if (nbytes == 0)
+ {
+ /*
+ * This assumes that we are using POSIX or BSD style
+ * nonblocking I/O. On System V we will get a zero
+ * return if there is no data, even when not at EOF.
+ */
+ return -1;
+ }
+
+ if (errno == EAGAIN
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ )
+ return 0;
+
+ return errno;
+ }
+
+ buf->last->size += nbytes;
+ if (countp != NULL)
+ *countp += nbytes;
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Copy lines from an input buffer to an output buffer. This copies
+ * all complete lines (characters up to a newline) from INBUF to
+ * OUTBUF. Each line in OUTBUF is preceded by the character COMMAND
+ * and a space.
+ */
+
+static void
+buf_copy_lines (outbuf, inbuf, command)
+ struct buffer *outbuf;
+ struct buffer *inbuf;
+ int command;
+{
+ if (! outbuf->output || inbuf->output)
+ abort ();
+
+ while (1)
+ {
+ struct buffer_data *data;
+ struct buffer_data *nldata;
+ char *nl;
+ int len;
+
+ /* See if there is a newline in INBUF. */
+ nldata = NULL;
+ nl = NULL;
+ for (data = inbuf->data; data != NULL; data = data->next)
+ {
+ nl = memchr (data->bufp, '\n', data->size);
+ if (nl != NULL)
+ {
+ nldata = data;
+ break;
+ }
+ }
+
+ if (nldata == NULL)
+ {
+ /* There are no more lines in INBUF. */
+ return;
+ }
+
+ /* Put in the command. */
+ buf_append_char (outbuf, command);
+ buf_append_char (outbuf, ' ');
+
+ if (inbuf->data != nldata)
+ {
+ /*
+ * Simply move over all the buffers up to the one containing
+ * the newline.
+ */
+ for (data = inbuf->data; data->next != nldata; data = data->next)
+ ;
+ data->next = NULL;
+ buf_append_data (outbuf, inbuf->data, data);
+ inbuf->data = nldata;
+ }
+
+ /*
+ * If the newline is at the very end of the buffer, just move
+ * the buffer onto OUTBUF. Otherwise we must copy the data.
+ */
+ len = nl + 1 - nldata->bufp;
+ if (len == nldata->size)
+ {
+ inbuf->data = nldata->next;
+ if (inbuf->data == NULL)
+ inbuf->last = NULL;
+
+ nldata->next = NULL;
+ buf_append_data (outbuf, nldata, nldata);
+ }
+ else
+ {
+ buf_output (outbuf, nldata->bufp, len);
+ nldata->bufp += len;
+ nldata->size -= len;
+ }
+ }
+}
+
+/*
+ * Copy counted data from one buffer to another. The count is an
+ * integer, host size, host byte order (it is only used across a
+ * pipe). If there is enough data, it should be moved over. If there
+ * is not enough data, it should remain on the original buffer. This
+ * returns the number of bytes it needs to see in order to actually
+ * copy something over.
+ */
+
+static int
+buf_copy_counted (outbuf, inbuf)
+ struct buffer *outbuf;
+ struct buffer *inbuf;
+{
+ if (! outbuf->output || inbuf->output)
+ abort ();
+
+ while (1)
+ {
+ struct buffer_data *data;
+ int need;
+ union
+ {
+ char intbuf[sizeof (int)];
+ int i;
+ } u;
+ char *intp;
+ int count;
+ struct buffer_data *start;
+ int startoff;
+ struct buffer_data *stop;
+ int stopwant;
+
+ /* See if we have enough bytes to figure out the count. */
+ need = sizeof (int);
+ intp = u.intbuf;
+ for (data = inbuf->data; data != NULL; data = data->next)
+ {
+ if (data->size >= need)
+ {
+ memcpy (intp, data->bufp, need);
+ break;
+ }
+ memcpy (intp, data->bufp, data->size);
+ intp += data->size;
+ need -= data->size;
+ }
+ if (data == NULL)
+ {
+ /* We don't have enough bytes to form an integer. */
+ return need;
+ }
+
+ count = u.i;
+ start = data;
+ startoff = need;
+
+ /*
+ * We have an integer in COUNT. We have gotten all the data
+ * from INBUF in all buffers before START, and we have gotten
+ * STARTOFF bytes from START. See if we have enough bytes
+ * remaining in INBUF.
+ */
+ need = count - (start->size - startoff);
+ if (need <= 0)
+ {
+ stop = start;
+ stopwant = count;
+ }
+ else
+ {
+ for (data = start->next; data != NULL; data = data->next)
+ {
+ if (need <= data->size)
+ break;
+ need -= data->size;
+ }
+ if (data == NULL)
+ {
+ /* We don't have enough bytes. */
+ return need;
+ }
+ stop = data;
+ stopwant = need;
+ }
+
+ /*
+ * We have enough bytes. Free any buffers in INBUF before
+ * START, and remove STARTOFF bytes from START, so that we can
+ * forget about STARTOFF.
+ */
+ start->bufp += startoff;
+ start->size -= startoff;
+
+ if (start->size == 0)
+ start = start->next;
+
+ if (stop->size == stopwant)
+ {
+ stop = stop->next;
+ stopwant = 0;
+ }
+
+ while (inbuf->data != start)
+ {
+ data = inbuf->data;
+ inbuf->data = data->next;
+ data->next = free_buffer_data;
+ free_buffer_data = data;
+ }
+
+ /*
+ * We want to copy over the bytes from START through STOP. We
+ * only want STOPWANT bytes from STOP.
+ */
+
+ if (start != stop)
+ {
+ /* Attach the buffers from START through STOP to OUTBUF. */
+ for (data = start; data->next != stop; data = data->next)
+ ;
+ inbuf->data = stop;
+ data->next = NULL;
+ buf_append_data (outbuf, start, data);
+ }
+
+ if (stopwant > 0)
+ {
+ buf_output (outbuf, stop->bufp, stopwant);
+ stop->bufp += stopwant;
+ stop->size -= stopwant;
+ }
+ }
+
+ /*NOTREACHED*/
+}
+
+static struct buffer protocol;
+
+static void
+protocol_memory_error (buf)
+ struct buffer *buf;
+{
+ error (1, ENOMEM, "Virtual memory exhausted");
+}
+
+/*
+ * Process IDs of the subprocess, or negative if that subprocess
+ * does not exist.
+ */
+static pid_t command_pid;
+
+static void
+outbuf_memory_error (buf)
+ struct buffer *buf;
+{
+ static const char msg[] = "E Fatal server error\n\
+error ENOMEM Virtual memory exhausted.\n";
+ if (command_pid > 0)
+ kill (command_pid, SIGTERM);
+
+ /*
+ * We have arranged things so that printing this now either will
+ * be legal, or the "E fatal error" line will get glommed onto the
+ * end of an existing "E" or "M" response.
+ */
+
+ /* If this gives an error, not much we could do. syslog() it? */
+ write (STDOUT_FILENO, msg, sizeof (msg) - 1);
+ server_cleanup (0);
+ exit (1);
+}
+
+static void
+input_memory_error (buf)
+ struct buffer *buf;
+{
+ outbuf_memory_error (buf);
+}
+
+/* Execute COMMAND in a subprocess with the approriate funky things done. */
+
+static struct fd_set_wrapper { fd_set fds; } command_fds_to_drain;
+static int max_command_fd;
+
+#ifdef SERVER_FLOWCONTROL
+static int flowcontrol_pipe[2];
+#endif /* SERVER_FLOWCONTROL */
+
+static void
+do_cvs_command (command)
+ int (*command) PROTO((int argc, char **argv));
+{
+ /*
+ * The following file descriptors are set to -1 if that file is not
+ * currently open.
+ */
+
+ /* Data on these pipes is a series of '\n'-terminated lines. */
+ int stdout_pipe[2];
+ int stderr_pipe[2];
+
+ /*
+ * Data on this pipe is a series of counted (see buf_send_counted)
+ * packets. Each packet must be processed atomically (i.e. not
+ * interleaved with data from stdout_pipe or stderr_pipe).
+ */
+ int protocol_pipe[2];
+
+ int dev_null_fd = -1;
+
+ int errs;
+
+ command_pid = -1;
+ stdout_pipe[0] = -1;
+ stdout_pipe[1] = -1;
+ stderr_pipe[0] = -1;
+ stderr_pipe[1] = -1;
+ protocol_pipe[0] = -1;
+ protocol_pipe[1] = -1;
+
+ server_write_entries ();
+
+ if (print_pending_error ())
+ goto free_args_and_return;
+
+ /*
+ * We use a child process which actually does the operation. This
+ * is so we can intercept its standard output. Even if all of CVS
+ * were written to go to some special routine instead of writing
+ * to stdout or stderr, we would still need to do the same thing
+ * for the RCS commands.
+ */
+
+ if (pipe (stdout_pipe) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ if (pipe (stderr_pipe) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ if (pipe (protocol_pipe) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+#ifdef SERVER_FLOWCONTROL
+ if (pipe (flowcontrol_pipe) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ set_nonblock_fd (flowcontrol_pipe[0]);
+ set_nonblock_fd (flowcontrol_pipe[1]);
+#endif /* SERVER_FLOWCONTROL */
+
+ dev_null_fd = open ("/dev/null", O_RDONLY);
+ if (dev_null_fd < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+
+ /* Don't use vfork; we're not going to exec(). */
+ command_pid = fork ();
+ if (command_pid < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ if (command_pid == 0)
+ {
+ int exitstatus;
+
+ /* Since we're in the child, and the parent is going to take
+ care of packaging up our error messages, we can clear this
+ flag. */
+ error_use_protocol = 0;
+
+ protocol.data = protocol.last = NULL;
+ protocol.fd = protocol_pipe[1];
+ protocol.output = 1;
+ protocol.nonblocking = 0;
+ protocol.memory_error = protocol_memory_error;
+
+ if (dup2 (dev_null_fd, STDIN_FILENO) < 0)
+ error (1, errno, "can't set up pipes");
+ if (dup2 (stdout_pipe[1], STDOUT_FILENO) < 0)
+ error (1, errno, "can't set up pipes");
+ if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
+ error (1, errno, "can't set up pipes");
+ close (stdout_pipe[0]);
+ close (stderr_pipe[0]);
+ close (protocol_pipe[0]);
+#ifdef SERVER_FLOWCONTROL
+ close (flowcontrol_pipe[1]);
+#endif /* SERVER_FLOWCONTROL */
+
+ /*
+ * Set this in .bashrc if you want to give yourself time to attach
+ * to the subprocess with a debugger.
+ */
+ if (getenv ("CVS_SERVER_SLEEP"))
+ {
+ int secs = atoi (getenv ("CVS_SERVER_SLEEP"));
+ sleep (secs);
+ }
+
+ exitstatus = (*command) (argument_count, argument_vector);
+
+ /*
+ * When we exit, that will close the pipes, giving an EOF to
+ * the parent.
+ */
+ exit (exitstatus);
+ }
+
+ /* OK, sit around getting all the input from the child. */
+ {
+ struct buffer outbuf;
+ struct buffer stdoutbuf;
+ struct buffer stderrbuf;
+ struct buffer protocol_inbuf;
+ /* Number of file descriptors to check in select (). */
+ int num_to_check;
+ int count_needed = 0;
+#ifdef SERVER_FLOWCONTROL
+ int have_flowcontrolled = 0;
+#endif /* SERVER_FLOWCONTROL */
+
+ FD_ZERO (&command_fds_to_drain.fds);
+ num_to_check = stdout_pipe[0];
+ FD_SET (stdout_pipe[0], &command_fds_to_drain.fds);
+ if (stderr_pipe[0] > num_to_check)
+ num_to_check = stderr_pipe[0];
+ FD_SET (stderr_pipe[0], &command_fds_to_drain.fds);
+ if (protocol_pipe[0] > num_to_check)
+ num_to_check = protocol_pipe[0];
+ FD_SET (protocol_pipe[0], &command_fds_to_drain.fds);
+ if (STDOUT_FILENO > num_to_check)
+ num_to_check = STDOUT_FILENO;
+ max_command_fd = num_to_check;
+ /*
+ * File descriptors are numbered from 0, so num_to_check needs to
+ * be one larger than the largest descriptor.
+ */
+ ++num_to_check;
+ if (num_to_check > FD_SETSIZE)
+ {
+ printf ("E internal error: FD_SETSIZE not big enough.\nerror \n");
+ goto error_exit;
+ }
+
+ outbuf.data = outbuf.last = NULL;
+ outbuf.fd = STDOUT_FILENO;
+ outbuf.output = 1;
+ outbuf.nonblocking = 0;
+ outbuf.memory_error = outbuf_memory_error;
+
+ stdoutbuf.data = stdoutbuf.last = NULL;
+ stdoutbuf.fd = stdout_pipe[0];
+ stdoutbuf.output = 0;
+ stdoutbuf.nonblocking = 0;
+ stdoutbuf.memory_error = input_memory_error;
+
+ stderrbuf.data = stderrbuf.last = NULL;
+ stderrbuf.fd = stderr_pipe[0];
+ stderrbuf.output = 0;
+ stderrbuf.nonblocking = 0;
+ stderrbuf.memory_error = input_memory_error;
+
+ protocol_inbuf.data = protocol_inbuf.last = NULL;
+ protocol_inbuf.fd = protocol_pipe[0];
+ protocol_inbuf.output = 0;
+ protocol_inbuf.nonblocking = 0;
+ protocol_inbuf.memory_error = input_memory_error;
+
+ set_nonblock (&outbuf);
+ set_nonblock (&stdoutbuf);
+ set_nonblock (&stderrbuf);
+ set_nonblock (&protocol_inbuf);
+
+ if (close (stdout_pipe[1]) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ stdout_pipe[1] = -1;
+
+ if (close (stderr_pipe[1]) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ stderr_pipe[1] = -1;
+
+ if (close (protocol_pipe[1]) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ protocol_pipe[1] = -1;
+
+#ifdef SERVER_FLOWCONTROL
+ if (close (flowcontrol_pipe[0]) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ flowcontrol_pipe[0] = -1;
+#endif /* SERVER_FLOWCONTROL */
+
+ if (close (dev_null_fd) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ dev_null_fd = -1;
+
+ while (stdout_pipe[0] >= 0
+ || stderr_pipe[0] >= 0
+ || protocol_pipe[0] >= 0)
+ {
+ fd_set readfds;
+ fd_set writefds;
+ int numfds;
+#ifdef SERVER_FLOWCONTROL
+ int bufmemsize;
+
+ /*
+ * See if we are swamping the remote client and filling our VM.
+ * Tell child to hold off if we do.
+ */
+ bufmemsize = buf_count_mem (&outbuf);
+ if (!have_flowcontrolled && (bufmemsize > SERVER_HI_WATER))
+ {
+ if (write(flowcontrol_pipe[1], "S", 1) == 1)
+ have_flowcontrolled = 1;
+ }
+ else if (have_flowcontrolled && (bufmemsize < SERVER_LO_WATER))
+ {
+ if (write(flowcontrol_pipe[1], "G", 1) == 1)
+ have_flowcontrolled = 0;
+ }
+#endif /* SERVER_FLOWCONTROL */
+
+ FD_ZERO (&readfds);
+ FD_ZERO (&writefds);
+ if (! buf_empty_p (&outbuf))
+ FD_SET (STDOUT_FILENO, &writefds);
+
+ if (stdout_pipe[0] >= 0)
+ {
+ FD_SET (stdout_pipe[0], &readfds);
+ }
+ if (stderr_pipe[0] >= 0)
+ {
+ FD_SET (stderr_pipe[0], &readfds);
+ }
+ if (protocol_pipe[0] >= 0)
+ {
+ FD_SET (protocol_pipe[0], &readfds);
+ }
+
+ do {
+ /* This used to select on exceptions too, but as far
+ as I know there was never any reason to do that and
+ SCO doesn't let you select on exceptions on pipes. */
+ numfds = select (num_to_check, &readfds, &writefds,
+ (fd_set *)0, (struct timeval *)NULL);
+ if (numfds < 0
+ && errno != EINTR)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ } while (numfds < 0);
+
+ if (FD_ISSET (STDOUT_FILENO, &writefds))
+ {
+ /* What should we do with errors? syslog() them? */
+ buf_send_output (&outbuf);
+ }
+
+ if (stdout_pipe[0] >= 0
+ && (FD_ISSET (stdout_pipe[0], &readfds)))
+ {
+ int status;
+
+ status = buf_input_data (&stdoutbuf, (int *) NULL);
+
+ buf_copy_lines (&outbuf, &stdoutbuf, 'M');
+
+ if (status == -1)
+ stdout_pipe[0] = -1;
+ else if (status > 0)
+ {
+ print_error (status);
+ goto error_exit;
+ }
+
+ /* What should we do with errors? syslog() them? */
+ buf_send_output (&outbuf);
+ }
+
+ if (stderr_pipe[0] >= 0
+ && (FD_ISSET (stderr_pipe[0], &readfds)))
+ {
+ int status;
+
+ status = buf_input_data (&stderrbuf, (int *) NULL);
+
+ buf_copy_lines (&outbuf, &stderrbuf, 'E');
+
+ if (status == -1)
+ stderr_pipe[0] = -1;
+ else if (status > 0)
+ {
+ print_error (status);
+ goto error_exit;
+ }
+
+ /* What should we do with errors? syslog() them? */
+ buf_send_output (&outbuf);
+ }
+
+ if (protocol_pipe[0] >= 0
+ && (FD_ISSET (protocol_pipe[0], &readfds)))
+ {
+ int status;
+ int count_read;
+
+ status = buf_input_data (&protocol_inbuf, &count_read);
+
+ /*
+ * We only call buf_copy_counted if we have read
+ * enough bytes to make it worthwhile. This saves us
+ * from continually recounting the amount of data we
+ * have.
+ */
+ count_needed -= count_read;
+ if (count_needed <= 0)
+ count_needed = buf_copy_counted (&outbuf, &protocol_inbuf);
+
+ if (status == -1)
+ protocol_pipe[0] = -1;
+ else if (status > 0)
+ {
+ print_error (status);
+ goto error_exit;
+ }
+
+ /* What should we do with errors? syslog() them? */
+ buf_send_output (&outbuf);
+ }
+ }
+
+ /*
+ * OK, we've gotten EOF on all the pipes. If there is
+ * anything left on stdoutbuf or stderrbuf (this could only
+ * happen if there was no trailing newline), send it over.
+ */
+ if (! buf_empty_p (&stdoutbuf))
+ {
+ buf_append_char (&stdoutbuf, '\n');
+ buf_copy_lines (&outbuf, &stdoutbuf, 'M');
+ }
+ if (! buf_empty_p (&stderrbuf))
+ {
+ buf_append_char (&stderrbuf, '\n');
+ buf_copy_lines (&outbuf, &stderrbuf, 'E');
+ }
+ if (! buf_empty_p (&protocol_inbuf))
+ buf_output0 (&outbuf,
+ "E Protocol error: uncounted data discarded\n");
+
+ errs = 0;
+
+ while (command_pid > 0)
+ {
+ int status;
+ pid_t waited_pid;
+ waited_pid = waitpid (command_pid, &status, 0);
+ if (waited_pid < 0)
+ {
+ /*
+ * Intentionally ignoring EINTR. Other errors
+ * "can't happen".
+ */
+ continue;
+ }
+
+ if (WIFEXITED (status))
+ errs += WEXITSTATUS (status);
+ else
+ {
+ int sig = WTERMSIG (status);
+ /*
+ * This is really evil, because signals might be numbered
+ * differently on the two systems. We should be using
+ * signal names (either of the "Terminated" or the "SIGTERM"
+ * variety). But cvs doesn't currently use libiberty...we
+ * could roll our own.... FIXME.
+ */
+ printf ("E Terminated with fatal signal %d\n", sig);
+
+ /* Test for a core dump. Is this portable? */
+ if (status & 0x80)
+ {
+ printf ("E Core dumped; preserving %s on server.\n\
+E CVS locks may need cleaning up.\n",
+ server_temp_dir);
+ dont_delete_temp = 1;
+ }
+ ++errs;
+ }
+ if (waited_pid == command_pid)
+ command_pid = -1;
+ }
+
+ /*
+ * OK, we've waited for the child. By now all CVS locks are free
+ * and it's OK to block on the network.
+ */
+ set_block (&outbuf);
+ buf_send_output (&outbuf);
+ }
+
+ if (errs)
+ /* We will have printed an error message already. */
+ printf ("error \n");
+ else
+ printf ("ok\n");
+ goto free_args_and_return;
+
+ error_exit:
+ if (command_pid > 0)
+ kill (command_pid, SIGTERM);
+
+ while (command_pid > 0)
+ {
+ pid_t waited_pid;
+ waited_pid = waitpid (command_pid, (int *) 0, 0);
+ if (waited_pid < 0 && errno == EINTR)
+ continue;
+ if (waited_pid == command_pid)
+ command_pid = -1;
+ }
+
+ close (dev_null_fd);
+ close (protocol_pipe[0]);
+ close (protocol_pipe[1]);
+ close (stderr_pipe[0]);
+ close (stderr_pipe[1]);
+ close (stdout_pipe[0]);
+ close (stdout_pipe[1]);
+
+ free_args_and_return:
+ /* Now free the arguments. */
+ {
+ /* argument_vector[0] is a dummy argument, we don't mess with it. */
+ char **cp;
+ for (cp = argument_vector + 1;
+ cp < argument_vector + argument_count;
+ ++cp)
+ free (*cp);
+
+ argument_count = 1;
+ }
+ return;
+}
+
+#ifdef SERVER_FLOWCONTROL
+/*
+ * Called by the child at convenient points in the server's execution for
+ * the server child to block.. ie: when it has no locks active.
+ */
+void
+server_pause_check()
+{
+ int paused = 0;
+ char buf[1];
+
+ while (read (flowcontrol_pipe[0], buf, 1) == 1)
+ {
+ if (*buf == 'S') /* Stop */
+ paused = 1;
+ else if (*buf == 'G') /* Go */
+ paused = 0;
+ else
+ return; /* ??? */
+ }
+ while (paused) {
+ int numfds, numtocheck;
+ fd_set fds;
+
+ FD_ZERO (&fds);
+ FD_SET (flowcontrol_pipe[0], &fds);
+ numtocheck = flowcontrol_pipe[0] + 1;
+
+ do {
+ numfds = select (numtocheck, &fds, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)NULL);
+ if (numfds < 0
+ && errno != EINTR)
+ {
+ print_error (errno);
+ return;
+ }
+ } while (numfds < 0);
+
+ if (FD_ISSET (flowcontrol_pipe[0], &fds))
+ {
+ while (read (flowcontrol_pipe[0], buf, 1) == 1)
+ {
+ if (*buf == 'S') /* Stop */
+ paused = 1;
+ else if (*buf == 'G') /* Go */
+ paused = 0;
+ else
+ return; /* ??? */
+ }
+ }
+ }
+}
+#endif /* SERVER_FLOWCONTROL */
+
+static void output_dir PROTO((char *, char *));
+
+static void
+output_dir (update_dir, repository)
+ char *update_dir;
+ char *repository;
+{
+ if (use_dir_and_repos)
+ {
+ if (update_dir[0] == '\0')
+ buf_output0 (&protocol, ".");
+ else
+ buf_output0 (&protocol, update_dir);
+ buf_output0 (&protocol, "/\n");
+ }
+ buf_output0 (&protocol, repository);
+ buf_output0 (&protocol, "/");
+}
+
+/*
+ * Entries line that we are squirreling away to send to the client when
+ * we are ready.
+ */
+static char *entries_line;
+
+/*
+ * File which has been Scratch_File'd, we are squirreling away that fact
+ * to inform the client when we are ready.
+ */
+static char *scratched_file;
+
+/*
+ * The scratched_file will need to be removed as well as having its entry
+ * removed.
+ */
+static int kill_scratched_file;
+
+void
+server_register (name, version, timestamp, options, tag, date, conflict)
+ char *name;
+ char *version;
+ char *timestamp;
+ char *options;
+ char *tag;
+ char *date;
+ char *conflict;
+{
+ int len;
+
+ if (trace)
+ {
+ (void) fprintf (stderr,
+ "%c-> server_register(%s, %s, %s, %s, %s, %s, %s)\n",
+ (server_active) ? 'S' : ' ', /* silly */
+ name, version, timestamp, options, tag ? tag : "",
+ date ? date : "", conflict ? conflict : "");
+ }
+
+ if (options == NULL)
+ options = "";
+
+ if (entries_line != NULL)
+ {
+ /*
+ * If CVS decides to Register it more than once (which happens
+ * on "cvs update foo/foo.c" where foo and foo.c are already
+ * checked out), use the last of the entries lines Register'd.
+ */
+ free (entries_line);
+ }
+
+ /*
+ * I have reports of Scratch_Entry and Register both happening, in
+ * two different cases. Using the last one which happens is almost
+ * surely correct; I haven't tracked down why they both happen (or
+ * even verified that they are for the same file).
+ */
+ if (scratched_file != NULL)
+ {
+ free (scratched_file);
+ scratched_file = NULL;
+ }
+
+ len = (strlen (name) + strlen (version) + strlen (options) + 80);
+ if (tag)
+ len += strlen (tag);
+ if (date)
+ len += strlen (date);
+
+ entries_line = xmalloc (len);
+ sprintf (entries_line, "/%s/%s/", name, version);
+ if (conflict != NULL)
+ {
+ strcat (entries_line, "+=");
+ }
+ strcat (entries_line, "/");
+ strcat (entries_line, options);
+ strcat (entries_line, "/");
+ if (tag != NULL)
+ {
+ strcat (entries_line, "T");
+ strcat (entries_line, tag);
+ }
+ else if (date != NULL)
+ {
+ strcat (entries_line, "D");
+ strcat (entries_line, date);
+ }
+}
+
+void
+server_scratch (fname)
+ char *fname;
+{
+ /*
+ * I have reports of Scratch_Entry and Register both happening, in
+ * two different cases. Using the last one which happens is almost
+ * surely correct; I haven't tracked down why they both happen (or
+ * even verified that they are for the same file).
+ */
+ if (entries_line != NULL)
+ {
+ free (entries_line);
+ entries_line = NULL;
+ }
+
+ if (scratched_file != NULL)
+ {
+ buf_output0 (&protocol,
+ "E CVS server internal error: duplicate Scratch_Entry\n");
+ buf_send_counted (&protocol);
+ return;
+ }
+ scratched_file = xstrdup (fname);
+ kill_scratched_file = 1;
+}
+
+void
+server_scratch_entry_only ()
+{
+ kill_scratched_file = 0;
+}
+
+/* Print a new entries line, from a previous server_register. */
+static void
+new_entries_line ()
+{
+ if (entries_line)
+ {
+ buf_output0 (&protocol, entries_line);
+ buf_output (&protocol, "\n", 1);
+ }
+ else
+ /* Return the error message as the Entries line. */
+ buf_output0 (&protocol,
+ "CVS server internal error: Register missing\n");
+ free (entries_line);
+ entries_line = NULL;
+}
+
+static void
+serve_ci (arg)
+ char *arg;
+{
+ do_cvs_command (commit);
+}
+
+void
+server_checked_in (file, update_dir, repository)
+ char *file;
+ char *update_dir;
+ char *repository;
+{
+ if (noexec)
+ return;
+ if (scratched_file != NULL && entries_line == NULL)
+ {
+ /*
+ * This happens if we are now doing a "cvs remove" after a previous
+ * "cvs add" (without a "cvs ci" in between).
+ */
+ buf_output0 (&protocol, "Remove-entry ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ free (scratched_file);
+ scratched_file = NULL;
+ }
+ else
+ {
+ buf_output0 (&protocol, "Checked-in ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ new_entries_line ();
+ }
+ buf_send_counted (&protocol);
+}
+
+void
+server_update_entries (file, update_dir, repository, updated)
+ char *file;
+ char *update_dir;
+ char *repository;
+ enum server_updated_arg4 updated;
+{
+ if (noexec)
+ return;
+ if (updated == SERVER_UPDATED)
+ buf_output0 (&protocol, "Checked-in ");
+ else
+ {
+ if (!supported_response ("New-entry"))
+ return;
+ buf_output0 (&protocol, "New-entry ");
+ }
+
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ new_entries_line ();
+ buf_send_counted (&protocol);
+}
+
+static void
+serve_update (arg)
+ char *arg;
+{
+ do_cvs_command (update);
+}
+
+static void
+serve_diff (arg)
+ char *arg;
+{
+ do_cvs_command (diff);
+}
+
+static void
+serve_log (arg)
+ char *arg;
+{
+ do_cvs_command (cvslog);
+}
+
+static void
+serve_add (arg)
+ char *arg;
+{
+ do_cvs_command (add);
+}
+
+static void
+serve_remove (arg)
+ char *arg;
+{
+ do_cvs_command (cvsremove);
+}
+
+static void
+serve_status (arg)
+ char *arg;
+{
+ do_cvs_command (status);
+}
+
+static void
+serve_rdiff (arg)
+ char *arg;
+{
+ do_cvs_command (patch);
+}
+
+static void
+serve_tag (arg)
+ char *arg;
+{
+ do_cvs_command (tag);
+}
+
+static void
+serve_rtag (arg)
+ char *arg;
+{
+ do_cvs_command (rtag);
+}
+
+static void
+serve_import (arg)
+ char *arg;
+{
+ do_cvs_command (import);
+}
+
+static void
+serve_admin (arg)
+ char *arg;
+{
+ do_cvs_command (admin);
+}
+
+static void
+serve_history (arg)
+ char *arg;
+{
+ do_cvs_command (history);
+}
+
+static void
+serve_release (arg)
+ char *arg;
+{
+ do_cvs_command (release);
+}
+
+static void
+serve_co (arg)
+ char *arg;
+{
+ char *tempdir;
+ int status;
+
+ if (print_pending_error ())
+ return;
+
+ if (!isdir (CVSADM))
+ {
+ /*
+ * The client has not sent a "Repository" line. Check out
+ * into a pristine directory.
+ */
+ tempdir = malloc (strlen (server_temp_dir) + 80);
+ if (tempdir == NULL)
+ {
+ printf ("E Out of memory\n");
+ return;
+ }
+ strcpy (tempdir, server_temp_dir);
+ strcat (tempdir, "/checkout-dir");
+ status = mkdir_p (tempdir);
+ if (status != 0 && status != EEXIST)
+ {
+ printf ("E Cannot create %s\n", tempdir);
+ print_error (errno);
+ free (tempdir);
+ return;
+ }
+
+ if (chdir (tempdir) < 0)
+ {
+ printf ("E Cannot change to directory %s\n", tempdir);
+ print_error (errno);
+ free (tempdir);
+ return;
+ }
+ free (tempdir);
+ }
+ do_cvs_command (checkout);
+}
+
+static void
+serve_export (arg)
+ char *arg;
+{
+ /* Tell checkout() to behave like export not checkout. */
+ command_name = "export";
+ serve_co (arg);
+}
+
+void
+server_copy_file (file, update_dir, repository, newfile)
+ char *file;
+ char *update_dir;
+ char *repository;
+ char *newfile;
+{
+ if (!supported_response ("Copy-file"))
+ return;
+ buf_output0 (&protocol, "Copy-file ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output0 (&protocol, "\n");
+ buf_output0 (&protocol, newfile);
+ buf_output0 (&protocol, "\n");
+}
+
+void
+server_updated (file, update_dir, repository, updated, file_info, checksum)
+ char *file;
+ char *update_dir;
+ char *repository;
+ enum server_updated_arg4 updated;
+ struct stat *file_info;
+ unsigned char *checksum;
+{
+ char *short_pathname;
+
+ if (noexec)
+ return;
+
+ short_pathname = xmalloc (strlen (update_dir) + strlen (file) + 10);
+ if (update_dir[0] == '\0')
+ strcpy (short_pathname, file);
+ else
+ sprintf (short_pathname, "%s/%s", update_dir, file);
+
+ if (entries_line != NULL && scratched_file == NULL)
+ {
+ FILE *f;
+ struct stat sb;
+ struct buffer_data *list, *last;
+ unsigned long size;
+ char size_text[80];
+
+ if (stat (file, &sb) < 0)
+ {
+ if (existence_error (errno))
+ {
+ /*
+ * If we have a sticky tag for a branch on which the
+ * file is dead, and cvs update the directory, it gets
+ * a T_CHECKOUT but no file. So in this case just
+ * forget the whole thing.
+ */
+ free (entries_line);
+ entries_line = NULL;
+ goto done;
+ }
+ error (1, errno, "reading %s", short_pathname);
+ }
+
+ if (checksum != NULL)
+ {
+ static int checksum_supported = -1;
+
+ if (checksum_supported == -1)
+ {
+ checksum_supported = supported_response ("Checksum");
+ }
+
+ if (checksum_supported)
+ {
+ int i;
+ char buf[3];
+
+ buf_output0 (&protocol, "Checksum ");
+ for (i = 0; i < 16; i++)
+ {
+ sprintf (buf, "%02x", (unsigned int) checksum[i]);
+ buf_output0 (&protocol, buf);
+ }
+ buf_append_char (&protocol, '\n');
+ }
+ }
+
+ if (updated == SERVER_UPDATED)
+ buf_output0 (&protocol, "Updated ");
+ else if (updated == SERVER_MERGED)
+ buf_output0 (&protocol, "Merged ");
+ else if (updated == SERVER_PATCHED)
+ buf_output0 (&protocol, "Patched ");
+ else
+ abort ();
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+
+ new_entries_line ();
+
+ {
+ char *mode_string;
+
+ /* FIXME: When we check out files the umask of the server
+ (set in .bashrc if rsh is in use, or set in main.c in
+ the kerberos case, I think) affects what mode we send,
+ and it shouldn't. */
+ if (file_info != NULL)
+ mode_string = mode_to_string (file_info->st_mode);
+ else
+ mode_string = mode_to_string (sb.st_mode);
+ buf_output0 (&protocol, mode_string);
+ buf_output0 (&protocol, "\n");
+ free (mode_string);
+ }
+
+ list = last = NULL;
+ size = 0;
+ if (sb.st_size > 0)
+ {
+ if (gzip_level
+ /*
+ * For really tiny files, the gzip process startup
+ * time will outweigh the compression savings. This
+ * might be computable somehow; using 100 here is just
+ * a first approximation.
+ */
+ && sb.st_size > 100)
+ {
+ int status, fd, gzip_status;
+ pid_t gzip_pid;
+
+ fd = open (file, O_RDONLY, 0);
+ if (fd < 0)
+ error (1, errno, "reading %s", short_pathname);
+ fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
+ f = fdopen (fd, "r");
+ status = buf_read_file_to_eof (f, &list, &last);
+ size = buf_chain_length (list);
+ if (status == -2)
+ (*protocol.memory_error) (&protocol);
+ else if (status != 0)
+ error (1, ferror (f) ? errno : 0, "reading %s",
+ short_pathname);
+ if (fclose (f) == EOF)
+ error (1, errno, "reading %s", short_pathname);
+ if (waitpid (gzip_pid, &gzip_status, 0) == -1)
+ error (1, errno, "waiting for gzip process %d", gzip_pid);
+ else if (gzip_status != 0)
+ error (1, 0, "gzip exited %d", gzip_status);
+ /* Prepending length with "z" is flag for using gzip here. */
+ buf_output0 (&protocol, "z");
+ }
+ else
+ {
+ long status;
+
+ size = sb.st_size;
+ f = fopen (file, "r");
+ if (f == NULL)
+ error (1, errno, "reading %s", short_pathname);
+ status = buf_read_file (f, sb.st_size, &list, &last);
+ if (status == -2)
+ (*protocol.memory_error) (&protocol);
+ else if (status != 0)
+ error (1, ferror (f) ? errno : 0, "reading %s",
+ short_pathname);
+ if (fclose (f) == EOF)
+ error (1, errno, "reading %s", short_pathname);
+ }
+ }
+
+ sprintf (size_text, "%lu\n", size);
+ buf_output0 (&protocol, size_text);
+
+ buf_append_data (&protocol, list, last);
+ /* Note we only send a newline here if the file ended with one. */
+
+ /*
+ * Avoid using up too much disk space for temporary files.
+ * A file which does not exist indicates that the file is up-to-date,
+ * which is now the case. If this is SERVER_MERGED, the file is
+ * not up-to-date, and we indicate that by leaving the file there.
+ * I'm thinking of cases like "cvs update foo/foo.c foo".
+ */
+ if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED)
+ /* But if we are joining, we'll need the file when we call
+ join_file. */
+ && !joining ())
+ unlink (file);
+ }
+ else if (scratched_file != NULL && entries_line == NULL)
+ {
+ if (strcmp (scratched_file, file) != 0)
+ error (1, 0,
+ "CVS server internal error: `%s' vs. `%s' scratched",
+ scratched_file,
+ file);
+ free (scratched_file);
+ scratched_file = NULL;
+
+ if (kill_scratched_file)
+ buf_output0 (&protocol, "Removed ");
+ else
+ buf_output0 (&protocol, "Remove-entry ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ }
+ else if (scratched_file == NULL && entries_line == NULL)
+ {
+ /*
+ * This can happen with death support if we were processing
+ * a dead file in a checkout.
+ */
+ }
+ else
+ error (1, 0,
+ "CVS server internal error: Register *and* Scratch_Entry.\n");
+ buf_send_counted (&protocol);
+ done:
+ free (short_pathname);
+}
+
+void
+server_set_entstat (update_dir, repository)
+ char *update_dir;
+ char *repository;
+{
+ static int set_static_supported = -1;
+ if (set_static_supported == -1)
+ set_static_supported = supported_response ("Set-static-directory");
+ if (!set_static_supported) return;
+
+ buf_output0 (&protocol, "Set-static-directory ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, "\n");
+ buf_send_counted (&protocol);
+}
+
+void
+server_clear_entstat (update_dir, repository)
+ char *update_dir;
+ char *repository;
+{
+ static int clear_static_supported = -1;
+ if (clear_static_supported == -1)
+ clear_static_supported = supported_response ("Clear-static-directory");
+ if (!clear_static_supported) return;
+
+ if (noexec)
+ return;
+
+ buf_output0 (&protocol, "Clear-static-directory ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, "\n");
+ buf_send_counted (&protocol);
+}
+
+void
+server_set_sticky (update_dir, repository, tag, date)
+ char *update_dir;
+ char *repository;
+ char *tag;
+ char *date;
+{
+ static int set_sticky_supported = -1;
+ if (set_sticky_supported == -1)
+ set_sticky_supported = supported_response ("Set-sticky");
+ if (!set_sticky_supported) return;
+
+ if (noexec)
+ return;
+
+ if (tag == NULL && date == NULL)
+ {
+ buf_output0 (&protocol, "Clear-sticky ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, "\n");
+ }
+ else
+ {
+ buf_output0 (&protocol, "Set-sticky ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, "\n");
+ if (tag != NULL)
+ {
+ buf_output0 (&protocol, "T");
+ buf_output0 (&protocol, tag);
+ }
+ else
+ {
+ buf_output0 (&protocol, "D");
+ buf_output0 (&protocol, date);
+ }
+ buf_output0 (&protocol, "\n");
+ }
+ buf_send_counted (&protocol);
+}
+
+static void
+serve_gzip_contents (arg)
+ char *arg;
+{
+ int level;
+ level = atoi (arg);
+ if (level == 0)
+ level = 6;
+ gzip_level = level;
+}
+
+static void
+serve_ignore (arg)
+ char *arg;
+{
+ /*
+ * Just ignore this command. This is used to support the
+ * update-patches command, which is not a real command, but a signal
+ * to the client that update will accept the -u argument.
+ */
+}
+
+static int
+expand_proc (pargc, argv, where, mwhere, mfile, shorten,
+ local_specified, omodule, msg)
+ int *pargc;
+ char **argv;
+ char *where;
+ char *mwhere;
+ char *mfile;
+ int shorten;
+ int local_specified;
+ char *omodule;
+ char *msg;
+{
+ int i;
+ char *dir = argv[0];
+
+ /* If mwhere has been specified, the thing we're expanding is a
+ module -- just return its name so the client will ask for the
+ right thing later. If it is an alias or a real directory,
+ mwhere will not be set, so send out the appropriate
+ expansion. */
+
+ if (mwhere != NULL)
+ printf ("Module-expansion %s\n", mwhere);
+ else
+ {
+ /* We may not need to do this anymore -- check the definition
+ of aliases before removing */
+ if (*pargc == 1)
+ printf ("Module-expansion %s\n", dir);
+ else
+ for (i = 1; i < *pargc; ++i)
+ printf ("Module-expansion %s/%s\n", dir, argv[i]);
+ }
+ return 0;
+}
+
+static void
+serve_expand_modules (arg)
+ char *arg;
+{
+ int i;
+ int err;
+ DBM *db;
+ err = 0;
+
+ /*
+ * FIXME: error handling is bogus; do_module can write to stdout and/or
+ * stderr and we're not using do_cvs_command.
+ */
+
+ server_expanding = 1;
+ db = open_module ();
+ for (i = 1; i < argument_count; i++)
+ err += do_module (db, argument_vector[i],
+ CHECKOUT, "Updating", expand_proc,
+ NULL, 0, 0, 0,
+ (char *) NULL);
+ close_module (db);
+ server_expanding = 0;
+ {
+ /* argument_vector[0] is a dummy argument, we don't mess with it. */
+ char **cp;
+ for (cp = argument_vector + 1;
+ cp < argument_vector + argument_count;
+ ++cp)
+ free (*cp);
+
+ argument_count = 1;
+ }
+ if (err)
+ /* We will have printed an error message already. */
+ printf ("error \n");
+ else
+ printf ("ok\n");
+}
+
+void
+server_prog (dir, name, which)
+ char *dir;
+ char *name;
+ enum progs which;
+{
+ if (!supported_response ("Set-checkin-prog"))
+ {
+ printf ("E \
+warning: this client does not support -i or -u flags in the modules file.\n");
+ return;
+ }
+ switch (which)
+ {
+ case PROG_CHECKIN:
+ printf ("Set-checkin-prog ");
+ break;
+ case PROG_UPDATE:
+ printf ("Set-update-prog ");
+ break;
+ }
+ printf ("%s\n%s\n", dir, name);
+}
+
+static void
+serve_checkin_prog (arg)
+ char *arg;
+{
+ FILE *f;
+ f = fopen (CVSADM_CIPROG, "w+");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_CIPROG));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_CIPROG);
+ return;
+ }
+ if (fprintf (f, "%s\n", arg) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_CIPROG));
+ sprintf(pending_error_text, "E cannot write to %s", CVSADM_CIPROG);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_CIPROG));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_CIPROG);
+ return;
+ }
+}
+
+static void
+serve_update_prog (arg)
+ char *arg;
+{
+ FILE *f;
+ f = fopen (CVSADM_UPROG, "w+");
+ if (f == NULL)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_UPROG));
+ sprintf(pending_error_text, "E cannot open %s", CVSADM_UPROG);
+ return;
+ }
+ if (fprintf (f, "%s\n", arg) < 0)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_UPROG));
+ sprintf(pending_error_text, "E cannot write to %s", CVSADM_UPROG);
+ return;
+ }
+ if (fclose (f) == EOF)
+ {
+ pending_error = errno;
+ pending_error_text = malloc (80 + strlen(CVSADM_UPROG));
+ sprintf(pending_error_text, "E cannot close %s", CVSADM_UPROG);
+ return;
+ }
+}
+
+static void serve_valid_requests PROTO((char *arg));
+
+#endif /* SERVER_SUPPORT */
+#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
+
+/*
+ * Parts of this table are shared with the client code,
+ * but the client doesn't need to know about the handler
+ * functions.
+ */
+
+struct request requests[] =
+{
+#ifdef SERVER_SUPPORT
+#define REQ_LINE(n, f, s) {n, f, s}
+#else
+#define REQ_LINE(n, f, s) {n, s}
+#endif
+
+ REQ_LINE("Root", serve_root, rq_essential),
+ REQ_LINE("Valid-responses", serve_valid_responses, rq_essential),
+ REQ_LINE("valid-requests", serve_valid_requests, rq_essential),
+ REQ_LINE("Repository", serve_repository, rq_essential),
+ REQ_LINE("Directory", serve_directory, rq_optional),
+ REQ_LINE("Max-dotdot", serve_max_dotdot, rq_optional),
+ REQ_LINE("Static-directory", serve_static_directory, rq_optional),
+ REQ_LINE("Sticky", serve_sticky, rq_optional),
+ REQ_LINE("Checkin-prog", serve_checkin_prog, rq_optional),
+ REQ_LINE("Update-prog", serve_update_prog, rq_optional),
+ REQ_LINE("Entry", serve_entry, rq_essential),
+ REQ_LINE("Modified", serve_modified, rq_essential),
+ REQ_LINE("Lost", serve_lost, rq_optional),
+ REQ_LINE("UseUnchanged", serve_enable_unchanged, rq_enableme),
+ REQ_LINE("Unchanged", serve_unchanged, rq_optional),
+ REQ_LINE("Argument", serve_argument, rq_essential),
+ REQ_LINE("Argumentx", serve_argumentx, rq_essential),
+ REQ_LINE("Global_option", serve_global_option, rq_optional),
+ REQ_LINE("expand-modules", serve_expand_modules, rq_optional),
+ REQ_LINE("ci", serve_ci, rq_essential),
+ REQ_LINE("co", serve_co, rq_essential),
+ REQ_LINE("update", serve_update, rq_essential),
+ REQ_LINE("diff", serve_diff, rq_optional),
+ REQ_LINE("log", serve_log, rq_optional),
+ REQ_LINE("add", serve_add, rq_optional),
+ REQ_LINE("remove", serve_remove, rq_optional),
+ REQ_LINE("update-patches", serve_ignore, rq_optional),
+ REQ_LINE("gzip-file-contents", serve_gzip_contents, rq_optional),
+ REQ_LINE("status", serve_status, rq_optional),
+ REQ_LINE("rdiff", serve_rdiff, rq_optional),
+ REQ_LINE("tag", serve_tag, rq_optional),
+ REQ_LINE("rtag", serve_rtag, rq_optional),
+ REQ_LINE("import", serve_import, rq_optional),
+ REQ_LINE("admin", serve_admin, rq_optional),
+ REQ_LINE("export", serve_export, rq_optional),
+ REQ_LINE("history", serve_history, rq_optional),
+ REQ_LINE("release", serve_release, rq_optional),
+ REQ_LINE(NULL, NULL, rq_optional)
+
+#undef REQ_LINE
+};
+
+#endif /* SERVER_SUPPORT or CLIENT_SUPPORT */
+#ifdef SERVER_SUPPORT
+
+static void
+serve_valid_requests (arg)
+ char *arg;
+{
+ struct request *rq;
+ if (print_pending_error ())
+ return;
+ printf ("Valid-requests");
+ for (rq = requests; rq->name != NULL; rq++)
+ if (rq->func != NULL)
+ printf (" %s", rq->name);
+ printf ("\nok\n");
+}
+
+#ifdef sun
+/*
+ * Delete temporary files. SIG is the signal making this happen, or
+ * 0 if not called as a result of a signal.
+ */
+static int command_pid_is_dead;
+static void wait_sig (sig)
+ int sig;
+{
+ int status;
+ pid_t r = wait (&status);
+ if (r == command_pid)
+ command_pid_is_dead++;
+}
+#endif
+
+void
+server_cleanup (sig)
+ int sig;
+{
+ /* Do "rm -rf" on the temp directory. */
+ int len;
+ char *cmd;
+ char *temp_dir;
+
+ if (dont_delete_temp)
+ return;
+
+ /* What a bogus kludge. This disgusting code makes all kinds of
+ assumptions about SunOS, and is only for a bug in that system.
+ So only enable it on Suns. */
+#ifdef sun
+ if (command_pid > 0) {
+ /* To avoid crashes on SunOS due to bugs in SunOS tmpfs
+ triggered by the use of rename() in RCS, wait for the
+ subprocess to die. Unfortunately, this means draining output
+ while waiting for it to unblock the signal we sent it. Yuck! */
+ int status;
+ pid_t r;
+
+ signal (SIGCHLD, wait_sig);
+ if (sig)
+ /* Perhaps SIGTERM would be more correct. But the child
+ process will delay the SIGINT delivery until its own
+ children have exited. */
+ kill (command_pid, SIGINT);
+ /* The caller may also have sent a signal to command_pid, so
+ always try waiting. First, though, check and see if it's still
+ there.... */
+ do_waitpid:
+ r = waitpid (command_pid, &status, WNOHANG);
+ if (r == 0)
+ ;
+ else if (r == command_pid)
+ command_pid_is_dead++;
+ else if (r == -1)
+ switch (errno) {
+ case ECHILD:
+ command_pid_is_dead++;
+ break;
+ case EINTR:
+ goto do_waitpid;
+ }
+ else
+ /* waitpid should always return one of the above values */
+ abort ();
+ while (!command_pid_is_dead) {
+ struct timeval timeout;
+ struct fd_set_wrapper readfds;
+ char buf[100];
+ int i;
+
+ /* Use a non-zero timeout to avoid eating up CPU cycles. */
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ readfds = command_fds_to_drain;
+ switch (select (max_command_fd + 1, &readfds.fds,
+ (fd_set *)0, (fd_set *)0,
+ &timeout)) {
+ case -1:
+ if (errno != EINTR)
+ abort ();
+ case 0:
+ /* timeout */
+ break;
+ case 1:
+ for (i = 0; i <= max_command_fd; i++)
+ {
+ if (!FD_ISSET (i, &readfds.fds))
+ continue;
+ /* this fd is non-blocking */
+ while (read (i, buf, sizeof (buf)) >= 1)
+ ;
+ }
+ break;
+ default:
+ abort ();
+ }
+ }
+ }
+#endif
+
+ /* This might be set by the user in ~/.bashrc, ~/.cshrc, etc. */
+ temp_dir = getenv ("TMPDIR");
+ if (temp_dir == NULL || temp_dir[0] == '\0')
+ temp_dir = "/tmp";
+ chdir(temp_dir);
+
+ len = strlen (server_temp_dir) + 80;
+ cmd = malloc (len);
+ if (cmd == NULL)
+ {
+ printf ("E Cannot delete %s on server; out of memory\n",
+ server_temp_dir);
+ return;
+ }
+ sprintf (cmd, "rm -rf %s", server_temp_dir);
+ system (cmd);
+ free (cmd);
+}
+
+int server_active = 0;
+int server_expanding = 0;
+
+int
+server (argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc == -1)
+ {
+ static const char *const msg[] =
+ {
+ "Usage: %s %s\n",
+ " Normally invoked by a cvs client on a remote machine.\n",
+ NULL
+ };
+ usage (msg);
+ }
+ /* Ignore argc and argv. They might be from .cvsrc. */
+
+ /* Since we're in the server parent process, error should use the
+ protocol to report error messages. */
+ error_use_protocol = 1;
+
+ /*
+ * Put Rcsbin at the start of PATH, so that rcs programs can find
+ * themselves.
+ */
+#ifdef HAVE_PUTENV
+ if (Rcsbin != NULL && *Rcsbin)
+ {
+ char *p;
+ char *env;
+
+ p = getenv ("PATH");
+ if (p != NULL)
+ {
+ env = malloc (strlen (Rcsbin) + strlen (p) + sizeof "PATH=:");
+ if (env != NULL)
+ sprintf (env, "PATH=%s:%s", Rcsbin, p);
+ }
+ else
+ {
+ env = malloc (strlen (Rcsbin) + sizeof "PATH=");
+ if (env != NULL)
+ sprintf (env, "PATH=%s", Rcsbin);
+ }
+ if (env == NULL)
+ {
+ printf ("E Fatal server error, aborting.\n\
+error ENOMEM Virtual memory exhausted.\n");
+ exit (1);
+ }
+ putenv (env);
+ }
+#endif
+
+ /* OK, now figure out where we stash our temporary files. */
+ {
+ char *p;
+
+ /* This might be set by the user in ~/.bashrc, ~/.cshrc, etc. */
+ char *temp_dir = getenv ("TMPDIR");
+ if (temp_dir == NULL || temp_dir[0] == '\0')
+ temp_dir = "/tmp";
+
+ server_temp_dir = malloc (strlen (temp_dir) + 80);
+ if (server_temp_dir == NULL)
+ {
+ /*
+ * Strictly speaking, we're not supposed to output anything
+ * now. But we're about to exit(), give it a try.
+ */
+ printf ("E Fatal server error, aborting.\n\
+error ENOMEM Virtual memory exhausted.\n");
+ exit (1);
+ }
+ strcpy (server_temp_dir, temp_dir);
+
+ /* Remove a trailing slash from TMPDIR if present. */
+ p = server_temp_dir + strlen (server_temp_dir) - 1;
+ if (*p == '/')
+ *p = '\0';
+
+ /*
+ * I wanted to use cvs-serv/PID, but then you have to worry about
+ * the permissions on the cvs-serv directory being right. So
+ * use cvs-servPID.
+ */
+ strcat (server_temp_dir, "/cvs-serv");
+
+ p = server_temp_dir + strlen (server_temp_dir);
+ sprintf (p, "%d", getpid ());
+ }
+
+ (void) SIG_register (SIGHUP, server_cleanup);
+ (void) SIG_register (SIGINT, server_cleanup);
+ (void) SIG_register (SIGQUIT, server_cleanup);
+ (void) SIG_register (SIGPIPE, server_cleanup);
+ (void) SIG_register (SIGTERM, server_cleanup);
+
+ /* Now initialize our argument vector (for arguments from the client). */
+
+ /* Small for testing. */
+ argument_vector_size = 1;
+ argument_vector =
+ (char **) malloc (argument_vector_size * sizeof (char *));
+ if (argument_vector == NULL)
+ {
+ /*
+ * Strictly speaking, we're not supposed to output anything
+ * now. But we're about to exit(), give it a try.
+ */
+ printf ("E Fatal server error, aborting.\n\
+error ENOMEM Virtual memory exhausted.\n");
+ exit (1);
+ }
+
+ argument_count = 1;
+ argument_vector[0] = "Dummy argument 0";
+
+ server_active = 1;
+ while (1)
+ {
+ char *cmd, *orig_cmd;
+ struct request *rq;
+
+ orig_cmd = cmd = read_line (stdin);
+ if (cmd == NULL)
+ break;
+ if (cmd == NO_MEM_ERROR)
+ {
+ printf ("E Fatal server error, aborting.\n\
+error ENOMEM Virtual memory exhausted.\n");
+ break;
+ }
+ for (rq = requests; rq->name != NULL; ++rq)
+ if (strncmp (cmd, rq->name, strlen (rq->name)) == 0)
+ {
+ int len = strlen (rq->name);
+ if (cmd[len] == '\0')
+ cmd += len;
+ else if (cmd[len] == ' ')
+ cmd += len + 1;
+ else
+ /*
+ * The first len characters match, but it's a different
+ * command. e.g. the command is "cooperate" but we matched
+ * "co".
+ */
+ continue;
+ (*rq->func) (cmd);
+ break;
+ }
+ if (rq->name == NULL)
+ {
+ if (!print_pending_error ())
+ printf ("error unrecognized request `%s'\n", cmd);
+ }
+ free (orig_cmd);
+ }
+ server_cleanup (0);
+ return 0;
+}
+
+
+#ifdef AUTH_SERVER_SUPPORT
+
+/* This was test code, which we may need again. */
+#if 0
+ /* If we were invoked this way, then stdin comes from the
+ client and stdout/stderr writes to it. */
+ int c;
+ while ((c = getc (stdin)) != EOF && c != '*')
+ {
+ printf ("%c", toupper (c));
+ fflush (stdout);
+ }
+ exit (0);
+#endif /* 1/0 */
+
+
+/*
+ * 0 means no entry found for this user.
+ * 1 means entry found and password matches.
+ * 2 means entry found, but password does not match.
+ */
+int
+check_repository_password (username, password, repository)
+ char *username, *password, *repository;
+{
+ int retval = 0;
+ FILE *fp;
+ char *filename;
+ char linebuf[MAXLINELEN];
+ int found_it = 0, len;
+
+ filename = xmalloc (strlen (repository)
+ + 1
+ + strlen ("CVSROOT")
+ + 1
+ + strlen ("passwd")
+ + 1);
+
+ strcpy (filename, repository);
+ strcat (filename, "/CVSROOT");
+ strcat (filename, "/passwd");
+
+ fp = fopen (filename, "r");
+ if (fp == NULL)
+ {
+ /* This is ok -- the cvs passwd file might not exist. */
+ fclose (fp);
+ return 0;
+ }
+
+ /* Look for a relevant line -- one with this user's name. */
+ len = strlen (username);
+ while (fgets (linebuf, MAXPATHLEN - 1, fp))
+ {
+ if ((strncmp (linebuf, username, len) == 0)
+ && (linebuf[len] == ':'))
+ {
+ found_it = 1;
+ break;
+ }
+ }
+ fclose (fp);
+
+ /* If found_it != 0, then linebuf contains the information we need. */
+ if (found_it)
+ {
+ char *found_password;
+
+ strtok (linebuf, ":");
+ found_password = strtok (NULL, ": \n");
+
+ if (strcmp (found_password, crypt (password, found_password)) == 0)
+ retval = 1;
+ else
+ retval = 2;
+ }
+ else
+ retval = 0;
+
+ free (filename);
+
+ return retval;
+}
+
+
+/* Return 1 if password matches, else 0. */
+int
+check_password (username, password, repository)
+ char *username, *password, *repository;
+{
+ int rc;
+
+ /* First we see if this user has a password in the CVS-specific
+ password file. If so, that's enough to authenticate with. If
+ not, we'll check /etc/passwd. */
+
+ rc = check_repository_password (username, password, repository);
+
+ if (rc == 1)
+ return 1;
+ else if (rc == 2)
+ return 0;
+ else if (rc == 0)
+ {
+ /* No cvs password found, so try /etc/passwd. */
+
+ struct passwd *pw;
+ char *found_passwd;
+
+ pw = getpwnam (username);
+ if (pw == NULL)
+ {
+ printf ("E Fatal error, aborting.\n"
+ "error 0 %s: no such user\n", username);
+ exit (1);
+ }
+ found_passwd = pw->pw_passwd;
+
+ if (found_passwd && *found_passwd)
+ return (! strcmp (found_passwd, crypt (password, found_passwd)));
+ else if (password && *password)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ {
+ /* Something strange happened. We don't know what it was, but
+ we certainly won't grant authorization. */
+ return 0;
+ }
+}
+
+
+/* Read username and password from client (i.e., stdin).
+ If correct, then switch to run as that user and send an ACK to the
+ client via stdout, else send NACK and die. */
+void
+authenticate_connection ()
+{
+ int len;
+ char tmp[PATH_MAX];
+ char repository[PATH_MAX];
+ char username[PATH_MAX];
+ char password[PATH_MAX];
+ char server_user[PATH_MAX];
+ struct passwd *pw;
+
+ /* The Authentication Protocol. Client sends:
+ *
+ * BEGIN AUTH REQUEST\n
+ * <REPOSITORY>\n
+ * <USERNAME>\n
+ * <PASSWORD>\n
+ * END AUTH REQUEST\n
+ *
+ * Server uses above information to authenticate, then sends
+ *
+ * I LOVE YOU\n
+ *
+ * if it grants access, else
+ *
+ * I HATE YOU\n
+ *
+ * if it denies access (and it exits if denying).
+ *
+ * Note that the actual client/server protocol has not started up
+ * yet, because we haven't authenticated! Therefore, there are
+ * certain things we can't take for granted. For example, don't use
+ * error() because `error_use_protocol' has not yet been set by
+ * server().
+ *
+ * We need to know where the repository is too, to look up the
+ * password in the special CVS passwd file before we try
+ * /etc/passwd. However, the repository is normally transmitted in
+ * the regular client/server protocol, which has not yet started,
+ * blah blah blah. This is why the client transmits the repository
+ * as part of the "authentication protocol". Thus, the repository
+ * will be redundantly retransmitted later, but that's no big deal.
+ */
+
+ /* Make sure the protocol starts off on the right foot... */
+ fgets (tmp, PATH_MAX, stdin);
+ if (strcmp (tmp, "BEGIN AUTH REQUEST\n"))
+ {
+ printf ("error: bad auth protocol start: %s", tmp);
+ fflush (stdout);
+ exit (1);
+ }
+
+ /* Get the three important pieces of information in order. */
+ fgets (repository, PATH_MAX, stdin);
+ fgets (username, PATH_MAX, stdin);
+ fgets (password, PATH_MAX, stdin);
+
+ /* Make them pure. */
+ strip_trailing_newlines (repository);
+ strip_trailing_newlines (username);
+ strip_trailing_newlines (password);
+
+ /* ... and make sure the protocol ends on the right foot. */
+ fgets (tmp, PATH_MAX, stdin);
+ if (strcmp (tmp, "END AUTH REQUEST\n"))
+ {
+ printf ("error: bad auth protocol end: %s", tmp);
+ fflush (stdout);
+ exit (1);
+ }
+
+ if (check_password (username, password, repository))
+ {
+ printf ("I LOVE YOU\n");
+ fflush (stdout);
+ }
+ else
+ {
+ printf ("I HATE YOU\n");
+ fflush (stdout);
+ exit (1);
+ }
+
+ /* Do everything that kerberos did. */
+ pw = getpwnam (username);
+ if (pw == NULL)
+ {
+ printf ("E Fatal error, aborting.\n"
+ "error 0 %s: no such user\n", username);
+ exit (1);
+ }
+
+ initgroups (pw->pw_name, pw->pw_gid);
+ setgid (pw->pw_gid);
+ setuid (pw->pw_uid);
+ /* Inhibit access by randoms. Don't want people randomly
+ changing our temporary tree before we check things in. */
+ umask (077);
+
+#if HAVE_PUTENV
+ /* Set LOGNAME and USER in the environment, in case they are
+ already set to something else. */
+ {
+ char *env;
+
+ env = xmalloc (sizeof "LOGNAME=" + strlen (username));
+ (void) sprintf (env, "LOGNAME=%s", username);
+ (void) putenv (env);
+
+ env = xmalloc (sizeof "USER=" + strlen (username));
+ (void) sprintf (env, "USER=%s", username);
+ (void) putenv (env);
+ }
+#endif /* HAVE_PUTENV */
+}
+
+#endif AUTH_SERVER_SUPPORT
+
+
+#endif /* SERVER_SUPPORT */
+
diff --git a/gnu/usr.bin/cvs/cvs/server.h b/gnu/usr.bin/cvs/cvs/server.h
new file mode 100644
index 000000000000..cb49267e991e
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/server.h
@@ -0,0 +1,136 @@
+/* Interface between the server and the rest of CVS. */
+
+/* Miscellaneous stuff which isn't actually particularly server-specific. */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#endif
+
+#ifdef SERVER_SUPPORT
+
+/*
+ * Nonzero if we are using the server. Used by various places to call
+ * server-specific functions.
+ */
+extern int server_active;
+extern int server_expanding;
+
+/* Server functions exported to the rest of CVS. */
+
+/* Run the server. */
+extern int server PROTO((int argc, char **argv));
+
+/* We have a new Entries line for a file. TAG or DATE can be NULL. */
+extern void server_register
+ PROTO((char *name, char *version, char *timestamp,
+ char *options, char *tag, char *date, char *conflict));
+
+/*
+ * We want to nuke the Entries line for a file, and (unless
+ * server_scratch_entry_only is subsequently called) the file itself.
+ */
+extern void server_scratch PROTO((char *name));
+
+/*
+ * The file which just had server_scratch called on it needs to have only
+ * the Entries line removed, not the file itself.
+ */
+extern void server_scratch_entry_only PROTO((void));
+
+/*
+ * We just successfully checked in FILE (which is just the bare
+ * filename, with no directory). REPOSITORY is the directory for the
+ * repository.
+ */
+extern void server_checked_in
+ PROTO((char *file, char *update_dir, char *repository));
+
+extern void server_copy_file
+ PROTO((char *file, char *update_dir, char *repository, char *newfile));
+
+/*
+ * We just successfully updated FILE (bare filename, no directory).
+ * REPOSITORY is the directory for the repository. This is called
+ * after server_register or server_scratch, in the latter case the
+ * file is to be removed. UPDATED indicates whether the file is now
+ * up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, SERVER_PATCHED,
+ * yes, but file is a diff from user version to repository version).
+ */
+enum server_updated_arg4 {SERVER_UPDATED, SERVER_MERGED, SERVER_PATCHED};
+extern void server_updated
+ PROTO((char *file, char *update_dir, char *repository,
+ enum server_updated_arg4 updated, struct stat *,
+ unsigned char *checksum));
+
+/* Set the Entries.Static flag. */
+extern void server_set_entstat PROTO((char *update_dir, char *repository));
+/* Clear it. */
+extern void server_clear_entstat PROTO((char *update_dir, char *repository));
+
+/* Set or clear a per-directory sticky tag or date. */
+extern void server_set_sticky PROTO((char *update_dir, char *repository,
+ char *tag,
+ char *date));
+
+extern void server_update_entries
+ PROTO((char *file, char *update_dir, char *repository,
+ enum server_updated_arg4 updated));
+
+enum progs {PROG_CHECKIN, PROG_UPDATE};
+extern void server_prog PROTO((char *, char *, enum progs));
+extern void server_cleanup PROTO((int sig));
+
+#ifdef SERVER_FLOWCONTROL
+/* Pause if it's convenient to avoid memory blowout */
+extern void server_check_pause PROTO((void));
+#endif /* SERVER_FLOWCONTROL */
+
+#endif /* SERVER_SUPPORT */
+
+/* Stuff shared with the client. */
+struct request
+{
+ /* Name of the request. */
+ char *name;
+
+#ifdef SERVER_SUPPORT
+ /*
+ * Function to carry out the request. ARGS is the text of the command
+ * after name and, if present, a single space, have been stripped off.
+ */
+ void (*func) PROTO((char *args));
+#endif
+
+ /* Stuff for use by the client. */
+ enum {
+ /*
+ * Failure to implement this request can imply a fatal
+ * error. This should be set only for commands which were in the
+ * original version of the protocol; it should not be set for new
+ * commands.
+ */
+ rq_essential,
+
+ /* Some servers might lack this request. */
+ rq_optional,
+
+ /*
+ * Set by the client to one of the following based on what this
+ * server actually supports.
+ */
+ rq_supported,
+ rq_not_supported,
+
+ /*
+ * If the server supports this request, and we do too, tell the
+ * server by making the request.
+ */
+ rq_enableme
+ } status;
+};
+
+/* Table of requests ending with an entry with a NULL name. */
+extern struct request requests[];
+
+extern int use_unchanged;
diff --git a/gnu/usr.bin/cvs/cvs/update.h b/gnu/usr.bin/cvs/cvs/update.h
new file mode 100644
index 000000000000..68c91d55ab93
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/update.h
@@ -0,0 +1,9 @@
+/* Definitions of routines shared between local and client/server
+ "update" code. */
+
+/* List of files that we have either processed or are willing to
+ ignore. Any file not on this list gets a question mark printed. */
+extern List *ignlist;
+
+extern int
+update_filesdone_proc PROTO((int err, char *repository, char *update_dir));
diff --git a/gnu/usr.bin/cvs/cvs/wrapper.c b/gnu/usr.bin/cvs/cvs/wrapper.c
new file mode 100644
index 000000000000..ec5f43e8dcf4
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/wrapper.c
@@ -0,0 +1,371 @@
+#include "cvs.h"
+
+/*
+ Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94
+ Modified By: vdemarco@bou.shl.com
+
+ This package was written to support the NEXTSTEP concept of
+ "wrappers." These are essentially directories that are to be
+ treated as "files." This package allows such wrappers to be
+ "processed" on the way in and out of CVS. The intended use is to
+ wrap up a wrapper into a single tar, such that that tar can be
+ treated as a single binary file in CVS. To solve the problem
+ effectively, it was also necessary to be able to prevent rcsmerge
+ application at appropriate times.
+
+ ------------------
+ Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
+
+ wildcard [option value][option value]...
+
+ where option is one of
+ -f from cvs filter value: path to filter
+ -t to cvs filter value: path to filter
+ -m update methodology value: MERGE or COPY
+
+ and value is a single-quote delimited value.
+
+ E.g:
+ *.nib -f 'gunzipuntar' -t 'targzip' -m 'COPY'
+*/
+
+
+typedef struct {
+ char *wildCard;
+ char *tocvsFilter;
+ char *fromcvsFilter;
+ char *conflictHook;
+ WrapMergeMethod mergeMethod;
+} WrapperEntry;
+
+static WrapperEntry **wrap_list=NULL;
+static WrapperEntry **wrap_saved_list=NULL;
+
+static int wrap_size=0;
+static int wrap_count=0;
+static int wrap_tempcount=0;
+static int wrap_saved_count=0;
+static int wrap_saved_tempcount=0;
+
+#define WRAPPER_GROW 8
+
+void wrap_add_entry PROTO((WrapperEntry *e,int temp));
+void wrap_kill PROTO((void));
+void wrap_kill_temp PROTO((void));
+void wrap_free_entry PROTO((WrapperEntry *e));
+void wrap_free_entry_internal PROTO((WrapperEntry *e));
+void wrap_restore_saved PROTO((void));
+
+void wrap_setup()
+{
+ char file[PATH_MAX];
+ struct passwd *pw;
+
+ /* Then add entries found in repository, if it exists */
+ (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_WRAPPER);
+ if (isfile (file)){
+ wrap_add_file(file,0);
+ }
+
+ /* Then add entries found in home dir, (if user has one) and file exists */
+ if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir){
+ (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER);
+ if (isfile (file)){
+ wrap_add_file (file, 0);
+ }
+ }
+
+ /* Then add entries found in CVSWRAPPERS environment variable. */
+ wrap_add (getenv (WRAPPER_ENV), 0);
+}
+
+/*
+ * Open a file and read lines, feeding each line to a line parser. Arrange
+ * for keeping a temporary list of wrappers at the end, if the "temp"
+ * argument is set.
+ */
+void
+wrap_add_file (file, temp)
+ const char *file;
+ int temp;
+{
+ FILE *fp;
+ char line[1024];
+
+ wrap_restore_saved();
+ wrap_kill_temp();
+
+ /* load the file */
+ if (!(fp = fopen (file, "r")))
+ return;
+ while (fgets (line, sizeof (line), fp))
+ wrap_add (line, temp);
+ (void) fclose (fp);
+}
+
+void
+wrap_kill()
+{
+ wrap_kill_temp();
+ while(wrap_count)
+ wrap_free_entry(wrap_list[--wrap_count]);
+}
+
+void
+wrap_kill_temp()
+{
+ WrapperEntry **temps=wrap_list+wrap_count;
+
+ while(wrap_tempcount)
+ wrap_free_entry(temps[--wrap_tempcount]);
+}
+
+void
+wrap_free_entry(e)
+ WrapperEntry *e;
+{
+ wrap_free_entry_internal(e);
+ free(e);
+}
+
+void
+wrap_free_entry_internal(e)
+ WrapperEntry *e;
+{
+ free(e->wildCard);
+ if(e->tocvsFilter)
+ free(e->tocvsFilter);
+ if(e->fromcvsFilter)
+ free(e->fromcvsFilter);
+ if(e->conflictHook)
+ free(e->conflictHook);
+}
+
+void
+wrap_restore_saved()
+{
+ if(!wrap_saved_list)
+ return;
+
+ wrap_kill();
+
+ free(wrap_list);
+
+ wrap_list=wrap_saved_list;
+ wrap_count=wrap_saved_count;
+ wrap_tempcount=wrap_saved_tempcount;
+
+ wrap_saved_list=NULL;
+ wrap_saved_count=0;
+ wrap_saved_tempcount=0;
+}
+
+void
+wrap_add (line, isTemp)
+ char *line;
+ int isTemp;
+{
+ char *temp;
+ char ctemp;
+ WrapperEntry e;
+ char opt;
+
+ if (!line || line[0] == '#')
+ return;
+
+ memset (&e, 0, sizeof(e));
+
+ /* Search for the wild card */
+ while(*line && isspace(*line))
+ ++line;
+ for(temp=line;*line && !isspace(*line);++line)
+ ;
+ if(temp==line)
+ return;
+
+ ctemp=*line;
+ *line='\0';
+
+ e.wildCard=xstrdup(temp);
+ *line=ctemp;
+
+ while(*line){
+ /* Search for the option */
+ while(*line && *line!='-')
+ ++line;
+ if(!*line)
+ break;
+ ++line;
+ if(!*line)
+ break;
+ opt=*line;
+
+ /* Search for the filter commandline */
+ for(++line;*line && *line!='\'';++line);
+ if(!*line)
+ break;
+
+ for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
+ ;
+
+ if(line==temp+1)
+ break;
+
+ ctemp=*line;
+ *line='\0';
+ switch(opt){
+ case 'f':
+ if(e.fromcvsFilter)
+ free(e.fromcvsFilter);
+ e.fromcvsFilter=expand_path (temp);
+ if (!e.fromcvsFilter)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
+ break;
+ case 't':
+ if(e.tocvsFilter)
+ free(e.tocvsFilter);
+ e.tocvsFilter=expand_path (temp);
+ if (!e.tocvsFilter)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
+ break;
+ case 'c':
+ if(e.conflictHook)
+ free(e.conflictHook);
+ e.conflictHook=expand_path (temp);
+ if (!e.conflictHook)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
+ break;
+ case 'm':
+ if(*temp=='C' || *temp=='c')
+ e.mergeMethod=WRAP_COPY;
+ else
+ e.mergeMethod=WRAP_MERGE;
+ break;
+ default:
+ break;
+ }
+ *line=ctemp;
+ if(!*line)break;
+ ++line;
+ }
+
+ wrap_add_entry(&e, isTemp);
+}
+
+void
+wrap_add_entry(e, temp)
+ WrapperEntry *e;
+ int temp;
+{
+ int x;
+ if(wrap_count+wrap_tempcount>=wrap_size){
+ wrap_size += WRAPPER_GROW;
+ wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
+ wrap_size *
+ sizeof (WrapperEntry *));
+ }
+
+ if(!temp && wrap_tempcount){
+ for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
+ wrap_list[x+1]=wrap_list[x];
+ }
+
+ x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
+ wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
+ wrap_list[x]->wildCard=e->wildCard;
+ wrap_list[x]->fromcvsFilter=e->fromcvsFilter;
+ wrap_list[x]->tocvsFilter=e->tocvsFilter;
+ wrap_list[x]->conflictHook=e->conflictHook;
+ wrap_list[x]->mergeMethod=e->mergeMethod;
+}
+
+/* Return 1 if the given filename is a wrapper filename */
+int
+wrap_name_has (name,has)
+ const char *name;
+ WrapMergeHas has;
+{
+ int x,count=wrap_count+wrap_saved_count;
+ char *temp;
+
+ for(x=0;x<count;++x)
+ if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0){
+ switch(has){
+ case WRAP_TOCVS:
+ temp=wrap_list[x]->tocvsFilter;
+ break;
+ case WRAP_FROMCVS:
+ temp=wrap_list[x]->fromcvsFilter;
+ break;
+ case WRAP_CONFLICT:
+ temp=wrap_list[x]->conflictHook;
+ break;
+ default:
+ abort ();
+ }
+ if(temp==NULL)
+ return (0);
+ else
+ return (1);
+ }
+ return (0);
+}
+
+WrapperEntry *
+wrap_matching_entry (name)
+ const char *name;
+{
+ int x,count=wrap_count+wrap_saved_count;
+
+ for(x=0;x<count;++x)
+ if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0)
+ return wrap_list[x];
+ return (WrapperEntry *)NULL;
+}
+
+char *
+wrap_tocvs_process_file(fileName)
+ const char *fileName;
+{
+ WrapperEntry *e=wrap_matching_entry(fileName);
+ static char buf[L_tmpnam+1];
+
+ if(e==NULL || e->tocvsFilter==NULL)
+ return NULL;
+
+ tmpnam(buf);
+
+ run_setup(e->tocvsFilter,fileName,buf);
+ run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
+
+ return buf;
+}
+
+int
+wrap_merge_is_copy (fileName)
+ const char *fileName;
+{
+ WrapperEntry *e=wrap_matching_entry(fileName);
+ if(e==NULL || e->mergeMethod==WRAP_MERGE)
+ return 0;
+
+ return 1;
+}
+
+char *
+wrap_fromcvs_process_file(fileName)
+ const char *fileName;
+{
+ WrapperEntry *e=wrap_matching_entry(fileName);
+ static char buf[PATH_MAX];
+
+ if(e==NULL || e->fromcvsFilter==NULL)
+ return NULL;
+
+ run_setup(e->fromcvsFilter,fileName);
+ run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );
+ return buf;
+}
diff --git a/gnu/usr.bin/cvs/cvsbug/Makefile b/gnu/usr.bin/cvs/cvsbug/Makefile
new file mode 100644
index 000000000000..336fd135ef7f
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvsbug/Makefile
@@ -0,0 +1,25 @@
+# $Id: Makefile,v 1.2 1995/12/11 01:58:51 peter Exp $
+
+MAN8= cvsbug.8
+
+CLEANFILES+= cvsbug ver
+
+.SUFFIXES: .sh
+
+all: cvsbug
+
+.sh:
+ echo > ver cvs-`sed < ${.CURDIR}/../lib/version.c \
+ -e '/version_string/!d' \
+ -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
+ -e q`
+ sed -e "s,xVERSIONx,`cat ver`,g" ${.CURDIR}/$@.sh > $@
+
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ cvsbug ${DESTDIR}${BINDIR}/cvsbug
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
+
diff --git a/gnu/usr.bin/cvs/cvsbug/cvsbug.8 b/gnu/usr.bin/cvs/cvsbug/cvsbug.8
new file mode 100644
index 000000000000..496ef1458b60
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvsbug/cvsbug.8
@@ -0,0 +1,269 @@
+.\" -*- nroff -*-
+.\" ---------------------------------------------------------------------------
+.\" man page for send-pr (by Heinz G. Seidl, hgs@cygnus.com)
+.\" updated Feb 1993 for GNATS 3.00 by Jeffrey Osier, jeffrey@cygnus.com
+.\"
+.\" This file is part of the Problem Report Management System (GNATS)
+.\" Copyright 1992 Cygnus Support
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2 of the License, or (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Library General Public
+.\" License along with this program; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+.\"
+.\" ---------------------------------------------------------------------------
+.nh
+.TH CVSBUG 1 xVERSIONx "February 1993"
+.SH NAME
+cvsbug \- send problem report (PR) about CVS to a central support site
+.SH SYNOPSIS
+.B cvsbug
+[
+.I site
+]
+[
+.B \-f
+.I problem-report
+]
+[
+.B \-t
+.I mail-address
+]
+.br
+.in +0.8i
+[
+.B \-P
+]
+[
+.B \-L
+]
+[
+.B \-\-request-id
+]
+[
+.B \-v
+]
+.SH DESCRIPTION
+.B cvsbug
+is a tool used to submit
+.I problem reports
+.\" SITE ADMINISTRATORS - change this if you use a local default
+(PRs) to a central support site. In most cases the correct
+.I site
+will be the default. This argument indicates the support site which
+is responsible for the category of problem involved. Some sites may
+use a local address as a default.
+.I site
+values are defined by using the
+.BR aliases (5).
+.LP
+.B cvsbug
+invokes an editor on a problem report template (after trying to fill
+in some fields with reasonable default values). When you exit the
+editor,
+.B cvsbug
+sends the completed form to the
+.I Problem Report Management System
+(\fBGNATS\fR) at a central support site. At the support site, the PR
+is assigned a unique number and is stored in the \fBGNATS\fR database
+according to its category and submitter-id. \fBGNATS\fR automatically
+replies with an acknowledgement, citing the category and the PR
+number.
+.LP
+To ensure that a PR is handled promptly, it should contain your (unique)
+\fIsubmitter-id\fR and one of the available \fIcategories\fR to identify the
+problem area. (Use
+.B `cvsbug -L'
+to see a list of categories.)
+.LP
+The
+.B cvsbug
+template at your site should already be customized with your
+submitter-id (running `\|\fBinstall-sid\fP \fIsubmitter-id\fP\|' to
+accomplish this is part of the installation procedures for
+.BR cvsbug ).
+If this hasn't been done, see your system administrator for your
+submitter-id, or request one from your support site by invoking
+.B `cvsbug \-\-request\-id'.
+If your site does not distinguish between different user sites, or if
+you are not affiliated with the support site, use
+.B `net'
+for this field.
+.LP
+The more precise your problem description and the more complete your
+information, the faster your support team can solve your problems.
+.SH OPTIONS
+.TP
+.BI \-f " problem-report"
+specify a file (\fIproblem-report\fR) which already contains a
+complete problem report.
+.B cvsbug
+sends the contents of the file without invoking the editor. If
+the value for
+.I problem-report
+is
+.BR `\|\-\|' ,
+then
+.B cvsbug
+reads from standard input.
+.TP
+.BI \-t " mail-address"
+Change mail address at the support site for problem reports. The
+default
+.I mail-address
+is the address used for the default
+.IR site .
+Use the
+.I site
+argument rather than this option in nearly all cases.
+.TP
+.B \-P
+print the form specified by the environment variable
+.B PR_FORM
+on standard output. If
+.B PR_FORM
+is not set, print the standard blank PR template. No mail is sent.
+.TP
+.B -L
+print the list of available categories. No mail is sent.
+.TP
+.B \-\-request\-id
+sends mail to the default support site, or
+.I site
+if specified, with a request for your
+.IR submitter-id .
+If you are
+not affiliated with
+.IR site ,
+use a
+.I submitter-id
+of
+.BR net \|'.
+.TP
+.B \-v
+Display the
+.B cvsbug
+version number.
+.LP
+Note: use
+.B cvsbug
+to submit problem reports rather than mailing them directly. Using
+both the template and
+.B cvsbug
+itself will help ensure all necessary information will reach the
+support site.
+.SH ENVIRONMENT
+The environment variable
+.B EDITOR
+specifies the editor to invoke on the template.
+.br
+default:
+.B vi
+.sp
+If the environment variable
+.B PR_FORM
+is set, then its value is used as the file name of the template for
+your problem-report editing session. You can use this to start with a
+partially completed form (for example, a form with the identification
+fields already completed).
+.SH "HOW TO FILL OUT A PROBLEM REPORT"
+Problem reports have to be in a particular form so that a program can
+easily manage them. Please remember the following guidelines:
+.IP \(bu 3m
+describe only
+.B one problem
+with each problem report.
+.IP \(bu 3m
+For follow-up mail, use the same subject line as the one in the automatic
+acknowledgent. It consists of category, PR number and the original synopsis
+line. This allows the support site to relate several mail messages to a
+particular PR and to record them automatically.
+.IP \(bu 3m
+Please try to be as accurate as possible in the subject and/or synopsis line.
+.IP \(bu 3m
+The subject and the synopsis line are not confidential. This is
+because open-bugs lists are compiled from them. Avoid confidential
+information there.
+.LP
+See the GNU
+.B Info
+file
+.B cvsbug.info
+or the document \fIReporting Problems With cvsbug\fR\ for detailed
+information on reporting problems
+.SH "HOW TO SUBMIT TEST CASES, CODE, ETC."
+Submit small code samples with the PR. Contact the support site for
+instructions on submitting larger test cases and problematic source
+code.
+.SH FILES
+.ta \w'/tmp/pbad$$ 'u
+/tmp/p$$ copy of PR used in editing session
+.br
+/tmp/pf$$ copy of empty PR form, for testing purposes
+.br
+/tmp/pbad$$ file for rejected PRs
+.SH EMACS USER INTERFACE
+An Emacs user interface for
+.B cvsbug
+with completion of field values is part of the
+.B cvsbug
+distribution (invoked with
+.BR "M-x cvsbug" ).
+See the file
+.B cvsbug.info
+or the ASCII file
+.B INSTALL
+in the top level directory of the distribution for configuration and
+installation information. The Emacs LISP template file is
+.B cvsbug-el.in
+and is installed as
+.BR cvsbug.el .
+.SH INSTALLATION AND CONFIGURATION
+See
+.B cvsbug.info
+or
+.B INSTALL
+for installation instructions.
+.SH SEE ALSO
+.I Reporting Problems Using cvsbug
+(also installed as the GNU Info file
+.BR cvsbug.info ).
+.LP
+.BR gnats (l),
+.BR query-pr (1),
+.BR edit-pr (1),
+.BR gnats (8),
+.BR queue-pr (8),
+.BR at-pr (8),
+.BR mkcat (8),
+.BR mkdist (8).
+.SH AUTHORS
+Jeffrey Osier, Brendan Kehoe, Jason Merrill, Heinz G. Seidl (Cygnus
+Support)
+.SH COPYING
+Copyright (c) 1992, 1993 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
diff --git a/gnu/usr.bin/cvs/cvsbug/cvsbug.sh b/gnu/usr.bin/cvs/cvsbug/cvsbug.sh
new file mode 100644
index 000000000000..ab26cfce570a
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvsbug/cvsbug.sh
@@ -0,0 +1,528 @@
+#! /bin/sh
+# Submit a problem report to a GNATS site.
+# Copyright (C) 1993 Free Software Foundation, Inc.
+# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+# version written by Heinz G. Seidl (hgs@ide.com).
+#
+# This file is part of GNU GNATS.
+# Modified by Berliner for CVS.
+#
+#ident "@(#)cvs/src:$Name: $:$Id: cvsbug.sh,v 1.10 1995/11/15 00:18:00 woods Exp $"
+#
+# GNU GNATS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU GNATS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU GNATS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# The version of this send-pr.
+VERSION=3.2
+
+# The submitter-id for your site.
+SUBMITTER=net
+
+## # Where the GNATS directory lives, if at all.
+## [ -z "$GNATS_ROOT" ] &&
+## GNATS_ROOT=/usr/local/lib/gnats/gnats-db
+
+# The default mail address for PR submissions.
+GNATS_ADDR=bug-cvs@prep.ai.mit.edu
+
+## # Where the gnats category tree lives.
+## DATADIR=/usr/local/lib
+
+## # If we've been moved around, try using GCC_EXEC_PREFIX.
+## [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && DATADIR=${GCC_EXEC_PREFIX}..
+
+# The default release for this host.
+DEFAULT_RELEASE="xVERSIONx"
+
+# The default organization.
+DEFAULT_ORGANIZATION="net"
+
+## # The default site to look for.
+## GNATS_SITE=unknown
+
+## # Newer config information?
+## [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+
+# What mailer to use. This must come after the config file, since it is
+# host-dependent.
+if [ -f /usr/sbin/sendmail ]; then
+ MAIL_AGENT="/usr/sbin/sendmail -oi -t"
+else
+ MAIL_AGENT="/usr/lib/sendmail -oi -t"
+fi
+MAILER=`echo $MAIL_AGENT | sed -e 's, .*,,'`
+if [ ! -f "$MAILER" ] ; then
+ echo "$COMMAND: Cannot file mail program \"$MAILER\"."
+ echo "$COMMAND: Please fix the MAIL_AGENT entry in the $COMMAND file."
+ exit 1
+fi
+
+if test "`echo -n foo`" = foo ; then
+ ECHON=bsd
+elif test "`echo 'foo\c'`" = foo ; then
+ ECHON=sysv
+else
+ ECHON=none
+fi
+
+if [ $ECHON = bsd ] ; then
+ ECHON1="echo -n"
+ ECHON2=
+elif [ $ECHON = sysv ] ; then
+ ECHON1=echo
+ ECHON2='\c'
+else
+ ECHON1=echo
+ ECHON2=
+fi
+
+#
+
+[ -z "$TMPDIR" ] && TMPDIR=/tmp
+
+TEMP=$TMPDIR/p$$
+BAD=$TMPDIR/pbad$$
+REF=$TMPDIR/pf$$
+
+if [ -z "$LOGNAME" -a -n "$USER" ]; then
+ LOGNAME=$USER
+fi
+
+FROM="$LOGNAME"
+REPLY_TO="$LOGNAME"
+
+# Find out the name of the originator of this PR.
+if [ -n "$NAME" ]; then
+ ORIGINATOR="$NAME"
+elif [ -f $HOME/.fullname ]; then
+ ORIGINATOR="`sed -e '1q' $HOME/.fullname`"
+elif [ -f /bin/domainname ]; then
+ if [ "`/bin/domainname`" != "" -a -f /usr/bin/ypcat ]; then
+ # Must use temp file due to incompatibilities in quoting behavior
+ # and to protect shell metacharacters in the expansion of $LOGNAME
+ /usr/bin/ypcat passwd 2>/dev/null | cat - /etc/passwd | grep "^$LOGNAME:" |
+ cut -f5 -d':' | sed -e 's/,.*//' > $TEMP
+ ORIGINATOR="`cat $TEMP`"
+ rm -f $TEMP
+ fi
+fi
+
+if [ "$ORIGINATOR" = "" ]; then
+ grep "^$LOGNAME:" /etc/passwd | cut -f5 -d':' | sed -e 's/,.*//' > $TEMP
+ ORIGINATOR="`cat $TEMP`"
+ rm -f $TEMP
+fi
+
+if [ -n "$ORGANIZATION" ]; then
+ if [ -f "$ORGANIZATION" ]; then
+ ORGANIZATION="`cat $ORGANIZATION`"
+ fi
+else
+ if [ -n "$DEFAULT_ORGANIZATION" ]; then
+ ORGANIZATION="$DEFAULT_ORGANIZATION"
+ elif [ -f $HOME/.organization ]; then
+ ORGANIZATION="`cat $HOME/.organization`"
+ elif [ -f $HOME/.signature ]; then
+ ORGANIZATION="`cat $HOME/.signature`"
+ fi
+fi
+
+# If they don't have a preferred editor set, then use
+if [ -z "$VISUAL" ]; then
+ if [ -z "$EDITOR" ]; then
+ EDIT=vi
+ else
+ EDIT="$EDITOR"
+ fi
+else
+ EDIT="$VISUAL"
+fi
+
+# Find out some information.
+SYSTEM=`( [ -f /bin/uname ] && /bin/uname -a ) || \
+ ( [ -f /usr/bin/uname ] && /usr/bin/uname -a ) || echo ""`
+ARCH=`[ -f /bin/arch ] && /bin/arch`
+MACHINE=`[ -f /bin/machine ] && /bin/machine`
+
+COMMAND=`echo $0 | sed -e 's,.*/,,'`
+## USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename] [--request-id]
+USAGE="Usage: $COMMAND [-PVL]
+[--version]"
+REMOVE=
+BATCH=
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -r) ;; # Ignore for backward compat.
+## -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+## shift ; GNATS_ADDR="$1"
+## EXPLICIT_GNATS_ADDR=true
+## ;;
+## -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+## shift ; IN_FILE="$1"
+## if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+## echo "$COMMAND: cannot read $IN_FILE"
+## exit 1
+## fi
+## ;;
+ -b | --batch) BATCH=true ;;
+ -p | -P | --print) PRINT=true ;;
+ -L | --list) FORMAT=norm ;;
+ -l | -CL | --lisp) FORMAT=lisp ;;
+## --request-id) REQUEST_ID=true ;;
+ -h | --help) echo "$USAGE"; exit 0 ;;
+ -V | --version) echo "$VERSION"; exit 0 ;;
+ -*) echo "$USAGE" ; exit 1 ;;
+ *) echo "$USAGE" ; exit 1
+## if [ -z "$USER_GNATS_SITE" ]; then
+## if [ ! -r "$DATADIR/gnats/$1" ]; then
+## echo "$COMMAND: the GNATS site $1 does not have a categories list."
+## exit 1
+## else
+## # The site name is the alias they'll have to have created.
+## USER_GNATS_SITE=$1
+## fi
+## else
+## echo "$USAGE" ; exit 1
+## fi
+ ;;
+ esac
+ shift
+done
+
+if [ -n "$USER_GNATS_SITE" ]; then
+ GNATS_SITE=$USER_GNATS_SITE
+ GNATS_ADDR=$USER_GNATS_SITE-gnats
+fi
+
+if [ "$SUBMITTER" = "unknown" -a -z "$REQUEST_ID" -a -z "$IN_FILE" ]; then
+ cat << '__EOF__'
+It seems that send-pr is not installed with your unique submitter-id.
+You need to run
+
+ install-sid YOUR-SID
+
+where YOUR-SID is the identification code you received with `send-pr'.
+`send-pr' will automatically insert this value into the template field
+`>Submitter-Id'. If you've downloaded `send-pr' from the Net, use `net'
+for this value. If you do not know your id, run `send-pr --request-id' to
+get one from your support site.
+__EOF__
+ exit 1
+fi
+
+## if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then
+## CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort`
+## else
+## echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list."
+## exit 1
+## fi
+CATEGORIES="contrib cvs doc pcl-cvs portability"
+
+if [ -z "$CATEGORIES" ]; then
+ echo "$COMMAND: the categories list for $GNATS_SITE was empty!"
+ exit 1
+fi
+
+case "$FORMAT" in
+ lisp) echo "$CATEGORIES" | \
+ awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}'
+ exit 0
+ ;;
+ norm) l=`echo "$CATEGORIES" | \
+ awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ END {print max + 1;}'`
+ c=`expr 70 / $l`
+ if [ $c -eq 0 ]; then c=1; fi
+ echo "$CATEGORIES" | \
+ awk 'BEGIN {print "Known categories:"; i = 0 }
+ { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } }
+ END { print ""; }'
+ exit 0
+ ;;
+esac
+
+ORIGINATOR_C='<name of the PR author (one line)>'
+ORGANIZATION_C='<organization of PR author (multiple lines)>'
+CONFIDENTIAL_C='<[ yes | no ] (one line)>'
+SYNOPSIS_C='<synopsis of the problem (one line)>'
+SEVERITY_C='<[ non-critical | serious | critical ] (one line)>'
+PRIORITY_C='<[ low | medium | high ] (one line)>'
+CATEGORY_C='<name of the product (one line)>'
+CLASS_C='<[ sw-bug | doc-bug | change-request | support ] (one line)>'
+RELEASE_C='<release number or tag (one line)>'
+ENVIRONMENT_C='<machine, os, target, libraries (multiple lines)>'
+DESCRIPTION_C='<precise description of the problem (multiple lines)>'
+HOW_TO_REPEAT_C='<code/input/activities to reproduce the problem (multiple lines)>'
+FIX_C='<how to correct or work around the problem, if known (multiple lines)>'
+
+# Catch some signals. ($xs kludge needed by Sun /bin/sh)
+xs=0
+trap 'rm -f $REF $TEMP; exit $xs' 0
+trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 2 3 13 15
+
+# If they told us to use a specific file, then do so.
+if [ -n "$IN_FILE" ]; then
+ if [ "$IN_FILE" = "-" ]; then
+ # The PR is coming from the standard input.
+ if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+ sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP
+ else
+ cat > $TEMP
+ fi
+ else
+ # Use the file they named.
+ if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+ sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP
+ else
+ cat $IN_FILE > $TEMP
+ fi
+ fi
+else
+
+ if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+ # If their PR_FORM points to a bogus entry, then bail.
+ if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then
+ echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM"
+ sleep 1
+ PRINT_INTERN=bad_prform
+ fi
+ fi
+
+ if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+ cp $PR_FORM $TEMP ||
+ ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit )
+ else
+ for file in $TEMP $REF ; do
+ cat > $file << '__EOF__'
+SEND-PR: -*- send-pr -*-
+SEND-PR: Lines starting with `SEND-PR' will be removed automatically, as
+SEND-PR: will all comments (text enclosed in `<' and `>').
+SEND-PR:
+SEND-PR: Choose from the following categories:
+SEND-PR:
+__EOF__
+
+ # Format the categories so they fit onto lines.
+ l=`echo "$CATEGORIES" | \
+ awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ END {print max + 1;}'`
+ c=`expr 61 / $l`
+ if [ $c -eq 0 ]; then c=1; fi
+ echo "$CATEGORIES" | \
+ awk 'BEGIN {printf "SEND-PR: "; i = 0 }
+ { printf ("%-'$l'.'$l's", $0);
+ if ((++i % '$c') == 0) { printf "\nSEND-PR: " } }
+ END { printf "\nSEND-PR:\n"; }' >> $file
+
+ cat >> $file << __EOF__
+To: $GNATS_ADDR
+Subject:
+From: $FROM
+Reply-To: $REPLY_TO
+X-send-pr-version: $VERSION
+
+
+>Submitter-Id: $SUBMITTER
+>Originator: $ORIGINATOR
+>Organization:
+${ORGANIZATION-$ORGANIZATION_C}
+>Confidential: $CONFIDENTIAL_C
+>Synopsis: $SYNOPSIS_C
+>Severity: $SEVERITY_C
+>Priority: $PRIORITY_C
+>Category: $CATEGORY_C
+>Class: $CLASS_C
+>Release: ${DEFAULT_RELEASE-$RELEASE_C}
+>Environment:
+ $ENVIRONMENT_C
+`[ -n "$SYSTEM" ] && echo System: $SYSTEM`
+`[ -n "$ARCH" ] && echo Architecture: $ARCH`
+`[ -n "$MACHINE" ] && echo Machine: $MACHINE`
+>Description:
+ $DESCRIPTION_C
+>How-To-Repeat:
+ $HOW_TO_REPEAT_C
+>Fix:
+ $FIX_C
+__EOF__
+ done
+ fi
+
+ if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+ cat $TEMP
+ xs=0; exit
+ fi
+
+ chmod u+w $TEMP
+ if [ -z "$REQUEST_ID" ]; then
+ eval $EDIT $TEMP
+ else
+ ed -s $TEMP << '__EOF__'
+/^Subject/s/^Subject:.*/Subject: request for a customer id/
+/^>Category/s/^>Category:.*/>Category: send-pr/
+w
+q
+__EOF__
+ fi
+
+ if cmp -s $REF $TEMP ; then
+ echo "$COMMAND: problem report not filled out, therefore not sent"
+ xs=1; exit
+ fi
+fi
+
+#
+# Check the enumeration fields
+
+# This is a "sed-subroutine" with one keyword parameter
+# (with workaround for Sun sed bug)
+#
+SED_CMD='
+/$PATTERN/{
+s|||
+s|<.*>||
+s|^[ ]*||
+s|[ ]*$||
+p
+q
+}'
+
+
+while [ -z "$REQUEST_ID" ]; do
+ CNT=0
+
+ # 1) Confidential
+ #
+ PATTERN=">Confidential:"
+ CONFIDENTIAL=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$CONFIDENTIAL" in
+ ""|yes|no) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$CONFIDENTIAL' is not a valid value for \`Confidential'." ;;
+ esac
+ #
+ # 2) Severity
+ #
+ PATTERN=">Severity:"
+ SEVERITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$SEVERITY" in
+ ""|non-critical|serious|critical) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$SEVERITY' is not a valid value for \`Severity'."
+ esac
+ #
+ # 3) Priority
+ #
+ PATTERN=">Priority:"
+ PRIORITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$PRIORITY" in
+ ""|low|medium|high) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$PRIORITY' is not a valid value for \`Priority'."
+ esac
+ #
+ # 4) Category
+ #
+ PATTERN=">Category:"
+ CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ FOUND=
+ for C in $CATEGORIES
+ do
+ if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi
+ done
+ if [ -n "$FOUND" ]; then
+ CNT=`expr $CNT + 1`
+ else
+ if [ -z "$CATEGORY" ]; then
+ echo "$COMMAND: you must include a Category: field in your report."
+ else
+ echo "$COMMAND: \`$CATEGORY' is not a known category."
+ fi
+ fi
+ #
+ # 5) Class
+ #
+ PATTERN=">Class:"
+ CLASS=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$CLASS" in
+ ""|sw-bug|doc-bug|change-request|support) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$CLASS' is not a valid value for \`Class'."
+ esac
+
+ [ $CNT -lt 5 -a -z "$BATCH" ] &&
+ echo "Errors were found with the problem report."
+
+ while true; do
+ if [ -z "$BATCH" ]; then
+ $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+ read input
+ else
+ if [ $CNT -eq 5 ]; then
+ input=s
+ else
+ input=a
+ fi
+ fi
+ case "$input" in
+ a*)
+ if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $TEMP $BAD
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ xs=1; exit
+ ;;
+ e*)
+ eval $EDIT $TEMP
+ continue 2
+ ;;
+ s*)
+ break 2
+ ;;
+ esac
+ done
+done
+#
+# Remove comments and send the problem report
+# (we have to use patterns, where the comment contains regex chars)
+#
+# /^>Originator:/s;$ORIGINATOR;;
+sed -e "
+/^SEND-PR:/d
+/^>Organization:/,/^>[A-Za-z-]*:/s;$ORGANIZATION_C;;
+/^>Confidential:/s;<.*>;;
+/^>Synopsis:/s;$SYNOPSIS_C;;
+/^>Severity:/s;<.*>;;
+/^>Priority:/s;<.*>;;
+/^>Category:/s;$CATEGORY_C;;
+/^>Class:/s;<.*>;;
+/^>Release:/,/^>[A-Za-z-]*:/s;$RELEASE_C;;
+/^>Environment:/,/^>[A-Za-z-]*:/s;$ENVIRONMENT_C;;
+/^>Description:/,/^>[A-Za-z-]*:/s;$DESCRIPTION_C;;
+/^>How-To-Repeat:/,/^>[A-Za-z-]*:/s;$HOW_TO_REPEAT_C;;
+/^>Fix:/,/^>[A-Za-z-]*:/s;$FIX_C;;
+" $TEMP > $REF
+
+if $MAIL_AGENT < $REF; then
+ echo "$COMMAND: problem report sent"
+ xs=0; exit
+else
+ echo "$COMMAND: mysterious mail failure."
+ if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $REF $BAD
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ xs=1; exit
+fi
diff --git a/gnu/usr.bin/cvs/cvsinit/cvsinit.8 b/gnu/usr.bin/cvs/cvsinit/cvsinit.8
new file mode 100644
index 000000000000..1012d62b687e
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvsinit/cvsinit.8
@@ -0,0 +1,142 @@
+.de Id
+.ds Rv \\$4
+.ds Dt \\$5
+..
+.Id @(#)ccvs/man:$Name: $:$Id: cvsinit.8,v 1.2 1995/11/14 20:48:54 woods Exp $
+.TH CVSINIT 8 "\*(Dt"
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.\" quoted command
+.de `
+.RB ` "\|\\$1\|" '\\$2
+..
+.\"
+.SH "NAME"
+cvsinit \- Concurrent Versions System repository initialization script
+.SH "SYNOPSIS"
+.TP
+.B cvsinit
+.\"
+.SH "DESCRIPTION"
+.\"
+The
+.B cvsinit
+script initializes a repository in the location specified by the
+.SM CVSROOT
+environment variable.
+.SH "FILES"
+For more detailed information on
+.B cvs
+supporting files, see
+.BR cvs ( 5 ).
+.LP
+Files in source repositories (created by
+.BR cvsinit ):
+.TP
+$CVSROOT/CVSROOT
+Directory of global administrative files for repository.
+.TP
+$CVSROOT/commitinfo,v
+Records programs for filtering
+.` "cvs commit"
+requests.
+.TP
+$CVSROOT/history
+Log file of \fBcvs\fP transactions.
+.TP
+$CVSROOT/modules,v
+Definitions for modules in this repository.
+.TP
+$CVSROOT/loginfo,v
+Records programs for piping
+.` "cvs commit"
+log entries.
+.TP
+$CVSROOT/rcsinfo,v
+Records pathnames to templates used during a
+.` "cvs commit"
+operation.
+.TP
+$CVSROOT/editinfo,v
+Records programs for editing/validating
+.` "cvs commit"
+log entries.
+.TP
+$CVSROOT/log
+Sample logging script for use in
+.IR loginfo .
+.TP
+$CVSROOT/commit_prep
+Sample logging script for use in
+.I commitinfo
+with the
+.I log_accum
+script
+.TP
+$CVSROOT/log_accum
+Sample loggin script for use in
+.I loginfo
+with the
+.I commit_prep
+script
+.\"
+.SH "ENVIRONMENT VARIABLES"
+.TP
+.SM CVSROOT
+Should contain the full pathname to the root of the
+.B cvs
+source repository (where the
+.SM RCS
+files are kept). This information must be available to \fBcvs\fP for
+most commands to execute; if
+.SM CVSROOT
+is not set, or if you wish to override it for one invocation, you can
+supply it on the command line:
+.` "cvs \-d \fIcvsroot cvs_command\fP\|.\|.\|."
+You may not need to set
+.SM CVSROOT
+if your \fBcvs\fP binary has the right path compiled in; use
+.` "cvs \-v"
+to display all compiled-in paths.
+.\"
+.SH "AUTHORS"
+.TP
+Dick Grune
+Original author of the
+.B cvs
+shell script version posted to
+.B comp.sources.unix
+in the volume6 release of December, 1986.
+Credited with much of the
+.B cvs
+conflict resolution algorithms.
+.TP
+Brian Berliner
+Coder and designer of the
+.B cvs
+program itself in April, 1989, based on the original work done by Dick.
+.TP
+Jeff Polk
+Helped Brian with the design of the
+.B cvs
+module and vendor branch support and author of the
+.BR checkin ( 1 )
+shell script (the ancestor of
+.` "cvs import").
+.SH "SEE ALSO"
+.BR ci ( 1 ),
+.BR co ( 1 ),
+.BR cvs ( 5 ),
+.BR diff ( 1 ),
+.BR grep ( 1 ),
+.BR mkmodules ( 1 ),
+.BR patch ( 1 ),
+.BR rcs ( 1 ),
+.BR rcsdiff ( 1 ),
+.BR rcsmerge ( 1 ),
+.BR rlog ( 1 ),
+.BR rm ( 1 ),
+.BR sort ( 1 ).
diff --git a/gnu/usr.bin/cvs/doc/cvsclient.texi b/gnu/usr.bin/cvs/doc/cvsclient.texi
new file mode 100644
index 000000000000..9c8f326bd186
--- /dev/null
+++ b/gnu/usr.bin/cvs/doc/cvsclient.texi
@@ -0,0 +1,673 @@
+\input texinfo
+
+@setfilename cvsclient.info
+
+@node Top
+@top CVS Client/Server
+
+This manual describes the client/server protocol used by CVS. It does
+not describe how to use or administer client/server CVS; see the
+regular CVS manual for that.
+
+@menu
+* Goals:: Basic design decisions, requirements, scope, etc.
+* Notes:: Notes on the current implementation
+* How To:: How to remote your favorite CVS command
+* Protocol Notes:: Possible enhancements, limitations, etc. of the protocol
+* Protocol:: Complete description of the protocol
+@end menu
+
+@node Goals
+@chapter Goals
+
+@itemize @bullet
+@item
+Do not assume any access to the repository other than via this protocol.
+It does not depend on NFS, rdist, etc.
+
+@item
+Providing a reliable transport is outside this protocol. It is expected
+that it runs over TCP, UUCP, etc.
+
+@item
+Security and authentication are handled outside this protocol (but see
+below about @samp{cvs kserver}).
+
+@item
+This might be a first step towards adding transactions to CVS (i.e. a
+set of operations is either executed atomically or none of them is
+executed), improving the locking, or other features. The current server
+implementation is a long way from being able to do any of these
+things. The protocol, however, is not known to contain any defects
+which would preclude them.
+
+@item
+The server never has to have any CVS locks in place while it is waiting
+for communication with the client. This makes things robust in the face
+of flaky networks.
+
+@item
+Data is transferred in large chunks, which is necessary for good
+performance. In fact, currently the client uploads all the data
+(without waiting for server responses), and then waits for one server
+response (which consists of a massive download of all the data). There
+may be cases in which it is better to have a richer interraction, but
+the need for the server to release all locks whenever it waits for the
+client makes it complicated.
+@end itemize
+
+@node Notes
+@chapter Notes on the Current Implementation
+
+The client is built in to the normal @code{cvs} program, triggered by a
+@code{CVSROOT} variable containing a colon, for example
+@code{cygnus.com:/rel/cvsfiles}.
+
+The client stores what is stored in checked-out directories (including
+@file{CVS}). The way these are stored is totally compatible with
+standard CVS. The server requires no storage other than the repository,
+which also is totally compatible with standard CVS.
+
+The server is started by @code{cvs server}. There is no particularly
+compelling reason for this rather than making it a separate program
+which shares a lot of sources with cvs.
+
+The server can also be started by @code{cvs kserver}, in which case it
+does an initial Kerberos authentication on stdin. If the authentication
+succeeds, it subsequently runs identically to @code{cvs server}.
+
+The current server implementation can use up huge amounts of memory
+when transmitting a lot of data over a slow link (i.e. the network is
+slower than the server can generate the data). Avoiding this is
+tricky because of the goal of not having the server block on the
+network when it has locks open (this could lock the repository for
+hours if things are running smoothly or longer if not). Several
+solutions are possible. The two-pass design would involve first
+noting what versions of everything we need (with locks in place) and
+then sending the data, blocking on the network, with no locks needed.
+The lather-rinse-repeat design would involve doing things as it does
+now until a certain amount of server memory is being used (10M?), then
+releasing locks, and trying the whole update again (some of it is
+presumably already done). One problem with this is getting merges to
+work right. The two-pass design appears to be the more elegant of the
+two (it actually reduces the amount of time that locks need to be in
+place), but people have expressed concerns about whether it would be
+slower (because it traverses the repository twice). It is not clear
+whether this is a real problem (looking for whether a file needs to be
+updated and actually checking it out are done separately already), but
+I don't think anyone has investigated carefully. One hybrid approach
+which avoids the problem with merges would be to start out in one-pass
+mode and switch to two-pass mode if data is backing up--but this
+complicates the code and should be undertaken only if the pure
+two-pass design is shown to be flawed.
+
+@node How To
+@chapter How to add more remote commands
+
+It's the usual simple twelve step process. Let's say you're making
+the existing @code{cvs fix} command work remotely.
+
+@itemize @bullet
+@item
+Add a declaration for the @code{fix} function, which already implements
+the @code{cvs fix} command, to @file{server.c}.
+@item
+Now, the client side.
+Add a function @code{client_fix} to @file{client.c}, which calls
+@code{parse_cvsroot} and then calls the usual @code{fix} function.
+@item
+Add a declaration for @code{client_fix} to @file{client.h}.
+@item
+Add @code{client_fix} to the "fix" entry in the table of commands in
+@file{main.c}.
+@item
+Now for the server side.
+Add the @code{serve_fix} routine to @file{server.c}; make it do:
+@example @code
+static void
+serve_fix (arg)
+ char *arg;
+@{
+ do_cvs_command (fix);
+@}
+@end example
+@item
+Add the server command @code{"fix"} to the table of requests in @file{server.c}.
+@item
+The @code{fix} function can now be entered in three different situations:
+local (the old situation), client, and server. On the server side it probably
+will not need any changes to cope.
+Modify the @code{fix} function so that if it is run when the variable
+@code{client_active} is set, it starts the server, sends over parsed
+arguments and possibly files, sends a "fix" command to the server,
+and handles responses from the server. Sample code:
+@example @code
+ if (!client_active) @{
+ /* Do whatever you used to do */
+ @} else @{
+ /* We're the local client. Fire up the remote server. */
+ start_server ();
+
+ if (local)
+ if (fprintf (to_server, "Argument -l\n") == EOF)
+ error (1, errno, "writing to server");
+ send_option_string (options);
+
+ send_files (argc, argv, local);
+
+ if (fprintf (to_server, "fix\n") == EOF)
+ error (1, errno, "writing to server");
+ err = get_responses_and_close ();
+ @}
+@end example
+@item
+Build it locally. Copy the new version into somewhere on the
+remote system, in your path so that @code{rsh host cvs} finds it.
+Now you can test it.
+@item
+You may want to set the environment variable @code{CVS_CLIENT_PORT} to
+-1 to prevent the client from contacting the server via a direct TCP
+link. That will force the client to fall back to using @code{rsh},
+which will run your new binary.
+@item
+Set the environment variable @code{CVS_CLIENT_LOG} to a filename prefix
+such as @file{/tmp/cvslog}. Whenever you run a remote CVS command,
+the commands and responses sent across the client/server connection
+will be logged in @file{/tmp/cvslog.in} and @file{/tmp/cvslog.out}.
+Examine them for problems while you're testing.
+@end itemize
+
+This should produce a good first cut at a working remote @code{cvs fix}
+command. You may have to change exactly how arguments are passed,
+whether files or just their names are sent, and how some of the deeper
+infrastructure of your command copes with remoteness.
+
+@node Protocol Notes
+@chapter Notes on the Protocol
+
+A number of enhancements are possible:
+
+@itemize @bullet
+@item
+The @code{Modified} request could be speeded up by sending diffs rather
+than entire files. The client would need some way to keep the version
+of the file which was originally checked out, which would double client
+disk space requirements or require coordination with editors (e.g. maybe
+it could use emacs numbered backups). This would also allow local
+operation of @code{cvs diff} without arguments.
+
+@item
+Have the client keep a copy of some part of the repository. This allows
+all of @code{cvs diff} and large parts of @code{cvs update} and
+@code{cvs ci} to be local. The local copy could be made consistent with
+the master copy at night (but if the master copy has been updated since
+the latest nightly re-sync, then it would read what it needs to from the
+master).
+
+@item
+Provide encryption using kerberos.
+
+@item
+The current procedure for @code{cvs update} is highly sub-optimal if
+there are many modified files. One possible alternative would be to
+have the client send a first request without the contents of every
+modified file, then have the server tell it what files it needs. Note
+the server needs to do the what-needs-to-be-updated check twice (or
+more, if changes in the repository mean it has to ask the client for
+more files), because it can't keep locks open while waiting for the
+network. Perhaps this whole thing is irrelevant if client-side
+repositories are implemented, and the rcsmerge is done by the client.
+@end itemize
+
+@node Protocol
+@chapter The CVS client/server protocol
+
+@menu
+* Entries Lines::
+* Modes::
+* Requests::
+* Responses::
+* Example::
+@end menu
+
+@node Entries Lines
+@section Entries Lines
+
+Entries lines are transmitted as:
+
+@example
+/ @var{name} / @var{version} / @var{conflict} / @var{options} / @var{tag_or_date}
+@end example
+
+@var{tag_or_date} is either @samp{T} @var{tag} or @samp{D} @var{date}
+or empty. If it is followed by a slash, anything after the slash
+shall be silently ignored.
+
+@var{version} can be empty, or start with @samp{0} or @samp{-}, for no
+user file, new user file, or user file to be removed, respectively.
+
+@var{conflict}, if it starts with @samp{+}, indicates that the file had
+conflicts in it. The rest of @var{conflict} is @samp{=} if the
+timestamp matches the file, or anything else if it doesn't. If
+@var{conflict} does not start with a @samp{+}, it is silently ignored.
+
+@node Modes
+@section Modes
+
+A mode is any number of repetitions of
+
+@example
+@var{mode-type} = @var{data}
+@end example
+
+separated by @samp{,}.
+
+@var{mode-type} is an identifier composed of alphanumeric characters.
+Currently specified: @samp{u} for user, @samp{g} for group, @samp{o} for
+other, as specified in POSIX. If at all possible, give these their
+POSIX meaning and use other mode-types for other behaviors. For
+example, on VMS it shouldn't be hard to make the groups behave like
+POSIX, but you would need to use ACLs for some cases.
+
+@var{data} consists of any data not containing @samp{,}, @samp{\0} or
+@samp{\n}. For @samp{u}, @samp{g}, and @samp{o} mode types, data
+consists of alphanumeric characters, where @samp{r} means read, @samp{w}
+means write, @samp{x} means execute, and unrecognized letters are
+silently ignored.
+
+@node Requests
+@section Requests
+
+File contents (noted below as @var{file transmission}) can be sent in
+one of two forms. The simpler form is a number of bytes, followed by a
+newline, followed by the specified number of bytes of file contents.
+These are the entire contents of the specified file. Second, if both
+client and server support @samp{gzip-file-contents}, a @samp{z} may
+precede the length, and the `file contents' sent are actually compressed
+with @samp{gzip}. The length specified is that of the compressed
+version of the file.
+
+In neither case are the file content followed by any additional data.
+The transmission of a file will end with a newline iff that file (or its
+compressed form) ends with a newline.
+
+@table @code
+@item Root @var{pathname} \n
+Response expected: no.
+Tell the server which @code{CVSROOT} to use.
+
+@item Valid-responses @var{request-list} \n
+Response expected: no.
+Tell the server what responses the client will accept.
+request-list is a space separated list of tokens.
+
+@item valid-requests \n
+Response expected: yes.
+Ask the server to send back a @code{Valid-requests} response.
+
+@item Repository @var{repository} \n
+Response expected: no. Tell the server what repository to use. This
+should be a directory name from a previous server response. Note that
+this both gives a default for @code{Entry } and @code{Modified } and
+also for @code{ci} and the other commands; normal usage is to send a
+@code{Repository } for each directory in which there will be an
+@code{Entry } or @code{Modified }, and then a final @code{Repository }
+for the original directory, then the command.
+
+@item Directory @var{local-directory} \n
+Additional data: @var{repository} \n. This is like @code{Repository},
+but the local name of the directory may differ from the repository name.
+If the client uses this request, it affects the way the server returns
+pathnames; see @ref{Responses}. @var{local-directory} is relative to
+the top level at which the command is occurring (i.e. the last
+@code{Directory} or @code{Repository} which is sent before the command).
+
+@item Max-dotdot @var{level} \n
+Tell the server that @var{level} levels of directories above the
+directory which @code{Directory} requests are relative to will be
+needed. For example, if the client is planning to use a
+@code{Directory} request for @file{../../foo}, it must send a
+@code{Max-dotdot} request with a @var{level} of at least 2.
+@code{Max-dotdot} must be sent before the first @code{Directory}
+request.
+
+@item Static-directory \n
+Response expected: no. Tell the server that the directory most recently
+specified with @code{Repository} or @code{Directory} should not have
+additional files checked out unless explicitly requested. The client
+sends this if the @code{Entries.Static} flag is set, which is controlled
+by the @code{Set-static-directory} and @code{Clear-static-directory}
+responses.
+
+@item Sticky @var{tagspec} \n
+Response expected: no. Tell the server that the directory most recently
+specified with @code{Repository} has a sticky tag or date @var{tagspec}.
+The first character of @var{tagspec} is @samp{T} for a tag, or @samp{D}
+for a date. The remainder of @var{tagspec} contains the actual tag or
+date.
+
+@item Checkin-prog @var{program} \n
+Response expected: no. Tell the server that the directory most recently
+specified with @code{Directory} has a checkin program @var{program}.
+Such a program would have been previously set with the
+@code{Set-checkin-prog} response.
+
+@item Update-prog @var{program} \n
+Response expected: no. Tell the server that the directory most recently
+specified with @code{Directory} has an update program @var{program}.
+Such a program would have been previously set with the
+@code{Set-update-prog} response.
+
+@item Entry @var{entry-line} \n
+Response expected: no. Tell the server what version of a file is on the
+local machine. The name in @var{entry-line} is a name relative to the
+directory most recently specified with @code{Repository}. If the user
+is operating on only some files in a directory, @code{Entry} requests
+for only those files need be included. If an @code{Entry} request is
+sent without @code{Modified}, @code{Unchanged}, or @code{Lost} for that
+file the meaning depends on whether @code{UseUnchanged} has been sent;
+if it has been it means the file is lost, if not it means the file is
+unchanged.
+
+@item Modified @var{filename} \n
+Response expected: no. Additional data: mode, \n, file transmission.
+Send the server a copy of one locally modified file. @var{filename} is
+relative to the most recent repository sent with @code{Repository}. If
+the user is operating on only some files in a directory, only those
+files need to be included. This can also be sent without @code{Entry},
+if there is no entry for the file.
+
+@item Lost @var{filename} \n
+Response expected: no. Tell the server that @var{filename} no longer
+exists. The name is relative to the most recent repository sent with
+@code{Repository}. This is used for any case in which @code{Entry} is
+being sent but the file no longer exists. If the client has issued the
+@code{UseUnchanged} request, then this request is not used.
+
+@item Unchanged @var{filename} \n
+Response expected: no. Tell the server that @var{filename} has not been
+modified in the checked out directory. The name is relative to the most
+recent repository sent with @code{Repository}. This request can only be
+issued if @code{UseUnchanged} has been sent.
+
+@item UseUnchanged \n
+Response expected: no. Tell the server that the client will be
+indicating unmodified files with @code{Unchanged}, and that files for
+which no information is sent are nonexistent on the client side, not
+unchanged. This is necessary for correct behavior since only the server
+knows what possible files may exist, and thus what files are
+nonexistent.
+
+@item Argument @var{text} \n
+Response expected: no.
+Save argument for use in a subsequent command. Arguments
+accumulate until an argument-using command is given, at which point
+they are forgotten.
+
+@item Argumentx @var{text} \n
+Response expected: no. Append \n followed by text to the current
+argument being saved.
+
+@item Global_option @var{option} \n
+Transmit one of the global options @samp{-q}, @samp{-Q}, @samp{-l},
+@samp{-t}, @samp{-r}, or @samp{-n}. @var{option} must be one of those
+strings, no variations (such as combining of options) are allowed. For
+graceful handling of @code{valid-requests}, it is probably better to
+make new global options separate requests, rather than trying to add
+them to this request.
+
+@item expand-modules \n
+Response expected: yes. Expand the modules which are specified in the
+arguments. Returns the data in @code{Module-expansion} responses. Note
+that the server can assume that this is checkout or export, not rtag or
+rdiff; the latter do not access the working directory and thus have no
+need to expand modules on the client side.
+
+@item co \n
+@itemx update \n
+@itemx ci \n
+@itemx diff \n
+@itemx tag \n
+@itemx status \n
+@itemx log \n
+@itemx add \n
+@itemx remove \n
+@itemx rdiff \n
+@itemx rtag \n
+@itemx import \n
+@itemx admin \n
+@itemx export \n
+@itemx history \n
+@itemx release \n
+Response expected: yes. Actually do a cvs command. This uses any
+previous @code{Argument}, @code{Repository}, @code{Entry},
+@code{Modified}, or @code{Lost} requests, if they have been sent. The
+last @code{Repository} sent specifies the working directory at the time
+of the operation. No provision is made for any input from the user.
+This means that @code{ci} must use a @code{-m} argument if it wants to
+specify a log message.
+
+@item update-patches \n
+This request does not actually do anything. It is used as a signal that
+the server is able to generate patches when given an @code{update}
+request. The client must issue the @code{-u} argument to @code{update}
+in order to receive patches.
+
+@item gzip-file-contents @var{level} \n
+This request asks the server to filter files it sends to the client
+through the @samp{gzip} program, using the specified level of
+compression. If this request is not made, the server must not do any
+compression.
+
+This is only a hint to the server. It may still decide (for example, in
+the case of very small files, or files that already appear to be
+compressed) not to do the compression. Compression is indicated by a
+@samp{z} preceding the file length.
+
+Availability of this request in the server indicates to the client that
+it may compress files sent to the server, regardless of whether the
+client actually uses this request.
+
+@item @var{other-request} @var{text} \n
+Response expected: yes.
+Any unrecognized request expects a response, and does not
+contain any additional data. The response will normally be something like
+@samp{error unrecognized request}, but it could be a different error if
+a previous command which doesn't expect a response produced an error.
+@end table
+
+When the client is done, it drops the connection.
+
+@node Responses
+@section Responses
+
+After a command which expects a response, the server sends however many
+of the following responses are appropriate. Pathnames are of the actual
+files operated on (i.e. they do not contain @samp{,v} endings), and are
+suitable for use in a subsequent @code{Repository} request. However, if
+the client has used the @code{Directory} request, then it is instead a
+local directory name relative to the directory in which the command was
+given (i.e. the last @code{Directory} before the command). Then a
+newline and a repository name (the pathname which is sent if
+@code{Directory} is not used). Then the slash and the filename. For
+example, for a file @file{i386.mh} which is in the local directory
+@file{gas.clean/config} and for which the repository is
+@file{/rel/cvsfiles/devo/gas/config}:
+
+@example
+gas.clean/config/
+/rel/cvsfiles/devo/gas/config/i386.mh
+@end example
+
+Any response always ends with @samp{error} or @samp{ok}. This indicates
+that the response is over.
+
+@table @code
+@item Valid-requests @var{request-list} \n
+Indicate what requests the server will accept. @var{request-list}
+is a space separated list of tokens. If the server supports sending
+patches, it will include @samp{update-patches} in this list. The
+@samp{update-patches} request does not actually do anything.
+
+@item Checked-in @var{pathname} \n
+Additional data: New Entries line, \n. This means a file @var{pathname}
+has been successfully operated on (checked in, added, etc.). name in
+the Entries line is the same as the last component of @var{pathname}.
+
+@item New-entry @var{pathname} \n
+Additional data: New Entries line, \n. Like @code{Checked-in}, but the
+file is not up to date.
+
+@item Updated @var{pathname} \n
+Additional data: New Entries line, \n, mode, \n, file transmission. A
+new copy of the file is enclosed. This is used for a new revision of an
+existing file, or for a new file, or for any other case in which the
+local (client-side) copy of the file needs to be updated, and after
+being updated it will be up to date. If any directory in pathname does
+not exist, create it.
+
+@item Merged @var{pathname} \n
+This is just like @code{Updated} and takes the same additional data,
+with the one difference that after the new copy of the file is enclosed,
+it will still not be up to date. Used for the results of a merge, with
+or without conflicts.
+
+@item Patched @var{pathname} \n
+This is just like @code{Updated} and takes the same additional data,
+with the one difference that instead of sending a new copy of the file,
+the server sends a patch produced by @samp{diff -u}. This client must
+apply this patch, using the @samp{patch} program, to the existing file.
+This will only be used when the client has an exact copy of an earlier
+revision of a file. This response is only used if the @code{update}
+command is given the @samp{-u} argument.
+
+@item Checksum @var{checksum}\n
+The @var{checksum} applies to the next file sent over via
+@code{Updated}, @code{Merged}, or @code{Patched}. In the case of
+@code{Patched}, the checksum applies to the file after being patched,
+not to the patch itself. The client should compute the checksum itself,
+after receiving the file or patch, and signal an error if the checksums
+do not match. The checksum is the 128 bit MD5 checksum represented as
+32 hex digits. This response is optional, and is only used if the
+client supports it (as judged by the @code{Valid-responses} request).
+
+@item Copy-file @var{pathname} \n
+Additional data: @var{newname} \n. Copy file @var{pathname} to
+@var{newname} in the same directory where it already is. This does not
+affect @code{CVS/Entries}.
+
+@item Removed @var{pathname} \n
+The file has been removed from the repository (this is the case where
+cvs prints @samp{file foobar.c is no longer pertinent}).
+
+@item Remove-entry @var{pathname} \n
+The file needs its entry removed from @code{CVS/Entries}, but the file
+itself is already gone (this happens in response to a @code{ci} request
+which involves committing the removal of a file).
+
+@item Set-static-directory @var{pathname} \n
+This instructs the client to set the @code{Entries.Static} flag, which
+it should then send back to the server in a @code{Static-directory}
+request whenever the directory is operated on. @var{pathname} ends in a
+slash; its purpose is to specify a directory, not a file within a
+directory.
+
+@item Clear-static-directory @var{pathname} \n
+Like @code{Set-static-directory}, but clear, not set, the flag.
+
+@item Set-sticky @var{pathname} \n
+Additional data: @var{tagspec} \n. Tell the client to set a sticky tag
+or date, which should be supplied with the @code{Sticky} request for
+future operations. @var{pathname} ends in a slash; its purpose is to
+specify a directory, not a file within a directory. The first character
+of @var{tagspec} is @samp{T} for a tag, or @samp{D} for a date. The
+remainder of @var{tagspec} contains the actual tag or date.
+
+@item Clear-sticky @var{pathname} \n
+Clear any sticky tag or date set by @code{Set-sticky}.
+
+@item Set-checkin-prog @var{dir} \n
+Additional data: @var{prog} \n. Tell the client to set a checkin
+program, which should be supplied with the @code{Checkin-prog} request
+for future operations.
+
+@item Set-update-prog @var{dir} \n
+Additional data: @var{prog} \n. Tell the client to set an update
+program, which should be supplied with the @code{Update-prog} request
+for future operations.
+
+@item Module-expansion @var{pathname} \n
+Return a file or directory which is included in a particular module.
+@var{pathname} is relative to cvsroot, unlike most pathnames in
+responses.
+
+@item M @var{text} \n
+A one-line message for the user.
+
+@item E @var{text} \n
+Same as @code{M} but send to stderr not stdout.
+
+@item error @var{errno-code} @samp{ } @var{text} \n
+The command completed with an error. @var{errno-code} is a symbolic
+error code (e.g. @code{ENOENT}); if the server doesn't support this
+feature, or if it's not appropriate for this particular message, it just
+omits the errno-code (in that case there are two spaces after
+@samp{error}). Text is an error message such as that provided by
+strerror(), or any other message the server wants to use.
+
+@item ok \n
+The command completed successfully.
+@end table
+
+@node Example
+@section Example
+
+Lines beginning with @samp{c>} are sent by the client; lines beginning
+with @samp{s>} are sent by the server; lines beginning with @samp{#} are
+not part of the actual exchange.
+
+@example
+c> Root /rel/cvsfiles
+# In actual practice the lists of valid responses and requests would
+# be longer
+c> Valid-responses Updated Checked-in M ok error
+c> valid-requests
+s> Valid-requests Root co Modified Entry Repository ci Argument Argumentx
+s> ok
+# cvs co devo/foo
+c> Argument devo/foo
+c> co
+s> Updated /rel/cvsfiles/devo/foo/foo.c
+s> /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
+s> 26
+s> int mein () @{ abort (); @}
+s> Updated /rel/cvsfiles/devo/foo/Makefile
+s> /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
+s> 28
+s> foo: foo.c
+s> $(CC) -o foo $<
+s> ok
+# In actual practice the next part would be a separate connection.
+# Here it is shown as part of the same one.
+c> Repository /rel/cvsfiles/devo/foo
+# foo.c relative to devo/foo just set as Repository.
+c> Entry /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
+c> Entry /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
+c> Modified foo.c
+c> 26
+c> int main () @{ abort (); @}
+# cvs ci -m <log message> foo.c
+c> Argument -m
+c> Argument Well, you see, it took me hours and hours to find this typo and I
+c> Argumentx searched and searched and eventually had to ask John for help.
+c> Argument foo.c
+c> ci
+s> Checked-in /rel/cvsfiles/devo/foo/foo.c
+s> /foo.c/1.5/ Mon Apr 19 15:54:22 CDT 1993//
+s> M Checking in foo.c;
+s> M /cygint/rel/cvsfiles/devo/foo/foo.c,v <-- foo.c
+s> M new revision: 1.5; previous revision: 1.4
+s> M done
+s> ok
+@end example
+@bye
diff --git a/gnu/usr.bin/cvs/examples/checkoutlist b/gnu/usr.bin/cvs/examples/checkoutlist
new file mode 100644
index 000000000000..45ee6dc44f15
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/checkoutlist
@@ -0,0 +1,20 @@
+#
+#ident "@(#)cvs/examples:$Name: $:$Id: checkoutlist,v 1.2 1995/11/14 23:24:49 woods Exp $"
+#
+# The "checkoutlist" file is used to support additional version controlled
+# administrative files in $CVSROOT/CVSROOT, such as template files.
+#
+# The first entry on a line is a filename which will be checked out from
+# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
+# The remainder of the line is an error message to use if the file cannot
+# be checked out.
+#
+# File format:
+#
+# [<whitespace>]<filename><whitespace><error message><end-of-line>
+#
+# comment lines begin with '#'
+#
+rcstemplate
+wrap
+unwrap
diff --git a/gnu/usr.bin/cvs/examples/cvswrappers b/gnu/usr.bin/cvs/examples/cvswrappers
new file mode 100644
index 000000000000..c666292e6460
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/cvswrappers
@@ -0,0 +1,29 @@
+#
+#ident "@(#)cvs/examples:$Name: $:$Id: cvswrappers,v 1.3 1995/11/14 23:23:11 woods Exp $"
+#
+# This file describes wrappers and other binary files to CVS.
+#
+# Wrappers are the concept where directories of files are to be
+# treated as a single file. The intended use is to wrap up a wrapper
+# into a single tar such that the tar archive can be treated as a
+# single binary file in CVS.
+#
+# To solve the problem effectively, it was also necessary to be able to
+# prevent rcsmerge from merging these files.
+#
+# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
+#
+# wildcard [option value][option value]...
+#
+# where option is one of
+# -f from cvs filter value: path to filter
+# -t to cvs filter value: path to filter
+# -m update methodology value: MERGE or COPY
+#
+# and value is a single-quote delimited value.
+#
+#
+*.nib -f '$CVSROOT/CVSROOT/unwrap %s' -t '$CVSROOT/CVSROOT/wrap %s %s'
+*.rtfd -f '$CVSROOT/CVSROOT/unwrap %s' -t '$CVSROOT/CVSROOT/wrap %s %s'
+*.draw -f '$CVSROOT/CVSROOT/unwrap %s' -t '$CVSROOT/CVSROOT/wrap %s %s'
+*.tiff -m 'COPY'
diff --git a/gnu/usr.bin/cvs/examples/rcstemplate b/gnu/usr.bin/cvs/examples/rcstemplate
new file mode 100644
index 000000000000..c9a2d1eb248c
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/rcstemplate
@@ -0,0 +1,7 @@
+CVS:
+CVS: WARNING: You are commiting a change to the main source repository.
+CVS:
+CVS: This change will be immediately available to all other users
+CVS: of this repository! Please be sure your changes have been
+CVS: adequately tested.
+CVS:
diff --git a/gnu/usr.bin/cvs/examples/taginfo b/gnu/usr.bin/cvs/examples/taginfo
new file mode 100644
index 000000000000..02de62b2808e
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/taginfo
@@ -0,0 +1,25 @@
+#
+#ident "@(#)cvs/examples:$Name: $:$Id: taginfo,v 1.3 1995/11/14 23:27:52 woods Exp $"
+#
+# The "taginfo" file is used to control pre-tag checks.
+# The filter on the right is invoked with the following arguments:
+#
+# $1 -- tagname
+# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
+# $3 -- repository
+# $4-> file revision [file revision ...]
+#
+# A non-zero exit of the filter program will cause the tag to be aborted.
+#
+# The first entry on a line is a regular expression which is tested
+# against the directory that the change is being committed to, relative
+# to the $CVSROOT. For the first match that is found, then the remainder
+# of the line is the name of the filter to run.
+#
+# If the repository name does not match any of the regular expressions in this
+# file, the "DEFAULT" line is used, if it is specified.
+#
+# If the name "ALL" appears as a regular expression it is always used
+# in addition to the first matching regex or "DEFAULT".
+#
+DEFAULT $CVSROOT/CVSROOT/tag_logging_program
diff --git a/gnu/usr.bin/cvs/examples/unwrap b/gnu/usr.bin/cvs/examples/unwrap
new file mode 100644
index 000000000000..def05610f551
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/unwrap
@@ -0,0 +1,21 @@
+#! /bin/sh
+#
+# unwrap - extract the combined package (created with wrap)
+#
+#ident "@(#)cvs/examples:$Name: $:$Id: unwrap,v 1.1 1995/11/14 23:20:30 woods Exp $"
+
+# move the file to a new name with an extension
+rm -rf $1.cvswrap
+mv $1 $1.cvswrap
+
+# untar the file
+
+if `gzip -t $1.cvswrap > /dev/null 2>&1`
+then
+ gzcat -d $1.cvswrap | gnutar --preserve --sparse -x -f -
+else
+ gnutar --preserve --sparse -x -f $1.cvswrap
+fi
+
+# remove the original
+rm -rf $1.cvswrap
diff --git a/gnu/usr.bin/cvs/examples/wrap b/gnu/usr.bin/cvs/examples/wrap
new file mode 100644
index 000000000000..b6a6a772c240
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/wrap
@@ -0,0 +1,21 @@
+#! /bin/sh
+#
+# wrap - Combine a directory into a single tar package.
+#
+#ident "@(#)cvs/examples:$Name: $:$Id: wrap,v 1.1 1995/11/14 23:20:32 woods Exp $"
+
+# This script is always called with the current directory set to
+# where the file to be combined exists. but i may get called with a
+# path to where cvs first started executing. (this probably should be
+# fixed in cvs) so strip out all of the directory information. The
+# first sed expression will only work if the path has a leading /
+# if it doesn't the one in the if statement will work.
+DIRNAME=`echo $1 | sed -e "s|/.*/||g"`
+if [ ! -d $DIRNAME ] ; then
+ DIRNAME=`echo $1 | sed -e "s|.*/||g"`
+fi
+#
+# Now tar up the directory but we now will only get a relative path
+# even if the user did a cvs commit . at the top.
+#
+gnutar --preserve --sparse -cf - $DIRNAME | gzip --no-name --best -c > $2
diff --git a/gnu/usr.bin/cvs/lib/ChangeLog b/gnu/usr.bin/cvs/lib/ChangeLog
new file mode 100644
index 000000000000..c8aa4a480682
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/ChangeLog
@@ -0,0 +1,335 @@
+Mon Dec 4 10:54:04 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * getdate.c: Remove #line directives. I know, this is a kludge,
+ but Visual C++ 2.1 seems to require it (why, I have no idea. It
+ has no trouble with the #line directives in getdate in CVS 1.6).
+
+Sat Nov 18 16:20:37 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * rename.c: same.
+
+ * mkdir.c: Use new macro `existence_error', instead of comparing
+ errno to ENOENT directly.
+
+ * system.h (existence_error): new macro, tries to portably ask if
+ errno represents a file-not-exist error.
+
+Fri Nov 17 20:08:58 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * system.h (NEED_DECOY_PERMISSIONS): moved this section to where
+ it belongs, duh.
+
+ * getdate.c: if STDC_HEADERS, then just include <stdlib.h> instead
+ of declaring malloc() and realloc() to be char *.
+
+ * system.h: ifdef NEED_DECOY_PERMISSIONS, then define the S_I*
+ permission masks for USR, GRP, and OTH in terms of the simpler
+ OS/2 masks.
+
+Wed Nov 15 15:36:03 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * system.h: ifdef USE_OWN_TCPIP_H, then include "tcpip.h". Only
+ OS/2 does this right now.
+
+Tue Nov 14 18:44:57 1995 Greg A. Woods <woods@most.weird.com>
+
+ * getdate.c: OK, this one is from SunOS-4.1 yacc and may be more
+ portable -- at least it compiles silently here! ;-)
+
+Mon Nov 13 03:53:45 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * fnmatch.c: conform to 80 column standard (yes, I'm a pedant).
+
+Wed Nov 8 11:10:59 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * system.h (STAT_MACROS): ifdef S_IFMT, then use it as before; but
+ if it's not defined, then just do a single mask and assume
+ acceptance any of non-zero result. Norbert, I trust you'll let me
+ know if this is unsatisfactory. :-)
+ Ifdef HAVE_SYS_UTIME_H, then include <sys/utime.h>. Only OS/2
+ defines this right now.
+
+Wed Nov 8 13:18:51 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * valloc.c: omit malloc declaration (it's already in system.h
+ which is included and conflicts with <stdlib.h> on some
+ systems).
+
+Tue Nov 7 19:38:48 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * system.h (STAT_MACROS_BROKEN): undo previous change, because
+ else all regular files will be identified as links (the mask for
+ links is S_IFREG|S_IFCHR).
+
+Mon Nov 6 19:20:56 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * system.h (STAT_MACROS_BROKEN): in defining the S_IF* macros,
+ don't fold to 1 or 0 by first masking with S_IFMT; not all
+ systems have that macro, and anyway it's only necessary that we
+ return non-zero.
+
+Fri Oct 27 13:43:35 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * save-cwd.c: use __PROTO instead of __P (see below).
+
+ * getline.h (__PROTO): same as below.
+
+ * save-cwd.h (__PROTO): replaces __P. New name, so don't ask if
+ already defined. The conflict was that OS/2 w/ IBM C/C++ uses
+ `__P' for something else, in <ctype.h> of all places.
+
+ * system.h: do nothing about alloca ifdef ALLOCA_IN_STDLIB (see
+ ../src/ChangeLog).
+
+Tue Oct 24 13:01:25 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * wait.h: include sys/resource.h if available. This is needed at
+ least under AIX-3.2 where <sys/wait.h> doesn't include it.
+
+Mon Oct 23 17:39:11 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * valloc.c (valloc): change parameter definition
+
+Sun Oct 22 14:15:44 1995 Jim Meyering (meyering@comco.com)
+
+ * getline.c, getline.h: New files.
+ * Makefile.in (SOURCES, OBJECTS, HEADERS): Add getline.c, getline.o,
+ and getline.h, respectively.
+
+Tue Oct 10 18:01:50 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * Makefile.in (cvs_srcdir): define cvs_srcdir to be ../src, then
+ include it with -I so save_cwd.c can find error.h (for example).
+
+Sun Oct 8 12:27:57 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * system.h: define POSIX_SIGNALS or BSD_SIGNALS if configure has
+ located all the necessary functions for each "type".
+ * sighandle.c: detect/use POSIX/BSD reliable signals (especially
+ for blocking signals in critical sections). Helps prevent stray
+ locks on interruption.
+
+Mon Oct 2 18:11:23 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * system.h: Doc fix.
+
+Mon Oct 2 18:10:35 1995 Larry Jones <larry.jones@sdrc.com>
+
+ * regex.c: compile 4.2 BSD compatible functions even when
+ _POSIX_SOURCE is defined since we need them and we wouldn't be
+ compiling this file unless they don't exist.
+
+Mon Oct 2 10:32:20 1995 Michael Finken <finken@conware.de>
+
+ * strstr.c (strstr): new file and func.
+
+ * Makefile.in (SOURCES): added strstr.c.
+
+Sun Oct 1 21:03:40 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * regex.c: reverted below change.
+
+Thu Sep 28 13:37:04 1995 Larry Jones <larry.jones@sdrc.com>
+
+ * regexp.c: check for ISC.
+
+Thu Sep 7 19:18:00 1995 Jim Blandy <jimb@cyclic.com>
+
+ * save-cwd.c: #include <direct.h> and <io.h>, on systems that
+ have them.
+
+ * getopt.c (_getopt_internal): Cast the return value of strlen,
+ which is unsigned, before comparing it with the difference between
+ two pointers, which is unsigned.
+
+Thu Aug 31 11:31:42 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * getdate.y [STDC_HEADERS]: #include <stdlib.h>, for abort.
+ [HAVE_ALLOCA_H]: #include <alloca.h>, for alloca on Windows NT.
+
+Wed Aug 30 18:48:44 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * system.h [HAVE_IO_H]: #include <io.h>, for Windows NT.
+ [HAVE_DIRECT_H]: #include <direct.h>, for Windows NT.
+ (CVS_MKDIR, FOLD_FN_CHAR, fnfold, fncmp, ISDIRSEP, OPEN_BINARY,
+ FOPEN_BINARY_READ, FOPEN_BINARY_WRITE): New macros/functions, for
+ use in system-sensitive code.
+
+ * regex.c (re_set_registers): start and end are pointers, not
+ integers. Cast the initializing value appropriately.
+
+ * getopt.c [HAVE_STRING_H]: #include <string.h>, to avoid
+ warnings.
+
+ * fnmatch.c (FOLD_FN_CHAR): Give this a dummy #definition if
+ config.h didn't #define it.
+ (fnmatch): Pass filename characters through FOLD_FN_CHAR before
+ comparing them.
+
+ * argmatch.c: #include <sys/types.h>.
+ (argmatch): Declare arglen to be a size_t, rather than an int,
+ to avoid signed/unsigned comparison "problems".
+
+ * .cvsignore: Remove getdate.c from this file. We want to
+ distribute it, for systems that don't have a Yacc-equivalent
+ installed (like Windows NT).
+
+Sat Aug 19 22:00:51 1995 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * error.c: Don't #define CVS_SUPPORT here. config.h takes care of
+ that for us.
+ [CVS_SUPPORT] (error_use_protocol): New variable, with apology.
+ (error): If error_use_protocol is set, report errors using the
+ client/server protocol.
+ * error.h [CVS_SUPPORT]: Extern decl for error_use_protocol.
+
+Fri Aug 4 00:01:24 1995 Jim Meyering (meyering@comco.com)
+
+ * xgetwd.c: Don't declare free. A K&R style declaration gets
+ a conflict on some Sun systems when compiling with acc.
+
+ * save-cwd.c: New file.
+ * save-cwd.h: New file.
+ * Makefile.in (SOURCES): Add save-cwd.c
+ (OBJECTS): Add save-cwd.o.
+ (HEADERS): Add save-cwd.h.
+
+Thu Aug 3 00:55:54 1995 Jim Meyering (meyering@comco.com)
+
+ * error.h: New file.
+ * Makefile.in (HEADERS): Add error.h.
+
+Sat Jul 29 15:53:55 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (SOURCES): Add getdate.c.
+
+Thu Jul 27 09:11:41 1995 Robert Lipe <robertl@rjlhome.arnet.com>
+
+ * system.h: Check for PATHSIZE before falling back to _POSIX_PATH_MAX.
+
+Thu Jul 20 12:38:03 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * error.c: Instead of calling cvs functions to clean up, allow cvs
+ to register a callback via error_set_cleanup. Avoids hassles with
+ include files and SERVER_SUPPORT and so on.
+
+Tue Jul 18 21:18:00 1995 Jim Blandy <jimb@cyclic.com>
+
+ * system.h: Include <sys/param.h> only if HAVE_SYS_PARAM_H
+ is #defined. We've added a test to configure.in to #define this
+ on most systems.
+
+Thu Jul 13 11:22:21 1995 Jim Meyering (meyering@comco.com)
+
+ * xgetwd.c: New file.
+ * Makefile.in (SOURCES): Add xgetwd.c
+ (OBJECTS): Add xgetwd.o.
+
+Wed Jul 12 09:18:49 1995 Jim Meyering (meyering@comco.com)
+
+ * Makefile.in (OBJECTS): Remove fnmatch.o. Now configure adds it
+ to LIBOBJS when necessary.
+
+Fri Jun 30 16:27:18 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * rename.c (rename): If MVDIR is not defined, just give an error
+ on attempt to rename a directory.
+
+Thu Jun 29 00:46:31 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * system.h: Check HAVE_SYS_TIMEB_H not non-existent HAVE_TIMEB_H.
+
+ * system.h: Don't define alloca if it is already defined.
+
+Wed Jun 28 15:24:51 1995 James Kingdon <kingdon@harvey.cyclic.com>
+
+ * system.h: If NeXT, define utimbuf ourself.
+
+Mon May 29 22:32:40 1995 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * system.h: Handle time and directory headers as recommended in
+ the autoconf manual.
+ Undefine the S_FOO() macros if STAT_MACROS_BROKEN is set.
+ Don't define mode_t, as it is handled by config.h.
+
+Sat May 27 08:46:00 1995 Jim Meyering (meyering@comco.com)
+
+ * Makefile.in (Makefile): Regenerate only Makefile in current
+ directory when Makefile.in is out of date. Depend on ../config.status.
+
+Fri Apr 28 22:49:25 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * Makefile.in (SOURCES, OBJECTS): Updated.
+ (HEADERS): New variable.
+ (DISTFILES): Updated.
+ (dist-dir): Renamed from dist; changed to work with DISTDIR
+ variable passed from parent.
+
+Wed Feb 8 06:37:53 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * system.h (S_IRUSR et al): Define if not already defined.
+
+ * waitpid.c [HAVE_CONFIG_H]: Include "config.h".
+ (ualloc): Return OLDPTR rather than running off the end.
+
+Mon Aug 22 22:48:19 1994 Ken Raeburn (raeburn@kr-pc.cygnus.com)
+
+ * error.c (strerror): Replaced conditional static definition
+ (always used, since the condition variable was never set) with an
+ extern declaration, since it's provided by libc or strerror.c.
+
+Wed Aug 10 14:54:25 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * Makefile.in (SOURCES): Add waitpid.c.
+ * waitpid.c: New file.
+
+Tue Aug 9 16:00:12 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * md5.h (uint32): If SIZEOF_LONG isn't 4, don't define this to be
+ "unsigned long"; try SIZEOF_INT and "unsigned int", otherwise
+ complain.
+
+ * md5.c: Include config.h.
+ (const): Don't bother defining here, config.h should take care of
+ it.
+
+ * valloc.c (malloc): Declare.
+
+Fri Jul 15 12:57:20 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * getopt.c: Do not include <stdlib.h> unless __GNU_LIBRARY__ is
+ defined. On Irix 5.2, <stdlib.h> includes <getopt.h>, which
+ causes a multiple definition of struct option.
+
+Fri Jul 8 10:04:59 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * md5.h, md5.c: Remove ANSI-isms.
+
+Thu Jul 7 20:24:18 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
+
+ * md5.h, md5.c: New files.
+ * Makefile.in (SOURCES): Add md5.c.
+ (OBJECTS): Add md5.o.
+ (DISTFILES): Add md5.h.
+ (md5.o): New target; depend upon md5.h.
+
+Fri May 27 18:15:34 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
+
+ * valloc.c: New file.
+
+Tue May 17 08:18:26 1994 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * error.c (error, fperror): If server_active, call server_cleanup
+ as well as Lock_Cleanup.
+
+Thu Jan 6 13:45:04 1994 Ken Raeburn (raeburn@cujo.cygnus.com)
+
+ * system.h: Fix Dec 27 change to work correctly. Makes Sep 9
+ change unnecessary, so backed that one out. Never define PATH_MAX
+ in terms of pathconf, because that doesn't produce a constant, and
+ PATH_MAX is used to set array sizes.
+
+Mon Dec 27 14:22:07 1993 Mark Eichin (eichin@cygnus.com)
+
+ * system.h: don't touch PATH_MAX or MAXPATHLEN if *both* of them
+ are already defined, as one may be defined in terms of the other.
diff --git a/gnu/usr.bin/cvs/lib/error.h b/gnu/usr.bin/cvs/lib/error.h
new file mode 100644
index 000000000000..7d4f5353797a
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/error.h
@@ -0,0 +1,47 @@
+/* error.h -- declaration for error-reporting function
+ Copyright (C) 1995 Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _error_h_
+#define _error_h_
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# endif
+/* The __-protected variants of `format' and `printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __format__ format
+# define __printf__ printf
+# endif
+#endif
+
+#if __STDC__
+void error (int, int, const char *, ...) \
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+#else
+void error ();
+#endif
+
+/* If non-zero, error will use the CVS protocol to report error
+ messages. This will only be set in the CVS server parent process;
+ most other code is run via do_cvs_command, which forks off a child
+ process and packages up its stderr in the protocol. */
+extern int error_use_protocol;
+
+#endif /* _error_h_ */
diff --git a/gnu/usr.bin/cvs/lib/filesubr.c b/gnu/usr.bin/cvs/lib/filesubr.c
new file mode 100644
index 000000000000..3a5269104266
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/filesubr.c
@@ -0,0 +1,640 @@
+/* filesubr.c --- subroutines for dealing with files
+ Jim Blandy <jimb@cyclic.com>
+
+ This file is part of GNU CVS.
+
+ GNU CVS is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* These functions were moved out of subr.c because they need different
+ definitions under operating systems (like, say, Windows NT) with different
+ file system semantics. */
+
+#include "cvs.h"
+
+#ifndef lint
+static const char rcsid[] = "$CVSid:$";
+USE(rcsid);
+#endif
+
+/*
+ * I don't know of a convenient way to test this at configure time, or else
+ * I'd certainly do it there.
+ */
+#if defined(NeXT)
+#define LOSING_TMPNAM_FUNCTION
+#endif
+
+static int deep_remove_dir PROTO((const char *path));
+
+/*
+ * Copies "from" to "to".
+ */
+void
+copy_file (from, to)
+ const char *from;
+ const char *to;
+{
+ struct stat sb;
+ struct utimbuf t;
+ int fdin, fdout;
+
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
+ (server_active) ? 'S' : ' ', from, to);
+#else
+ (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
+#endif
+ if (noexec)
+ return;
+
+ if ((fdin = open (from, O_RDONLY)) < 0)
+ error (1, errno, "cannot open %s for copying", from);
+ if (fstat (fdin, &sb) < 0)
+ error (1, errno, "cannot fstat %s", from);
+ if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
+ error (1, errno, "cannot create %s for copying", to);
+ if (sb.st_size > 0)
+ {
+ char buf[BUFSIZ];
+ int n;
+
+ for (;;)
+ {
+ n = read (fdin, buf, sizeof(buf));
+ if (n == -1)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ error (1, errno, "cannot read file %s for copying", from);
+ }
+ else if (n == 0)
+ break;
+
+ if (write(fdout, buf, n) != n) {
+ error (1, errno, "cannot write file %s for copying", to);
+ }
+ }
+
+#ifdef HAVE_FSYNC
+ if (fsync (fdout))
+ error (1, errno, "cannot fsync file %s after copying", to);
+#endif
+ }
+
+ if (close (fdin) < 0)
+ error (0, errno, "cannot close %s", from);
+ if (close (fdout) < 0)
+ error (1, errno, "cannot close %s", to);
+
+ /* now, set the times for the copied file to match those of the original */
+ memset ((char *) &t, 0, sizeof (t));
+ t.actime = sb.st_atime;
+ t.modtime = sb.st_mtime;
+ (void) utime (to, &t);
+}
+
+/* FIXME-krp: these functions would benefit from caching the char * &
+ stat buf. */
+
+/*
+ * Returns non-zero if the argument file is a directory, or is a symbolic
+ * link which points to a directory.
+ */
+int
+isdir (file)
+ const char *file;
+{
+ struct stat sb;
+
+ if (stat (file, &sb) < 0)
+ return (0);
+ return (S_ISDIR (sb.st_mode));
+}
+
+/*
+ * Returns non-zero if the argument file is a symbolic link.
+ */
+int
+islink (file)
+ const char *file;
+{
+#ifdef S_ISLNK
+ struct stat sb;
+
+ if (lstat (file, &sb) < 0)
+ return (0);
+ return (S_ISLNK (sb.st_mode));
+#else
+ return (0);
+#endif
+}
+
+/*
+ * Returns non-zero if the argument file exists.
+ */
+int
+isfile (file)
+ const char *file;
+{
+ return isaccessible(file, F_OK);
+}
+
+/*
+ * Returns non-zero if the argument file is readable.
+ */
+int
+isreadable (file)
+ const char *file;
+{
+ return isaccessible(file, R_OK);
+}
+
+/*
+ * Returns non-zero if the argument file is writable.
+ */
+int
+iswritable (file)
+ const char *file;
+{
+ return isaccessible(file, W_OK);
+}
+
+/*
+ * Returns non-zero if the argument file is accessable according to
+ * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
+ * bits set.
+ */
+int
+isaccessible (file, mode)
+ const char *file;
+ const int mode;
+{
+#ifdef SETXID_SUPPORT
+ struct stat sb;
+ int umask = 0;
+ int gmask = 0;
+ int omask = 0;
+ int uid;
+
+ if (stat(file, &sb) == -1)
+ return 0;
+ if (mode == F_OK)
+ return 1;
+
+ uid = geteuid();
+ if (uid == 0) /* superuser */
+ {
+ if (mode & X_OK)
+ return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
+ else
+ return 1;
+ }
+
+ if (mode & R_OK)
+ {
+ umask |= S_IRUSR;
+ gmask |= S_IRGRP;
+ omask |= S_IROTH;
+ }
+ if (mode & W_OK)
+ {
+ umask |= S_IWUSR;
+ gmask |= S_IWGRP;
+ omask |= S_IWOTH;
+ }
+ if (mode & X_OK)
+ {
+ umask |= S_IXUSR;
+ gmask |= S_IXGRP;
+ omask |= S_IXOTH;
+ }
+
+ if (sb.st_uid == uid)
+ return (sb.st_mode & umask) == umask;
+ else if (sb.st_gid == getegid())
+ return (sb.st_mode & gmask) == gmask;
+ else
+ return (sb.st_mode & omask) == omask;
+#else
+ return access(file, mode) == 0;
+#endif
+}
+
+/*
+ * Open a file and die if it fails
+ */
+FILE *
+open_file (name, mode)
+ const char *name;
+ const char *mode;
+{
+ FILE *fp;
+
+ if ((fp = fopen (name, mode)) == NULL)
+ error (1, errno, "cannot open %s", name);
+ return (fp);
+}
+
+/*
+ * Make a directory and die if it fails
+ */
+void
+make_directory (name)
+ const char *name;
+{
+ struct stat sb;
+
+ if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
+ error (0, 0, "%s already exists but is not a directory", name);
+ if (!noexec && mkdir (name, 0777) < 0)
+ error (1, errno, "cannot make directory %s", name);
+}
+
+/*
+ * Make a path to the argument directory, printing a message if something
+ * goes wrong.
+ */
+void
+make_directories (name)
+ const char *name;
+{
+ char *cp;
+
+ if (noexec)
+ return;
+
+ if (mkdir (name, 0777) == 0 || errno == EEXIST)
+ return;
+ if (! existence_error (errno))
+ {
+ error (0, errno, "cannot make path to %s", name);
+ return;
+ }
+ if ((cp = strrchr (name, '/')) == NULL)
+ return;
+ *cp = '\0';
+ make_directories (name);
+ *cp++ = '/';
+ if (*cp == '\0')
+ return;
+ (void) mkdir (name, 0777);
+}
+
+/*
+ * Change the mode of a file, either adding write permissions, or removing
+ * all write permissions. Either change honors the current umask setting.
+ */
+void
+xchmod (fname, writable)
+ char *fname;
+ int writable;
+{
+ struct stat sb;
+ mode_t mode, oumask;
+
+ if (stat (fname, &sb) < 0)
+ {
+ if (!noexec)
+ error (0, errno, "cannot stat %s", fname);
+ return;
+ }
+ oumask = umask (0);
+ (void) umask (oumask);
+ if (writable)
+ {
+ mode = sb.st_mode | (~oumask
+ & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
+ | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
+ | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
+ }
+ else
+ {
+ mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
+ }
+
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
+ (server_active) ? 'S' : ' ', fname, mode);
+#else
+ (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
+#endif
+ if (noexec)
+ return;
+
+ if (chmod (fname, mode) < 0)
+ error (0, errno, "cannot change mode of file %s", fname);
+}
+
+/*
+ * Rename a file and die if it fails
+ */
+void
+rename_file (from, to)
+ const char *from;
+ const char *to;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
+ (server_active) ? 'S' : ' ', from, to);
+#else
+ (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
+#endif
+ if (noexec)
+ return;
+
+ if (rename (from, to) < 0)
+ error (1, errno, "cannot rename file %s to %s", from, to);
+}
+
+/*
+ * link a file, if possible.
+ */
+int
+link_file (from, to)
+ const char *from;
+ const char *to;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> link(%s,%s)\n",
+ (server_active) ? 'S' : ' ', from, to);
+#else
+ (void) fprintf (stderr, "-> link(%s,%s)\n", from, to);
+#endif
+ if (noexec)
+ return (0);
+
+ return (link (from, to));
+}
+
+/*
+ * unlink a file, if possible.
+ */
+int
+unlink_file (f)
+ const char *f;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> unlink(%s)\n",
+ (server_active) ? 'S' : ' ', f);
+#else
+ (void) fprintf (stderr, "-> unlink(%s)\n", f);
+#endif
+ if (noexec)
+ return (0);
+
+ return (unlink (f));
+}
+
+/*
+ * Unlink a file or dir, if possible. If it is a directory do a deep
+ * removal of all of the files in the directory. Return -1 on error
+ * (in which case errno is set).
+ */
+int
+unlink_file_dir (f)
+ const char *f;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
+ (server_active) ? 'S' : ' ', f);
+#else
+ (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
+#endif
+ if (noexec)
+ return (0);
+
+ if (unlink (f) != 0)
+ {
+ /* under NEXTSTEP errno is set to return EPERM if
+ * the file is a directory,or if the user is not
+ * allowed to read or write to the file.
+ * [This is probably a bug in the O/S]
+ * other systems will return EISDIR to indicate
+ * that the path is a directory.
+ */
+ if (errno == EISDIR || errno == EPERM)
+ return deep_remove_dir (f);
+ else
+ /* The file wasn't a directory and some other
+ * error occured
+ */
+ return -1;
+ }
+ /* We were able to remove the file from the disk */
+ return 0;
+}
+
+/* Remove a directory and everything it contains. Returns 0 for
+ * success, -1 for failure (in which case errno is set).
+ */
+
+static int
+deep_remove_dir (path)
+ const char *path;
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char buf[PATH_MAX];
+
+ if ( rmdir (path) != 0 && errno == ENOTEMPTY )
+ {
+ if ((dirp = opendir (path)) == NULL)
+ /* If unable to open the directory return
+ * an error
+ */
+ return -1;
+
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 ||
+ strcmp (dp->d_name, "..") == 0)
+ continue;
+
+ sprintf (buf, "%s/%s", path, dp->d_name);
+
+ if (unlink (buf) != 0 )
+ {
+ if (errno == EISDIR || errno == EPERM)
+ {
+ if (deep_remove_dir (buf))
+ {
+ closedir (dirp);
+ return -1;
+ }
+ }
+ else
+ {
+ /* buf isn't a directory, or there are
+ * some sort of permision problems
+ */
+ closedir (dirp);
+ return -1;
+ }
+ }
+ }
+ closedir (dirp);
+ return rmdir (path);
+ }
+ /* Was able to remove the directory return 0 */
+ return 0;
+}
+
+/* Read NCHARS bytes from descriptor FD into BUF.
+ Return the number of characters successfully read.
+ The number returned is always NCHARS unless end-of-file or error. */
+static size_t
+block_read (fd, buf, nchars)
+ int fd;
+ char *buf;
+ size_t nchars;
+{
+ char *bp = buf;
+ size_t nread;
+
+ do
+ {
+ nread = read (fd, bp, nchars);
+ if (nread == (size_t)-1)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ return (size_t)-1;
+ }
+
+ if (nread == 0)
+ break;
+
+ bp += nread;
+ nchars -= nread;
+ } while (nchars != 0);
+
+ return bp - buf;
+}
+
+
+/*
+ * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
+ */
+int
+xcmp (file1, file2)
+ const char *file1;
+ const char *file2;
+{
+ char *buf1, *buf2;
+ struct stat sb1, sb2;
+ int fd1, fd2;
+ int ret;
+
+ if ((fd1 = open (file1, O_RDONLY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file1);
+ if ((fd2 = open (file2, O_RDONLY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file2);
+ if (fstat (fd1, &sb1) < 0)
+ error (1, errno, "cannot fstat %s", file1);
+ if (fstat (fd2, &sb2) < 0)
+ error (1, errno, "cannot fstat %s", file2);
+
+ /* A generic file compare routine might compare st_dev & st_ino here
+ to see if the two files being compared are actually the same file.
+ But that won't happen in CVS, so we won't bother. */
+
+ if (sb1.st_size != sb2.st_size)
+ ret = 1;
+ else if (sb1.st_size == 0)
+ ret = 0;
+ else
+ {
+ /* FIXME: compute the optimal buffer size by computing the least
+ common multiple of the files st_blocks field */
+ size_t buf_size = 8 * 1024;
+ size_t read1;
+ size_t read2;
+
+ buf1 = xmalloc (buf_size);
+ buf2 = xmalloc (buf_size);
+
+ do
+ {
+ read1 = block_read (fd1, buf1, buf_size);
+ if (read1 == (size_t)-1)
+ error (1, errno, "cannot read file %s for comparing", file1);
+
+ read2 = block_read (fd2, buf2, buf_size);
+ if (read2 == (size_t)-1)
+ error (1, errno, "cannot read file %s for comparing", file2);
+
+ /* assert (read1 == read2); */
+
+ ret = memcmp(buf1, buf2, read1);
+ } while (ret == 0 && read1 == buf_size);
+
+ free (buf1);
+ free (buf2);
+ }
+
+ (void) close (fd1);
+ (void) close (fd2);
+ return (ret);
+}
+
+#ifdef LOSING_TMPNAM_FUNCTION
+char *tmpnam(char *s)
+{
+ static char value[L_tmpnam+1];
+
+ if (s){
+ strcpy(s,"/tmp/cvsXXXXXX");
+ mktemp(s);
+ return s;
+ }else{
+ strcpy(value,"/tmp/cvsXXXXXX");
+ mktemp(s);
+ return value;
+ }
+}
+#endif
+
+/* Return non-zero iff FILENAME is absolute.
+ Trivial under Unix, but more complicated under other systems. */
+int
+isabsolute (filename)
+ const char *filename;
+{
+ return filename[0] == '/';
+}
+
+
+/* Return a pointer into PATH's last component. */
+char *
+last_component (path)
+ char *path;
+{
+ char *last = strrchr (path, '/');
+
+ if (last)
+ return last + 1;
+ else
+ return path;
+}
diff --git a/gnu/usr.bin/cvs/lib/getline.c b/gnu/usr.bin/cvs/lib/getline.c
new file mode 100644
index 000000000000..c69946148f69
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getline.c
@@ -0,0 +1,126 @@
+/* getline.c -- Replacement for GNU C library function getline
+
+Copyright (C) 1993 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#define NDEBUG
+#include <assert.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc (), *realloc ();
+#endif
+
+/* Always add at least this many bytes when extending the buffer. */
+#define MIN_CHUNK 64
+
+/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
+ + OFFSET (and null-terminate it). *LINEPTR is a pointer returned from
+ malloc (or NULL), pointing to *N characters of space. It is realloc'd
+ as necessary. Return the number of characters read (not including the
+ null terminator), or -1 on error or EOF. */
+
+int
+getstr (lineptr, n, stream, terminator, offset)
+ char **lineptr;
+ size_t *n;
+ FILE *stream;
+ char terminator;
+ int offset;
+{
+ int nchars_avail; /* Allocated but unused chars in *LINEPTR. */
+ char *read_pos; /* Where we're reading into *LINEPTR. */
+ int ret;
+
+ if (!lineptr || !n || !stream)
+ return -1;
+
+ if (!*lineptr)
+ {
+ *n = MIN_CHUNK;
+ *lineptr = malloc (*n);
+ if (!*lineptr)
+ return -1;
+ }
+
+ nchars_avail = *n - offset;
+ read_pos = *lineptr + offset;
+
+ for (;;)
+ {
+ register int c = getc (stream);
+
+ /* We always want at least one char left in the buffer, since we
+ always (unless we get an error while reading the first char)
+ NUL-terminate the line buffer. */
+
+ assert(*n - nchars_avail == read_pos - *lineptr);
+ if (nchars_avail < 2)
+ {
+ if (*n > MIN_CHUNK)
+ *n *= 2;
+ else
+ *n += MIN_CHUNK;
+
+ nchars_avail = *n + *lineptr - read_pos;
+ *lineptr = realloc (*lineptr, *n);
+ if (!*lineptr)
+ return -1;
+ read_pos = *n - nchars_avail + *lineptr;
+ assert(*n - nchars_avail == read_pos - *lineptr);
+ }
+
+ if (c == EOF || ferror (stream))
+ {
+ /* Return partial line, if any. */
+ if (read_pos == *lineptr)
+ return -1;
+ else
+ break;
+ }
+
+ *read_pos++ = c;
+ nchars_avail--;
+
+ if (c == terminator)
+ /* Return the line. */
+ break;
+ }
+
+ /* Done - NUL terminate and return the number of chars read. */
+ *read_pos = '\0';
+
+ ret = read_pos - (*lineptr + offset);
+ return ret;
+}
+
+int
+getline (lineptr, n, stream)
+ char **lineptr;
+ size_t *n;
+ FILE *stream;
+{
+ return getstr (lineptr, n, stream, '\n', 0);
+}
diff --git a/gnu/usr.bin/cvs/lib/getline.h b/gnu/usr.bin/cvs/lib/getline.h
new file mode 100644
index 000000000000..30bcc258373d
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getline.h
@@ -0,0 +1,15 @@
+#ifndef _getline_h_
+#define _getline_h_ 1
+
+#include <stdio.h>
+
+#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#define __PROTO(args) args
+#else
+#define __PROTO(args) ()
+#endif /* GCC. */
+
+int
+ getline __PROTO ((char **_lineptr, size_t *_n, FILE *_stream));
+
+#endif /* _getline_h_ */
diff --git a/gnu/usr.bin/cvs/lib/run.c b/gnu/usr.bin/cvs/lib/run.c
new file mode 100644
index 000000000000..6a06a385e04b
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/run.c
@@ -0,0 +1,533 @@
+/* run.c --- routines for executing subprocesses.
+
+ This file is part of GNU CVS.
+
+ GNU CVS is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "cvs.h"
+
+#ifdef HAVE_VPRINTF
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif
+#else
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif
+
+static void run_add_arg PROTO((const char *s));
+static void run_init_prog PROTO((void));
+
+extern char *strtok ();
+
+/*
+ * To exec a program under CVS, first call run_setup() to setup any initial
+ * arguments. The options to run_setup are essentially like printf(). The
+ * arguments will be parsed into whitespace separated words and added to the
+ * global run_argv list.
+ *
+ * Then, optionally call run_arg() for each additional argument that you'd like
+ * to pass to the executed program.
+ *
+ * Finally, call run_exec() to execute the program with the specified arguments.
+ * The execvp() syscall will be used, so that the PATH is searched correctly.
+ * File redirections can be performed in the call to run_exec().
+ */
+static char *run_prog;
+static char **run_argv;
+static int run_argc;
+static int run_argc_allocated;
+
+/* VARARGS */
+#if defined (HAVE_VPRINTF) && (defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__))
+void
+run_setup (const char *fmt,...)
+#else
+void
+run_setup (fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif
+ char *cp;
+ int i;
+
+ run_init_prog ();
+
+ /* clean out any malloc'ed values from run_argv */
+ for (i = 0; i < run_argc; i++)
+ {
+ if (run_argv[i])
+ {
+ free (run_argv[i]);
+ run_argv[i] = (char *) 0;
+ }
+ }
+ run_argc = 0;
+
+ /* process the varargs into run_prog */
+#ifdef HAVE_VPRINTF
+ VA_START (args, fmt);
+ (void) vsprintf (run_prog, fmt, args);
+ va_end (args);
+#else
+ (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+ /* put each word into run_argv, allocating it as we go */
+ for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
+ run_add_arg (cp);
+}
+
+void
+run_arg (s)
+ const char *s;
+{
+ run_add_arg (s);
+}
+
+/* VARARGS */
+#if defined (HAVE_VPRINTF) && (defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__))
+void
+run_args (const char *fmt,...)
+#else
+void
+run_args (fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif
+
+ run_init_prog ();
+
+ /* process the varargs into run_prog */
+#ifdef HAVE_VPRINTF
+ VA_START (args, fmt);
+ (void) vsprintf (run_prog, fmt, args);
+ va_end (args);
+#else
+ (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+ /* and add the (single) argument to the run_argv list */
+ run_add_arg (run_prog);
+}
+
+static void
+run_add_arg (s)
+ const char *s;
+{
+ /* allocate more argv entries if we've run out */
+ if (run_argc >= run_argc_allocated)
+ {
+ run_argc_allocated += 50;
+ run_argv = (char **) xrealloc ((char *) run_argv,
+ run_argc_allocated * sizeof (char **));
+ }
+
+ if (s)
+ run_argv[run_argc++] = xstrdup (s);
+ else
+ run_argv[run_argc] = (char *) 0; /* not post-incremented on purpose! */
+}
+
+static void
+run_init_prog ()
+{
+ /* make sure that run_prog is allocated once */
+ if (run_prog == (char *) 0)
+ run_prog = xmalloc (10 * 1024); /* 10K of args for _setup and _arg */
+}
+
+int
+run_exec (stin, stout, sterr, flags)
+ char *stin;
+ char *stout;
+ char *sterr;
+ int flags;
+{
+ int shin, shout, sherr;
+ int mode_out, mode_err;
+ int status;
+ int rc = -1;
+ int rerrno = 0;
+ int pid, w;
+
+#ifdef POSIX_SIGNALS
+ sigset_t sigset_mask, sigset_omask;
+ struct sigaction act, iact, qact;
+
+#else
+#ifdef BSD_SIGNALS
+ int mask;
+ struct sigvec vec, ivec, qvec;
+
+#else
+ RETSIGTYPE (*istat) (), (*qstat) ();
+#endif
+#endif
+
+ if (trace)
+ {
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> system(", (server_active) ? 'S' : ' ');
+#else
+ (void) fprintf (stderr, "-> system(");
+#endif
+ run_print (stderr);
+ (void) fprintf (stderr, ")\n");
+ }
+ if (noexec && (flags & RUN_REALLY) == 0)
+ return (0);
+
+ /* make sure that we are null terminated, since we didn't calloc */
+ run_add_arg ((char *) 0);
+
+ /* setup default file descriptor numbers */
+ shin = 0;
+ shout = 1;
+ sherr = 2;
+
+ /* set the file modes for stdout and stderr */
+ mode_out = mode_err = O_WRONLY | O_CREAT;
+ mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
+ mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
+
+ if (stin && (shin = open (stin, O_RDONLY)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for reading (prog %s)",
+ stin, run_argv[0]);
+ goto out0;
+ }
+ if (stout && (shout = open (stout, mode_out, 0666)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for writing (prog %s)",
+ stout, run_argv[0]);
+ goto out1;
+ }
+ if (sterr && (flags & RUN_COMBINED) == 0)
+ {
+ if ((sherr = open (sterr, mode_err, 0666)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for writing (prog %s)",
+ sterr, run_argv[0]);
+ goto out2;
+ }
+ }
+
+ /* Make sure we don't flush this twice, once in the subprocess. */
+ fflush (stdout);
+ fflush (stderr);
+
+ /* The output files, if any, are now created. Do the fork and dups */
+#ifdef HAVE_VFORK
+ pid = vfork ();
+#else
+ pid = fork ();
+#endif
+ if (pid == 0)
+ {
+ if (shin != 0)
+ {
+ (void) dup2 (shin, 0);
+ (void) close (shin);
+ }
+ if (shout != 1)
+ {
+ (void) dup2 (shout, 1);
+ (void) close (shout);
+ }
+ if (flags & RUN_COMBINED)
+ (void) dup2 (1, 2);
+ else if (sherr != 2)
+ {
+ (void) dup2 (sherr, 2);
+ (void) close (sherr);
+ }
+
+ /* dup'ing is done. try to run it now */
+ (void) execvp (run_argv[0], run_argv);
+ error (0, errno, "cannot exec %s", run_argv[0]);
+ _exit (127);
+ }
+ else if (pid == -1)
+ {
+ rerrno = errno;
+ goto out;
+ }
+
+ /* the parent. Ignore some signals for now */
+#ifdef POSIX_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ act.sa_handler = SIG_IGN;
+ (void) sigemptyset (&act.sa_mask);
+ act.sa_flags = 0;
+ (void) sigaction (SIGINT, &act, &iact);
+ (void) sigaction (SIGQUIT, &act, &qact);
+ }
+ else
+ {
+ (void) sigemptyset (&sigset_mask);
+ (void) sigaddset (&sigset_mask, SIGINT);
+ (void) sigaddset (&sigset_mask, SIGQUIT);
+ (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
+ }
+#else
+#ifdef BSD_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ memset ((char *) &vec, 0, sizeof (vec));
+ vec.sv_handler = SIG_IGN;
+ (void) sigvec (SIGINT, &vec, &ivec);
+ (void) sigvec (SIGQUIT, &vec, &qvec);
+ }
+ else
+ mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
+#else
+ istat = signal (SIGINT, SIG_IGN);
+ qstat = signal (SIGQUIT, SIG_IGN);
+#endif
+#endif
+
+ /* wait for our process to die and munge return status */
+#ifdef POSIX_SIGNALS
+ while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+ ;
+#else
+ while ((w = wait (&status)) != pid)
+ {
+ if (w == -1 && errno != EINTR)
+ break;
+ }
+#endif
+ if (w == -1)
+ {
+ rc = -1;
+ rerrno = errno;
+ }
+ else if (WIFEXITED (status))
+ rc = WEXITSTATUS (status);
+ else if (WIFSIGNALED (status))
+ {
+ if (WTERMSIG (status) == SIGPIPE)
+ error (1, 0, "broken pipe");
+ rc = 2;
+ }
+ else
+ rc = 1;
+
+ /* restore the signals */
+#ifdef POSIX_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ (void) sigaction (SIGINT, &iact, (struct sigaction *) NULL);
+ (void) sigaction (SIGQUIT, &qact, (struct sigaction *) NULL);
+ }
+ else
+ (void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *) NULL);
+#else
+#ifdef BSD_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ (void) sigvec (SIGINT, &ivec, (struct sigvec *) NULL);
+ (void) sigvec (SIGQUIT, &qvec, (struct sigvec *) NULL);
+ }
+ else
+ (void) sigsetmask (mask);
+#else
+ (void) signal (SIGINT, istat);
+ (void) signal (SIGQUIT, qstat);
+#endif
+#endif
+
+ /* cleanup the open file descriptors */
+ out:
+ if (sterr)
+ (void) close (sherr);
+ out2:
+ if (stout)
+ (void) close (shout);
+ out1:
+ if (stin)
+ (void) close (shin);
+
+ out0:
+ if (rerrno)
+ errno = rerrno;
+ return (rc);
+}
+
+void
+run_print (fp)
+ FILE *fp;
+{
+ int i;
+
+ for (i = 0; i < run_argc; i++)
+ {
+ (void) fprintf (fp, "'%s'", run_argv[i]);
+ if (i != run_argc - 1)
+ (void) fprintf (fp, " ");
+ }
+}
+
+FILE *
+Popen (cmd, mode)
+ const char *cmd;
+ const char *mode;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> Popen(%s,%s)\n",
+ (server_active) ? 'S' : ' ', cmd, mode);
+#else
+ (void) fprintf (stderr, "-> Popen(%s,%s)\n", cmd, mode);
+#endif
+ if (noexec)
+ return (NULL);
+
+ return (popen (cmd, mode));
+}
+
+extern int evecvp PROTO((char *file, char **argv));
+
+int
+piped_child (command, tofdp, fromfdp)
+ char **command;
+ int *tofdp;
+ int *fromfdp;
+{
+ int pid;
+ int to_child_pipe[2];
+ int from_child_pipe[2];
+
+ if (pipe (to_child_pipe) < 0)
+ error (1, errno, "cannot create pipe");
+ if (pipe (from_child_pipe) < 0)
+ error (1, errno, "cannot create pipe");
+
+ pid = fork ();
+ if (pid < 0)
+ error (1, errno, "cannot fork");
+ if (pid == 0)
+ {
+ if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0)
+ error (1, errno, "cannot dup2");
+ if (close (to_child_pipe[1]) < 0)
+ error (1, errno, "cannot close");
+ if (close (from_child_pipe[0]) < 0)
+ error (1, errno, "cannot close");
+ if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0)
+ error (1, errno, "cannot dup2");
+
+ execvp (command[0], command);
+ error (1, errno, "cannot exec");
+ }
+ if (close (to_child_pipe[0]) < 0)
+ error (1, errno, "cannot close");
+ if (close (from_child_pipe[1]) < 0)
+ error (1, errno, "cannot close");
+
+ *tofdp = to_child_pipe[1];
+ *fromfdp = from_child_pipe[0];
+ return pid;
+}
+
+
+void
+close_on_exec (fd)
+ int fd;
+{
+#if defined (FD_CLOEXEC) && defined (F_SETFD)
+ if (fcntl (fd, F_SETFD, 1))
+ error (1, errno, "can't set close-on-exec flag on %d", fd);
+#endif
+}
+
+/*
+ * dir = 0 : main proc writes to new proc, which writes to oldfd
+ * dir = 1 : main proc reads from new proc, which reads from oldfd
+ */
+
+int
+filter_stream_through_program (oldfd, dir, prog, pidp)
+ int oldfd, dir;
+ char **prog;
+ pid_t *pidp;
+{
+ int p[2], newfd;
+ pid_t newpid;
+
+ if (pipe (p))
+ error (1, errno, "cannot create pipe");
+ newpid = fork ();
+ if (pidp)
+ *pidp = newpid;
+ switch (newpid)
+ {
+ case -1:
+ error (1, errno, "cannot fork");
+ case 0:
+ /* child */
+ if (dir)
+ {
+ /* write to new pipe */
+ close (p[0]);
+ dup2 (oldfd, 0);
+ dup2 (p[1], 1);
+ }
+ else
+ {
+ /* read from new pipe */
+ close (p[1]);
+ dup2 (p[0], 0);
+ dup2 (oldfd, 1);
+ }
+ /* Should I be blocking some signals here? */
+ execvp (prog[0], prog);
+ error (1, errno, "couldn't exec %s", prog[0]);
+ default:
+ /* parent */
+ close (oldfd);
+ if (dir)
+ {
+ /* read from new pipe */
+ close (p[1]);
+ newfd = p[0];
+ }
+ else
+ {
+ /* write to new pipe */
+ close (p[0]);
+ newfd = p[1];
+ }
+ close_on_exec (newfd);
+ return newfd;
+ }
+}
diff --git a/gnu/usr.bin/cvs/lib/save-cwd.c b/gnu/usr.bin/cvs/lib/save-cwd.c
new file mode 100644
index 000000000000..1bdf79115100
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/save-cwd.c
@@ -0,0 +1,141 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+
+#ifdef HAVE_DIRECT_H
+# include <direct.h>
+#endif
+
+#ifdef HAVE_IO_H
+# include <io.h>
+#endif
+
+#include <errno.h>
+# ifndef errno
+extern int errno;
+#endif
+
+#include "save-cwd.h"
+#include "error.h"
+
+char *xgetwd __PROTO((void));
+
+/* Record the location of the current working directory in CWD so that
+ the program may change to other directories and later use restore_cwd
+ to return to the recorded location. This function may allocate
+ space using malloc (via xgetwd) or leave a file descriptor open;
+ use free_cwd to perform the necessary free or close. Upon failure,
+ no memory is allocated, any locally opened file descriptors are
+ closed; return non-zero -- in that case, free_cwd need not be
+ called, but doing so is ok. Otherwise, return zero. */
+
+int
+save_cwd (cwd)
+ struct saved_cwd *cwd;
+{
+ static int have_working_fchdir = 1;
+
+ cwd->desc = -1;
+ cwd->name = NULL;
+
+ if (have_working_fchdir)
+ {
+#ifdef HAVE_FCHDIR
+ cwd->desc = open (".", O_RDONLY);
+ if (cwd->desc < 0)
+ {
+ error (0, errno, "cannot open current directory");
+ return 1;
+ }
+
+# if __sun__ || sun
+ /* On SunOS 4, fchdir returns EINVAL if accounting is enabled,
+ so we have to fall back to chdir. */
+ if (fchdir (cwd->desc))
+ {
+ if (errno == EINVAL)
+ {
+ close (cwd->desc);
+ cwd->desc = -1;
+ have_working_fchdir = 0;
+ }
+ else
+ {
+ error (0, errno, "current directory");
+ close (cwd->desc);
+ cwd->desc = -1;
+ return 1;
+ }
+ }
+# endif /* __sun__ || sun */
+#else
+#define fchdir(x) (abort (), 0)
+ have_working_fchdir = 0;
+#endif
+ }
+
+ if (!have_working_fchdir)
+ {
+ cwd->name = xgetwd ();
+ if (cwd->name == NULL)
+ {
+ error (0, errno, "cannot get current directory");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Change to recorded location, CWD, in directory hierarchy.
+ If "saved working directory", NULL))
+ */
+
+int
+restore_cwd (cwd, dest)
+ const struct saved_cwd *cwd;
+ const char *dest;
+{
+ int fail = 0;
+ if (cwd->desc >= 0)
+ {
+ if (fchdir (cwd->desc))
+ {
+ error (0, errno, "cannot return to %s",
+ (dest ? dest : "saved working directory"));
+ fail = 1;
+ }
+ }
+ else if (chdir (cwd->name) < 0)
+ {
+ error (0, errno, "%s", cwd->name);
+ fail = 1;
+ }
+ return fail;
+}
+
+void
+free_cwd (cwd)
+ struct saved_cwd *cwd;
+{
+ if (cwd->desc >= 0)
+ close (cwd->desc);
+ if (cwd->name)
+ free (cwd->name);
+}
+
diff --git a/gnu/usr.bin/cvs/lib/save-cwd.h b/gnu/usr.bin/cvs/lib/save-cwd.h
new file mode 100644
index 000000000000..f9802f8ca2fa
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/save-cwd.h
@@ -0,0 +1,20 @@
+#ifndef SAVE_CWD_H
+#define SAVE_CWD_H 1
+
+struct saved_cwd
+ {
+ int desc;
+ char *name;
+ };
+
+#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#define __PROTO(args) args
+#else
+#define __PROTO(args) ()
+#endif /* GCC. */
+
+int save_cwd __PROTO((struct saved_cwd *cwd));
+int restore_cwd __PROTO((const struct saved_cwd *cwd, const char *dest));
+void free_cwd __PROTO((struct saved_cwd *cwd));
+
+#endif /* SAVE_CWD_H */
diff --git a/gnu/usr.bin/cvs/lib/xgetwd.c b/gnu/usr.bin/cvs/lib/xgetwd.c
new file mode 100644
index 000000000000..8fe4ec18c2ef
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/xgetwd.c
@@ -0,0 +1,79 @@
+/* xgetwd.c -- return current directory with unlimited length
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Derived from xgetcwd.c in e.g. the GNU sh-utils. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "system.h"
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+#include <sys/types.h>
+
+#ifndef HAVE_GETWD
+char *getwd ();
+#define GETWD(buf, max) getwd (buf)
+#else
+char *getcwd ();
+#define GETWD(buf, max) getcwd (buf, max)
+#endif
+
+/* Amount by which to increase buffer size when allocating more space. */
+#define PATH_INCR 32
+
+char *xmalloc ();
+char *xrealloc ();
+
+/* Return the current directory, newly allocated, arbitrarily long.
+ Return NULL and set errno on error. */
+
+char *
+xgetwd ()
+{
+ char *cwd;
+ char *ret;
+ unsigned path_max;
+
+ errno = 0;
+ path_max = (unsigned) PATH_MAX;
+ path_max += 2; /* The getcwd docs say to do this. */
+
+ cwd = xmalloc (path_max);
+
+ errno = 0;
+ while ((ret = GETWD (cwd, path_max)) == NULL && errno == ERANGE)
+ {
+ path_max += PATH_INCR;
+ cwd = xrealloc (cwd, path_max);
+ errno = 0;
+ }
+
+ if (ret == NULL)
+ {
+ int save_errno = errno;
+ free (cwd);
+ errno = save_errno;
+ return NULL;
+ }
+ return cwd;
+}