aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJordan K. Hubbard <jkh@FreeBSD.org>1993-06-18 05:46:17 +0000
committerJordan K. Hubbard <jkh@FreeBSD.org>1993-06-18 05:46:17 +0000
commitdb4427d33494e4695dbc1f5d90e612dbd28a045a (patch)
tree3595c61e4f85a1d3c1fb6ea6c1ede9ea8397ced7
downloadsrc-db4427d33494e4695dbc1f5d90e612dbd28a045a.tar.gz
src-db4427d33494e4695dbc1f5d90e612dbd28a045a.zip
Notes
Notes: svn path=/vendor/cvs/dist/; revision=12 svn path=/vendor/cvs/1.3/; revision=14; tag=vendor/misc-GNU/cvs/1.3
-rw-r--r--gnu/usr.bin/cvs/Makefile3
-rw-r--r--gnu/usr.bin/cvs/contrib/README68
-rw-r--r--gnu/usr.bin/cvs/contrib/cln_hist.pl91
-rw-r--r--gnu/usr.bin/cvs/contrib/commit_prep.pl168
-rw-r--r--gnu/usr.bin/cvs/contrib/cvs_acls.pl142
-rw-r--r--gnu/usr.bin/cvs/contrib/cvscheck84
-rw-r--r--gnu/usr.bin/cvs/contrib/cvscheck.man53
-rw-r--r--gnu/usr.bin/cvs/contrib/cvshelp.man562
-rw-r--r--gnu/usr.bin/cvs/contrib/descend116
-rw-r--r--gnu/usr.bin/cvs/contrib/descend.man115
-rw-r--r--gnu/usr.bin/cvs/contrib/dirfns481
-rw-r--r--gnu/usr.bin/cvs/contrib/log.pl104
-rw-r--r--gnu/usr.bin/cvs/contrib/log_accum.pl331
-rw-r--r--gnu/usr.bin/cvs/contrib/mfpipe.pl87
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog119
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL83
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile78
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/README14
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el52
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el884
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el298
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el386
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el89
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el6
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el1476
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info1367
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo1437
-rw-r--r--gnu/usr.bin/cvs/contrib/rcs-to-cvs208
-rw-r--r--gnu/usr.bin/cvs/contrib/rcslock.pl234
-rw-r--r--gnu/usr.bin/cvs/contrib/sccs2rcs277
-rw-r--r--gnu/usr.bin/cvs/cvs/Makefile18
-rw-r--r--gnu/usr.bin/cvs/cvs/add.c447
-rw-r--r--gnu/usr.bin/cvs/cvs/admin.c124
-rw-r--r--gnu/usr.bin/cvs/cvs/checkin.c135
-rw-r--r--gnu/usr.bin/cvs/cvs/checkout.c718
-rw-r--r--gnu/usr.bin/cvs/cvs/classify.c380
-rw-r--r--gnu/usr.bin/cvs/cvs/commit.c1229
-rw-r--r--gnu/usr.bin/cvs/cvs/config.h217
-rw-r--r--gnu/usr.bin/cvs/cvs/create_adm.c100
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.11991
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.5326
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.h438
-rw-r--r--gnu/usr.bin/cvs/cvs/diff.c407
-rw-r--r--gnu/usr.bin/cvs/cvs/entries.c488
-rw-r--r--gnu/usr.bin/cvs/cvs/find_names.c272
-rw-r--r--gnu/usr.bin/cvs/cvs/history.c1373
-rw-r--r--gnu/usr.bin/cvs/cvs/ignore.c227
-rw-r--r--gnu/usr.bin/cvs/cvs/import.c974
-rw-r--r--gnu/usr.bin/cvs/cvs/lock.c522
-rw-r--r--gnu/usr.bin/cvs/cvs/log.c132
-rw-r--r--gnu/usr.bin/cvs/cvs/logmsg.c449
-rw-r--r--gnu/usr.bin/cvs/cvs/main.c444
-rw-r--r--gnu/usr.bin/cvs/cvs/modules.c810
-rw-r--r--gnu/usr.bin/cvs/cvs/no_diff.c85
-rw-r--r--gnu/usr.bin/cvs/cvs/parseinfo.c147
-rw-r--r--gnu/usr.bin/cvs/cvs/patch.c523
-rw-r--r--gnu/usr.bin/cvs/cvs/patchlevel.h1
-rw-r--r--gnu/usr.bin/cvs/cvs/rcs.c1449
-rw-r--r--gnu/usr.bin/cvs/cvs/rcs.h102
-rw-r--r--gnu/usr.bin/cvs/cvs/recurse.c535
-rw-r--r--gnu/usr.bin/cvs/cvs/release.c219
-rw-r--r--gnu/usr.bin/cvs/cvs/remove.c176
-rw-r--r--gnu/usr.bin/cvs/cvs/repos.c169
-rw-r--r--gnu/usr.bin/cvs/cvs/rtag.c403
-rw-r--r--gnu/usr.bin/cvs/cvs/status.c230
-rw-r--r--gnu/usr.bin/cvs/cvs/tag.c263
-rw-r--r--gnu/usr.bin/cvs/cvs/update.c1028
-rw-r--r--gnu/usr.bin/cvs/cvs/vers_ts.c224
-rw-r--r--gnu/usr.bin/cvs/cvs/version.c11
-rw-r--r--gnu/usr.bin/cvs/doc/cvs.ms1073
-rw-r--r--gnu/usr.bin/cvs/examples/commitinfo21
-rw-r--r--gnu/usr.bin/cvs/examples/editinfo30
-rw-r--r--gnu/usr.bin/cvs/examples/loginfo20
-rw-r--r--gnu/usr.bin/cvs/examples/modules566
-rw-r--r--gnu/usr.bin/cvs/examples/rcsinfo18
-rw-r--r--gnu/usr.bin/cvs/lib/Makefile8
-rw-r--r--gnu/usr.bin/cvs/lib/Makefile.in91
-rw-r--r--gnu/usr.bin/cvs/lib/alloca.c191
-rw-r--r--gnu/usr.bin/cvs/lib/argmatch.c83
-rw-r--r--gnu/usr.bin/cvs/lib/dup2.c36
-rw-r--r--gnu/usr.bin/cvs/lib/error.c193
-rw-r--r--gnu/usr.bin/cvs/lib/fnmatch.c183
-rw-r--r--gnu/usr.bin/cvs/lib/fnmatch.h45
-rw-r--r--gnu/usr.bin/cvs/lib/ftruncate.c72
-rw-r--r--gnu/usr.bin/cvs/lib/getdate.y889
-rw-r--r--gnu/usr.bin/cvs/lib/getopt.c604
-rw-r--r--gnu/usr.bin/cvs/lib/getopt.h102
-rw-r--r--gnu/usr.bin/cvs/lib/getopt1.c166
-rw-r--r--gnu/usr.bin/cvs/lib/getwd.c31
-rw-r--r--gnu/usr.bin/cvs/lib/hash.c338
-rw-r--r--gnu/usr.bin/cvs/lib/hash.h77
-rw-r--r--gnu/usr.bin/cvs/lib/mkdir.c125
-rw-r--r--gnu/usr.bin/cvs/lib/myndbm.c212
-rw-r--r--gnu/usr.bin/cvs/lib/myndbm.h44
-rw-r--r--gnu/usr.bin/cvs/lib/regex.c4867
-rw-r--r--gnu/usr.bin/cvs/lib/regex.h479
-rw-r--r--gnu/usr.bin/cvs/lib/rename.c68
-rw-r--r--gnu/usr.bin/cvs/lib/sighandle.c412
-rw-r--r--gnu/usr.bin/cvs/lib/strdup.c39
-rw-r--r--gnu/usr.bin/cvs/lib/strippath.c74
-rw-r--r--gnu/usr.bin/cvs/lib/stripslash.c35
-rw-r--r--gnu/usr.bin/cvs/lib/subr.c912
-rw-r--r--gnu/usr.bin/cvs/lib/system.h223
-rw-r--r--gnu/usr.bin/cvs/lib/wait.h29
-rw-r--r--gnu/usr.bin/cvs/lib/y.tab.h18
-rw-r--r--gnu/usr.bin/cvs/lib/yesno.c37
-rw-r--r--gnu/usr.bin/cvs/mkmodules/Makefile8
-rw-r--r--gnu/usr.bin/cvs/mkmodules/mkmodules.165
-rw-r--r--gnu/usr.bin/cvs/mkmodules/mkmodules.c395
-rw-r--r--gnu/usr.bin/cvs/mkmodules/xxx5320
110 files changed, 45398 insertions, 0 deletions
diff --git a/gnu/usr.bin/cvs/Makefile b/gnu/usr.bin/cvs/Makefile
new file mode 100644
index 000000000000..d75858bc1e59
--- /dev/null
+++ b/gnu/usr.bin/cvs/Makefile
@@ -0,0 +1,3 @@
+SUBDIR = lib cvs mkmodules
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/cvs/contrib/README b/gnu/usr.bin/cvs/contrib/README
new file mode 100644
index 000000000000..412be26745d9
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/README
@@ -0,0 +1,68 @@
+@(#)README 1.8 92/04/10
+
+This "contrib" directory is a place holder for code/scripts sent to
+me by contributors around the world. This READM file will be kept
+up-to-date from release to release. BUT, I must point out that these
+contributions are really, REALLY UNSSUPPORTED. In fact, I probably
+don't even know what they do. Nor do I guarantee to have tried them,
+or ported them to work with this CVS distribution. If you have questions,
+you might contact the author, but you should not necessarily expect
+a reply. USE AT YOUR OWN RISK -- and all that stuff.
+
+Contents of this directory:
+
+ README This file.
+ log.pl A perl script suitable for including in your
+ $CVSROOT/CVSROOT/loginfo file for logging commit
+ changes. Includes the RCS revision of the change
+ as part of the log.
+ Contributed by Kevin Samborn <samborn@sunrise.com>.
+ pcl-cvs A directory that contains GNU Emacs lisp code which
+ implements a CVS-mode for emacs.
+ Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+ commit_prep.pl A perl script, to be combined with log_accum.pl, to
+ log_accum.pl provide for a way to combine the individual log
+ messages of a multi-directory "commit" into a
+ single log message, and mail the result somewhere.
+ Also does other checks for $Id and that you are
+ committing the correct revision of the file.
+ Read the comments carefully.
+ Contributed by David Hampton <hampton@cisco.com>.
+ mfpipe.pl Another perl script for logging. Allows you to
+ pipe the log message to a file and/or send mail
+ to some alias.
+ Contributed by John Clyne <clyne@niwot.scd.ucar.edu>.
+ rcs-to-cvs Script to import sources that may have been under
+ RCS control already.
+ Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+ cvscheck Identifies files added, changed, or removed in a
+ cvscheck.man checked out CVS tree; also notices unknown files.
+ Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+ cvshelp.man An introductory manual page written by Lowell Skoog
+ <fluke!lowell@uunet.uu.net>. It is most likely
+ out-of-date relative to CVS 1.3, but still may be
+ useful.
+ dirfns A shar file which contains some code that might
+ help your system support opendir/readdir/closedir,
+ if it does not already.
+ Copied from the C-News distribution.
+ rcslock.pl A perl script that can be added to your commitinfo
+ file that tries to determine if your RCS file is
+ currently locked by someone else, as might be the
+ case for a binary file.
+ Contributed by John Rouillard <rouilj@cs.umb.edu>.
+ cvs_acls.pl A perl script that implements Access Control Lists
+ by using the "commitinfo" hook provided with the
+ "cvs commit" command.
+ Contributed by David G. Grubbs <dgg@ksr.com>.
+ descend A shell script that can be used to recursively
+ descend.man descend through a directory. In CVS 1.2, this was
+ very useful, since many of the commands were not
+ recursive. In CVS 1.3 (and later), however, most of
+ the commands are recursive. However, this may still
+ come in handy.
+ Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+ cln_hist.pl A perl script to compress your
+ $CVSROOT/CVSROOT/history file, as it can grow quite
+ large after extended use.
+ Contributed by David G. Grubbs <dgg@ksr.com>
diff --git a/gnu/usr.bin/cvs/contrib/cln_hist.pl b/gnu/usr.bin/cvs/contrib/cln_hist.pl
new file mode 100644
index 000000000000..b353edc7023e
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cln_hist.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/perl -- # -*-Perl-*-
+#
+# cln_hist.pl,v 1.1 1992/04/10 03:04:15 berliner Exp
+# Contributed by David G. Grubbs <dgg@ksr.com>
+#
+# Clean up the history file. 10 Record types: MAR OFT WUCG
+#
+# WUCG records are thrown out.
+# MAR records are retained.
+# T records: retain only last tag with same combined tag/module.
+#
+# Two passes: Walk through the first time and remember the
+# 1. Last Tag record with same "tag" and "module" names.
+# 2. Last O record with unique user/module/directory, unless followed
+# by a matching F record.
+#
+
+$r = $ENV{"CVSROOT"};
+$c = "$r/CVSROOT";
+$h = "$c/history";
+
+eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
+ while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
+exit 255 if $die; # process any variable=value switches
+
+%tags = ();
+%outs = ();
+
+#
+# Move history file to safe place and re-initialize a new one.
+#
+rename($h, "$h.bak");
+open(XX, ">$h");
+close(XX);
+
+#
+# Pass1 -- remember last tag and checkout.
+#
+open(HIST, "$h.bak");
+while (<HIST>) {
+ next if /^[MARWUCG]/;
+
+ # Save whole line keyed by tag|module
+ if (/^T/) {
+ @tmp = split(/\|/, $_);
+ $tags{$tmp[4] . '|' . $tmp[5]} = $_;
+ }
+ # Save whole line
+ if (/^[OF]/) {
+ @tmp = split(/\|/, $_);
+ $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} = $_;
+ }
+}
+
+#
+# Pass2 -- print out what we want to save.
+#
+open(SAVE, ">$h.work");
+open(HIST, "$h.bak");
+while (<HIST>) {
+ next if /^[FWUCG]/;
+
+ # If whole line matches saved (i.e. "last") one, print it.
+ if (/^T/) {
+ @tmp = split(/\|/, $_);
+ next if $tags{$tmp[4] . '|' . $tmp[5]} ne $_;
+ }
+ # Save whole line
+ if (/^O/) {
+ @tmp = split(/\|/, $_);
+ next if $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} ne $_;
+ }
+
+ print SAVE $_;
+}
+
+#
+# Put back the saved stuff
+#
+system "cat $h >> $h.work";
+
+if (-s $h) {
+ rename ($h, "$h.interim");
+ print "history.interim has non-zero size.\n";
+} else {
+ unlink($h);
+}
+
+rename ("$h.work", $h);
+
+exit(0);
diff --git a/gnu/usr.bin/cvs/contrib/commit_prep.pl b/gnu/usr.bin/cvs/contrib/commit_prep.pl
new file mode 100644
index 000000000000..b3f7e9aa9f96
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/commit_prep.pl
@@ -0,0 +1,168 @@
+#!/usr/local/bin/perl -w
+#
+#
+# Perl filter to handle pre-commit checking of files. This program
+# records the last directory where commits will be taking place for
+# use by the log_accumulate script. For new file, it forcing the
+# existence of a RCS "Id" keyword in the first ten lines of the file.
+# For existing files, it checks version number in the "Id" line to
+# prevent losing changes because an old version of a file was copied
+# into the direcory.
+#
+# Possible future enhancements:
+#
+#
+# Check for cruft left by unresolved conflicts. Search for
+# "^<<<<<<<$", "^-------$", and "^>>>>>>>$".
+#
+# Look for a copyright and automagically update it to the
+# current year.
+#
+# Contributed by David Hampton <hampton@cisco.com>
+#
+
+############################################################
+#
+# Configurable options
+#
+############################################################
+#
+# Check each file (except dot files) for an RCS "Id" keyword.
+#
+$check_id = 1;
+
+#
+# Record the directory for later use by the log_accumulate stript.
+#
+$record_directory = 1;
+
+############################################################
+#
+# Constants
+#
+############################################################
+$LAST_FILE = "/tmp/#cvs.lastdir";
+$ENTRIES = "CVS/Entries";
+
+$NoId = "
+%s - Does not contain a line with the keyword \"Id:\".
+ Please see the template files for an example.\n";
+
+# Protect string from substitution by RCS.
+$NoName = "
+%s - The ID line should contain only \"\$\I\d\:\ \$\" for a newly created file.\n";
+
+$BadName = "
+%s - The file name '%s' in the ID line does not match
+ the actual filename.\n";
+
+$BadVersion = "
+%s - How dare you!! You replaced your copy of the file '%s',
+ which was based upon version %s, with an %s version based
+ upon %s. Please move your '%s' out of the way, perform an
+ update to get the current version, and them merge your changes
+ into that file.\n";
+
+############################################################
+#
+# Subroutines
+#
+############################################################
+
+sub write_line {
+ local($filename, $line) = @_;
+ open(FILE, ">$filename") || die("Cannot open $filename, stopped");
+ print(FILE $line, "\n");
+ close(FILE);
+}
+
+sub check_version {
+ local($i, $id, $rname, $version);
+ local($filename, $cvsversion) = @_;
+
+ open(FILE, $filename) || die("Cannot open $filename, stopped");
+ for ($i = 1; $i < 10; $i++) {
+ $pos = -1;
+ last if eof(FILE);
+ $line = <FILE>;
+ $pos = index($line, "Id: ");
+ last if ($pos >= 0);
+ }
+
+ if ($pos == -1) {
+ printf($NoId, $filename);
+ return(1);
+ }
+
+ ($id, $rname, $version) = split(' ', substr($line, $pos));
+ if ($cvsversion{$filename} == 0) {
+ if ($rname ne "\$") {
+ printf($NoName, $filename);
+ return(1);
+ }
+ return(0);
+ }
+
+ if ($rname ne "$filename,v") {
+ printf($BadName, $filename, substr($rname, 0, length($rname)-2));
+ return(1);
+ }
+ if ($cvsversion{$filename} < $version) {
+ printf($BadVersion, $filename, $filename, $cvsversion{$filename},
+ "newer", $version, $filename);
+ return(1);
+ }
+ if ($cvsversion{$filename} > $version) {
+ printf($BadVersion, $filename, $filename, $cvsversion{$filename},
+ "older", $version, $filename);
+ return(1);
+ }
+ return(0);
+}
+
+#############################################################
+#
+# Main Body
+#
+############################################################
+
+$id = getpgrp();
+#print("ARGV - ", join(":", @ARGV), "\n");
+#print("id - ", id, "\n");
+
+#
+# Suck in the Entries file
+#
+open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n");
+while (<ENTRIES>) {
+ local($filename, $version) = split('/', substr($_, 1));
+ $cvsversion{$filename} = $version;
+}
+
+#
+# Now check each file name passed in, except for dot files. Dot files
+# are considered to be administrative files by this script.
+#
+if ($check_id != 0) {
+ $failed = 0;
+ $directory = $ARGV[0];
+ shift @ARGV;
+ foreach $arg (@ARGV) {
+ next if (index($arg, ".") == 0);
+ $failed += &check_version($arg);
+ }
+ if ($failed) {
+ print "\n";
+ exit(1);
+ }
+}
+
+#
+# Record this directory as the last one checked. This will be used
+# by the log_accumulate script to determine when it is processing
+# the final directory of a multi-directory commit.
+#
+if ($record_directory != 0) {
+ &write_line("$LAST_FILE.$id", $directory);
+}
+exit(0);
diff --git a/gnu/usr.bin/cvs/contrib/cvs_acls.pl b/gnu/usr.bin/cvs/contrib/cvs_acls.pl
new file mode 100644
index 000000000000..1a0096a3a356
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvs_acls.pl
@@ -0,0 +1,142 @@
+#!/usr/bin/perl -- # -*-Perl-*-
+#
+# cvs_acls.pl,v 1.2 1992/04/11 16:01:24 berliner Exp
+#
+# Access control lists for CVS. dgg@ksr.com (David G. Grubbs)
+#
+# CVS "commitinfo" for matching repository names, running the program it finds
+# on the same line. More information is available in the CVS man pages.
+#
+# ==== INSTALLATION:
+#
+# To use this program as I intended, do the following four things:
+#
+# 0. Install PERL. :-)
+#
+# 1. Put one line, as the *only* non-comment line, in your commitinfo file:
+#
+# DEFAULT /usr/local/bin/cvs_acls
+#
+# 2. Install this file as /usr/local/bin/cvs_acls and make it executable.
+#
+# 3. Create a file named $CVSROOT/CVSROOT/avail.
+#
+# ==== FORMAT OF THE avail FILE:
+#
+# The avail file determines whether you may commit files. It contains lines
+# read from top to bottom, keeping track of a single "bit". The "bit"
+# defaults to "on". It can be turned "off" by "unavail" lines and "on" by
+# "avail" lines. ==> Last one counts.
+#
+# Any line not beginning with "avail" or "unavail" is ignored.
+#
+# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated
+# triples: (All spaces and tabs are ignored in a line.)
+#
+# {avail.*,unavail.*} [| user,user,... [| repos,repos,...]]
+#
+# 1. String starting with "avail" or "unavail".
+# 2. Optional, comma-separated list of usernames.
+# 3. Optional, comma-separated list of repository pathnames.
+# These are pathnames relative to $CVSROOT. They can be directories or
+# filenames. A directory name allows access to all files and
+# directories below it.
+#
+# Example: (Text from the ';;' rightward may not appear in the file.)
+#
+# unavail ;; Make whole repository unavailable.
+# avail|dgg ;; Except for user "dgg".
+# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to
+# ;; the module whose repository is "bin/ls"
+#
+# PROGRAM LOGIC:
+#
+# CVS passes to @ARGV an absolute directory pathname (the repository
+# appended to your $CVSROOT variable), followed by a list of filenames
+# within that directory.
+#
+# We walk through the avail file looking for a line that matches both
+# the username and repository.
+#
+# A username match is simply the user's name appearing in the second
+# column of the avail line in a space-or-comma separate list.
+#
+# A repository match is either:
+# - One element of the third column matches $ARGV[0], or some
+# parent directory of $ARGV[0].
+# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be
+# in the file list in one avail line.
+# - In other words, using directory names in the third column of
+# the avail file allows committing of any file (or group of
+# files in a single commit) in the tree below that directory.
+# - If individual file names are used in the third column of
+# the avail file, then files must be committed individually or
+# all files specified in a single commit must all appear in
+# third column of a single avail line.
+#
+
+$debug = 0;
+$cvsroot = $ENV{'CVSROOT'};
+$availfile = $cvsroot . "/CVSROOT/avail";
+$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"});
+
+eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
+ while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
+exit 255 if $die; # process any variable=value switches
+
+die "Must set CVSROOT\n" if !$cvsroot;
+($repos = shift) =~ s:^$cvsroot/::;
+grep($_ = $repos . '/' . $_, @ARGV);
+
+print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug;
+
+$exit_val = 0; # Good Exit value
+
+$universal_off = 0;
+open (AVAIL, $availfile) || exit(0); # It is ok for avail file not to exist
+while (<AVAIL>) {
+ chop;
+ next if /^\s*\#/;
+ next if /^\s*$/;
+ ($flagstr, $u, $m) = split(/[\s,]*\|[\s,]*/, $_);
+
+ # Skip anything not starting with "avail" or "unavail" and complain.
+ (print "Bad avail line: $_\n"), next
+ if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/);
+
+ # Set which bit we are playing with. ('0' is OK == Available).
+ $flag = (($& eq "avail") ? 0 : 1);
+
+ # If we find a "universal off" flag (i.e. a simple "unavail") remember it
+ $universal_off = 1 if ($flag && !$u && !$m);
+
+ # $myname considered "in user list" if actually in list or is NULL
+ $in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u)));
+ print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user;
+
+ # Module matches if it is a NULL module list in the avail line. If module
+ # list is not null, we check every argument combination.
+ if (!($in_repo = !$m)) {
+ @tmp = split(/[\s,]+/,$m);
+ for $j (@tmp) {
+ # If the repos from avail is a parent(or equal) dir of $repos, OK
+ $in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//);
+ }
+ if (!$in_repo) {
+ $in_repo = 1;
+ for $j (@ARGV) {
+ last if !($in_repo = grep ($_ eq $j, @tmp));
+ }
+ }
+ }
+ print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo;
+
+ $exit_val = $flag if ($in_user && $in_repo);
+ print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug;
+}
+close(AVAIL);
+print "$$ ==== \$exit_val = $exit_val\n" if $debug;
+print "**** Access denied: Insufficient Karma ($myname|$repos)\n" if $exit_val;
+print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n"
+ if $universal_off && !$exit_val;
+exit($exit_val);
diff --git a/gnu/usr.bin/cvs/contrib/cvscheck b/gnu/usr.bin/cvs/contrib/cvscheck
new file mode 100644
index 000000000000..67fdbbde10d0
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvscheck
@@ -0,0 +1,84 @@
+#! /bin/sh
+# cvscheck,v 1.2 1992/04/10 03:04:19 berliner 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/cvscheck.man b/gnu/usr.bin/cvs/contrib/cvscheck.man
new file mode 100644
index 000000000000..10d47f5115fb
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvscheck.man
@@ -0,0 +1,53 @@
+.\" cvscheck.man,v 1.1 1992/04/10 03:04:20 berliner Exp
+.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+.TH CVSCHECK LOCAL "4 March 1991" FLUKE
+.SH NAME
+cvscheck \- identify files added, changed, or removed in a CVS working
+directory
+.SH SYNOPSIS
+.B cvscheck
+.SH DESCRIPTION
+This command is a housekeeping aid. It 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
+.BR commit ted.
+It also determines whether the files have been CVS
+.BR add ed
+or CVS
+.BR remove d.
+For directories, this command determines only whether they have been
+.BR add ed.
+It operates in the current directory only.
+.LP
+This command provides information that is available using CVS
+.B status
+and CVS
+.BR diff .
+The advantage of
+.B cvscheck
+is that its output is very concise. It saves you the strain (and
+potential error) of interpreting the output of CVS
+.B status
+and
+.BR diff .
+.LP
+See
+.BR cvs (local)
+or
+.BR cvshelp (local)
+for instructions on how to add or remove a file or directory in a
+CVS-controlled package.
+.SH DIAGNOSTICS
+The exit status is 0 if no files have been added, changed, or removed
+from the current directory. Otherwise, the command returns a count of
+the adds, changes, and deletes.
+.SH SEE ALSO
+.BR cvs (local),
+.BR cvshelp (local)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+Technical Computing
diff --git a/gnu/usr.bin/cvs/contrib/cvshelp.man b/gnu/usr.bin/cvs/contrib/cvshelp.man
new file mode 100644
index 000000000000..a7128a8ca83c
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvshelp.man
@@ -0,0 +1,562 @@
+.\" cvshelp.man,v 1.1 1992/04/10 03:04:21 berliner Exp
+.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.\" Start a command example
+.de XS
+.SP
+.in +.5i
+.ft B
+.nf
+..
+.\" End a command example
+.de XE
+.fi
+.ft P
+.in -.5i
+.SP
+..
+.TH CVSHELP LOCAL "17 March 1991" FLUKE
+.SH NAME
+cvshelp \- advice on using the Concurrent Versions System
+.SH DESCRIPTION
+This man page is based on experience using CVS.
+It is bound to change as we gain more experience.
+If you come up with better advice than is found here,
+contact the Software Technology
+Group and we will add it to this page.
+.SS "Getting Started"
+Use the following steps to prepare to use CVS:
+.TP
+\(bu
+Take a look at the CVS manual page to see what it can do for you, and
+if it fits your environment (or can possibly be made to fit your
+environment).
+.XS
+man cvs
+.XE
+If things look good, continue on...
+.TP
+\(bu
+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. Say you choose
+.B /src/master
+as the root
+of your source repository. Make the
+.SB CVSROOT.adm
+directory in the root of the source repository:
+.XS
+mkdir /src/master/CVSROOT.adm
+.XE
+.TP
+\(bu
+Populate this directory with the
+.I loginfo
+and
+.I modules
+files from the
+.B "/usr/doc/local/cvs"
+directory. Edit these files to reflect your local source repository
+environment \- they may be quite small initially, but will grow as
+sources are added to your source repository. Turn these files into
+RCS controlled files:
+.XS
+cd /src/master/CVSROOT.adm
+ci \-m'Initial loginfo file' loginfo
+ci \-m'Initial modules file' modules
+.XE
+.TP
+\(bu
+Run the command:
+.XS
+mkmodules /src/master/CVSROOT.adm
+.XE
+This will build the
+.BR ndbm (3)
+file for the modules database.
+.TP
+\(bu
+Remember to edit the
+.I modules
+file manually when sources are checked
+in with
+.B checkin
+or CVS
+.BR add .
+A copy of the
+.I modules
+file for editing can be retrieved with the command:
+.XS
+cvs checkout CVSROOT.adm
+.XE
+.TP
+\(bu
+Have all users of the CVS system set the
+.SM 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 a
+.I .login
+or
+.I .profile
+file:
+.XS
+setenv CVSROOT /src/master
+.XE
+for csh users, and
+.XS
+CVSROOT=/src/master; export CVSROOT
+.XE
+for sh users.
+.SS "Placing Locally Written Sources Under CVS Control"
+Say you want to place the `whizbang' sources under
+CVS control. Say further that the sources have never
+been under revision control before.
+.TP
+\(bu
+Move the source hierarchy (lock, stock, and barrel)
+into the master source repository:
+.XS
+mv ~/whizbang $CVSROOT
+.XE
+.TP
+\(bu
+Clean out unwanted object files:
+.XS
+cd $CVSROOT/whizbang
+make clean
+.XE
+.TP
+\(bu
+Turn every file in the hierarchy into an RCS controlled file:
+.XS
+descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-nV\fR\fIx\fR\fB_\fR\fIy\fR\fB *'
+.XE
+In this example, the initial release tag is \fBV\fIx\fB_\fIy\fR,
+representing version \fIx\fR.\fIy\fR.
+.LP
+You can use CVS on sources that are already under RCS control.
+The following example shows how.
+In this example, the source package is called `skunkworks'.
+.TP
+\(bu
+Move the source hierarchy into the master source
+repository:
+.XS
+mv ~/skunkworks $CVSROOT
+.XE
+.TP
+\(bu
+Clean out unwanted object files:
+.XS
+cd $CVSROOT/skunkworks
+make clean
+.XE
+.TP
+\(bu
+Clean out unwanted working files, leaving only the RCS `,v' files:
+.XS
+descend \-r rcsclean
+.XE
+Note: If any working files have been checked out and changed,
+.B rcsclean
+will fail. Check in the modified working files
+and run the command again.
+.TP
+\(bu
+Get rid of
+.SB RCS
+subdirectories. CVS does not use them.
+.XS
+descend \-r \-f 'mv RCS/*,v .'
+descend \-r \-f 'rmdir RCS'
+.XE
+.TP
+\(bu
+Delete any unwanted files that remain in the source hierarchy. Then
+make sure all files are under RCS control:
+.XS
+descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-n\fR\fItag\fR\fB *'
+.XE
+.I tag
+is the latest symbolic revision tag that you applied to your package
+(if any). Note: This command will probably generate lots of error
+messages (for directories and existing RCS files) that you can
+ignore.
+.SS "Placing a Third-Party Source Distribution Under CVS Control"
+The
+.B checkin
+command checks third-party sources into CVS. The
+difference between third-party sources and locally
+written sources is that third-party sources must be checked into a
+separate branch (called the
+.IR "vendor branch" )
+of the RCS tree. This makes it possible to merge local changes to
+the sources with later releases from the vendor.
+.TP
+\(bu
+Save the original distribution kit somewhere. For example, if the
+master source repository is
+.B /src/master
+the distribution kit could be saved in
+.BR /src/dist .
+Organize the distribution directory so that each release
+is clearly identifiable.
+.TP
+\(bu
+Unpack the package in a scratch directory, for example
+.BR ~/scratch .
+.TP
+\(bu
+Create a repository for the package.
+In this example, the package is called `Bugs-R-Us 4.3'.
+.XS
+mkdir $CVSROOT/bugs
+.XE
+.TP
+\(bu
+Check in the unpacked files:
+.XS
+cd ~/scratch
+checkin \-m 'Bugs-R-Us 4.3 distribution' bugs VENDOR V4_3
+.XE
+There is nothing magic about the tag `VENDOR', which is applied to
+the vendor branch. You can use whatever tag you want. `VENDOR' is a
+useful convention.
+.TP
+\(bu
+Never modify vendor files before checking them in.
+Check in the files
+.I exactly
+as you unpacked them.
+If you check in locally modified files, future vendor releases may
+wipe out your local changes.
+.SS "Working With CVS-Controlled Sources"
+To use or edit the sources, you must check out a private copy.
+For the following examples, the master files are assumed to reside in
+.BR "$CVSROOT/behemoth" .
+The working directory is
+.BR "~/work" .
+See
+.BR cvs (local)
+for more details on the commands mentioned below.
+.TP
+.I "To Check Out Working Files
+Use CVS
+.BR checkout :
+.XS
+cd ~/work
+cvs checkout behemoth
+.XE
+There is nothing magic about the working directory. CVS will check
+out sources anywhere you like. Once you have a working copy of the
+sources, you can compile or edit them as desired.
+.TP
+.I "To Display Changes You Have Made"
+Use CVS
+.BR diff
+to display detailed changes, equivalent to
+.BR rcsdiff (local).
+You can also use
+.BR cvscheck (local)
+to list files added, changed, and removed in
+the directory, but not yet
+.BR commit ted.
+You must be in a directory containing working files.
+.TP
+.I "To Display Revision Information"
+Use CVS
+.BR log ,
+which is equivalent to
+.BR rlog (local).
+You must be in a directory containing working files.
+.TP
+.I "To Update Working Files"
+Use CVS
+.BR update
+in a directory containing working files.
+This command brings your working files up
+to date with changes checked into the
+master repository since you last checked out or updated
+your files.
+.TP
+.I "To Check In Your Changes"
+Use CVS
+.BR commit
+in a directory containing working files.
+This command checks your changes into the master repository.
+You can specify files by name or use
+.XS
+cvs commit \-a
+.XE
+to
+.B commit
+all the files you have changed.
+.TP
+.I "To Add a File"
+Add the file to the working directory.
+Use CVS
+.B add
+to mark the file as added.
+Use CVS
+.B commit
+to add the file to the master repository.
+.TP
+.I "To Remove a File"
+Remove the file from the working directory.
+Use CVS
+.B remove
+to mark the file as removed.
+Use CVS
+.B commit
+to move the file from its current location in the master repository
+to the CVS
+.IR Attic
+directory.
+.TP
+.I "To Add a Directory"
+Add the directory to the working directory.
+Use CVS
+.B add
+to add the directory to the master repository.
+.TP
+.I "To Remove a Directory"
+.br
+You shouldn't remove directories under CVS. You should instead remove
+their contents and then prune them (using the
+.B \-f
+and
+.B \-p
+options) when you
+.B checkout
+or
+.B update
+your working files.
+.TP
+.I "To Tag a Release"
+Use CVS
+.B tag
+to apply a symbolic tag to the latest revision of each file in the
+master repository. For example:
+.XS
+cvs tag V2_1 behemoth
+.XE
+.TP
+.I "To Retrieve an Exact Copy of a Previous Release"
+During a CVS
+.B checkout
+or
+.BR update ,
+use the
+.B \-r
+option to retrieve revisions associated with a symbolic tag.
+Use the
+.B \-f
+option to ignore all RCS files that do not contain the
+tag.
+Use the
+.B \-p
+option to prune directories that wind up empty because none
+of their files matched the tag. Example:
+.XS
+cd ~/work
+cvs checkout \-r V2_1 \-f \-p behemoth
+.XE
+.SS "Logging Changes"
+It is a good idea to keep a change log together with the
+sources. As a minimum, the change log should name and describe each
+tagged release. The change log should also be under CVS control and
+should be tagged along with the sources.
+.LP
+.BR cvslog (local)
+can help. This command logs
+changes reported during CVS
+.B commit
+operations. It automatically
+updates a change log file in your working directory. When you are
+finished making changes, you (optionally) edit the change log file and
+then commit it to the master repository.
+.LP
+Note: You must edit the change log to describe a new release
+and
+.B commit
+it to the master repository
+.I before
+.BR tag ging
+the release using CVS. Otherwise, the release description will not be
+included in the tagged package.
+.LP
+See
+.BR cvslog (local)
+for more information.
+.SS "Merging a Subsequent Third-Party Distribution"
+The initial steps in this process are identical to placing a
+third-party distribution under CVS for the first time: save the
+distribution kit and unpack the package in a scratch directory. From
+that point the steps diverge.
+The following example considers release 5.0 of the
+Bugs-R-Us package.
+.TP
+\(bu
+Check in the sources after unpacking them:
+.XS
+cd ~/scratch
+checkin \-m 'Bugs-R-Us 5.0 distribution' bugs VENDOR V5_0 \\
+ | tee ~/WARNINGS
+.XE
+It is important to save the output of
+.B checkin
+in a file
+because it lists the sources that have been locally modified.
+It is best to save the file in a different directory (for example,
+your home directory). Otherwise,
+.B checkin
+will try to check it into the master repository.
+.TP
+\(bu
+In your usual working directory, check out a fresh copy of the
+distribution that you just checked in.
+.XS
+cd ~/work
+cvs checkout \-r VENDOR bugs
+.XE
+The
+.B checkout
+command shown above retrieves the latest revision on the vendor branch.
+.TP
+\(bu
+See the `WARNINGS' file for a list of all locally modified
+sources.
+For each locally modified source,
+look at the differences between
+the new distribution and the latest local revision:
+.XS
+cvs diff \-r \fR\fILocalRev file\fR\fB
+.XE
+In this command,
+.I LocalRev
+is the latest
+numeric or symbolic revision
+on the RCS trunk of
+.IR file .
+You can use CVS
+.B log
+to get the revision history.
+.TP
+\(bu
+If your local modifications to a file have been incorporated into
+the vendor's distribution, then you should reset the default RCS
+branch for that file to the vendor branch. CVS doesn't provide a
+mechanism to do this. You have to do it by hand in the master
+repository:
+.XS
+rcs \-bVENDOR \fR\fIfile\fR\fB,v
+.XE
+.TP
+\(bu
+If your local modifications need to be merged with the
+new distribution, use CVS
+.B join
+to do it:
+.XS
+cvs join \-r VENDOR \fR\fIfile\fR\fB
+.XE
+The resulting file will be placed in your working directory.
+Edit it to resolve any overlaps.
+.TP
+\(bu
+Test the merged package.
+.TP
+\(bu
+Commit all modified files to the repository:
+.XS
+cvs commit \-a
+.XE
+.TP
+\(bu
+Tag the repository with a new local tag.
+.SS "Applying Patches to Third-Party Sources"
+Patches are handled in a manner very similar to complete
+third-party distributions. This example considers patches applied to
+Bugs-R-Us release 5.0.
+.TP
+\(bu
+Save the patch files together with the distribution kit
+to which they apply.
+The patch file names should clearly indicate the patch
+level.
+.TP
+\(bu
+In a scratch directory, check out the last `clean' vendor copy \- the
+highest revision on the vendor branch with
+.IR "no local changes" :
+.XS
+cd ~/scratch
+cvs checkout \-r VENDOR bugs
+.XE
+.TP
+\(bu
+Use
+.BR patch (local)
+to apply the patches. You should now have an image of the
+vendor's software just as though you had received a complete,
+new release.
+.TP
+\(bu
+Proceed with the steps described for merging a subsequent third-party
+distribution.
+.TP
+\(bu
+Note: When you get to the step that requires you
+to check out the new distribution after you have
+checked it into the vendor branch, you should move to a different
+directory. Do not attempt to
+.B checkout
+files in the directory in
+which you applied the patches. If you do, CVS will try to merge the
+changes that you made during patching with the version being checked
+out and things will get very confusing. Instead,
+go to a different directory (like your working directory) and
+check out the files there.
+.SS "Advice to Third-Party Source Hackers"
+As you can see from the preceding sections, merging local changes
+into third-party distributions remains difficult, and probably
+always will. This fact suggests some guidelines:
+.TP
+\(bu
+Minimize local changes.
+.I Never
+make stylistic changes.
+Change makefiles only as much as needed for installation. Avoid
+overhauling anything. Pray that the vendor does the same.
+.TP
+\(bu
+Avoid renaming files or moving them around.
+.TP
+\(bu
+Put independent, locally written files like help documents, local
+tools, or man pages in a sub-directory called `local-additions'.
+Locally written files that are linked into an existing executable
+should be added right in with the vendor's sources (not in a
+`local-additions' directory).
+If, in the future,
+the vendor distributes something
+equivalent to your locally written files
+you can CVS
+.B remove
+the files from the `local-additions' directory at that time.
+.SH SEE ALSO
+.BR cvs (local),
+.BR checkin (local),
+.BR cvslog (local),
+.BR cvscheck (local)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+Technical Computing
diff --git a/gnu/usr.bin/cvs/contrib/descend b/gnu/usr.bin/cvs/contrib/descend
new file mode 100644
index 000000000000..b63e4a77b3fd
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/descend
@@ -0,0 +1,116 @@
+#! /bin/sh
+# descend,v 1.1 1992/04/03 05:22:52 berliner 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/descend.man b/gnu/usr.bin/cvs/contrib/descend.man
new file mode 100644
index 000000000000..adeab3bd3820
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/descend.man
@@ -0,0 +1,115 @@
+.\" descend.man,v 1.1 1992/04/03 05:22:53 berliner Exp
+.TH DESCEND 1 "31 March 1992"
+.SH NAME
+descend \- walk directory tree and execute a command at each node
+.SH SYNOPSIS
+.B descend
+[
+.B \-afqrv
+]
+.I command
+[
+.I directory
+\&.\|.\|.
+]
+.SH DESCRIPTION
+.B descend
+walks down a directory tree and executes a command at each node. It
+is not as versatile as
+.BR find (1),
+but it has a simpler syntax. If no
+.I directory
+is specified,
+.B descend
+starts at the current one.
+.LP
+Unlike
+.BR find ,
+.B descend
+can be told to skip the special directories associated with RCS,
+CVS, and SCCS. This makes
+.B descend
+especially handy for use with these packages. It can be used with
+other commands too, of course.
+.LP
+.B descend
+is a poor man's way to make any command recursive. Note:
+.B descend
+does not follow symbolic links to directories unless they are
+specified on the command line.
+.SH OPTIONS
+.TP 15
+.B \-a
+.I All.
+Descend into directories that begin with '.'.
+.TP
+.B \-f
+.I Force.
+Ignore errors during descent. Normally,
+.B descend
+quits when an error occurs.
+.TP
+.B \-q
+.I Quiet.
+Suppress the message `In directory
+.IR directory '
+that is normally printed during the descent.
+.TP
+.B \-r
+.I Restricted.
+Don't descend into the special directories
+.SB RCS,
+.SB CVS,
+.SB CVS.adm,
+and
+.SB SCCS.
+.TP
+.B \-v
+.I Verbose.
+Print
+.I command
+before executing it.
+.SH EXAMPLES
+.TP 15
+.B "descend ls"
+Cheap substitute for `ls -R'.
+.TP 15
+.B "descend -f 'rm *' tree"
+Strip `tree' of its leaves. This command descends the `tree'
+directory, removing all regular files. Since
+.BR rm (1)
+does not remove directories, this command leaves the directory
+structure of `tree' intact, but denuded. The
+.B \-f
+option is required to keep
+.B descend
+from quitting. You could use `rm \-f' instead.
+.TP
+.B "descend -r 'co RCS/*'" /project/src/
+Check out every RCS file under the directory
+.BR "/project/src" .
+.TP
+.B "descend -r 'cvs diff'"
+Perform CVS `diff' operation on every directory below (and including)
+the current one.
+.SH DIAGNOSTICS
+Returns 1 if errors occur (and the
+.B \-f
+option is not used). Otherwise returns 0.
+.SH SEE ALSO
+.BR find (1),
+.BR rcsintro (1),
+.BR cvs (1),
+.BR sccs (1)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+John Fluke Mfg. Co., Inc.
+.SH BUGS
+Shell metacharacters in
+.I command
+may have bizarre effects. In particular, compound commands
+(containing ';', '[', and ']' characters) will not work. It is best
+to enclose complicated commands in single quotes \(aa\ \(aa.
diff --git a/gnu/usr.bin/cvs/contrib/dirfns b/gnu/usr.bin/cvs/contrib/dirfns
new file mode 100644
index 000000000000..8324c4198e35
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/dirfns
@@ -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/log.pl b/gnu/usr.bin/cvs/contrib/log.pl
new file mode 100644
index 000000000000..a6c75f6f0c6b
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/log.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+# Modified by berliner@Sun.COM to add support for CVS 1.3 2/27/92
+#
+# Date: Tue, 6 Aug 91 13:27 EDT
+# From: samborn@sunrise.com (Kevin Samborn)
+#
+# I revised the perl script I sent you yesterday to use the info you
+# send in on stdin. (I am appending the newer script to the end)
+#
+# now the output looks like this:
+#
+# **************************************
+# date: Tuesday, August 6, 1991 @ 13:17
+# author: samborn
+# Update of /elmer/cvs/CVSROOT.adm
+# In directory astro:/home/samborn/CVSROOT.adm
+#
+# Modified Files:
+# test3
+#
+# Added Files:
+# test6
+#
+# Removed Files:
+# test4
+#
+# Log Message:
+# wow, what a test
+#
+# RCS: 1.4 /elmer/cvs/CVSROOT.adm/test3,v
+# RCS: 1.1 /elmer/cvs/CVSROOT.adm/test6,v
+# RCS: 1.1 /elmer/cvs/CVSROOT.adm/Attic/test4,v
+#
+
+#
+# turn off setgid
+#
+$) = $(;
+
+#
+# parse command line arguments
+#
+@files = split(/ /,$ARGV[0]);
+$logfile = $ARGV[1];
+$cvsroot = $ENV{'CVSROOT'};
+
+#
+# Some date and time arrays
+#
+@mos = (January,February,March,April,May,June,July,August,September,
+ October,November,December);
+@days = (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
+
+#
+# get login name
+#
+$login = getlogin || (getpwuid($<))[0] || "nobody";
+
+#
+# open log file for appending
+#
+if ((open(OUT, ">>" . $logfile)) != 1) {
+ die "Could not open logfile " . $logfile . "\n";
+}
+
+#
+# Header
+#
+print OUT "\n";
+print OUT "**************************************\n";
+print OUT "date: " . $days[$wday] . ", " . $mos[$mon] . " " . $mday . ", 19" . $year .
+ " @ " . $hour . ":" . sprintf("%02d", $min) . "\n";
+print OUT "author: " . $login . "\n";
+
+#
+#print the stuff on stdin to the logfile
+#
+open(IN, "-");
+while(<IN>) {
+ print OUT $_;
+}
+close(IN);
+
+print OUT "\n";
+
+#
+# after log information, do an 'cvs -Qn status' on each file in the arguments.
+#
+for $file (@files[1..$#files]) {
+ if ($file eq "-") {
+ last;
+ }
+ open(RCS,"-|") || exec 'cvs', '-Qn', 'status', $file;
+ while (<RCS>) {
+ if (substr($_, 0, 7) eq " RCS") {
+ print OUT;
+ }
+ }
+ close (RCS);
+}
+
+close (OUT);
diff --git a/gnu/usr.bin/cvs/contrib/log_accum.pl b/gnu/usr.bin/cvs/contrib/log_accum.pl
new file mode 100644
index 000000000000..798e25f1f0e1
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/log_accum.pl
@@ -0,0 +1,331 @@
+#!/usr/local/bin/perl -w
+#
+# Perl filter to handle the log messages from the checkin of files in
+# a directory. This script will group the lists of files by log
+# message, and mail a single consolidated log message at the end of
+# the commit.
+#
+# This file assumes a pre-commit checking program that leaves the
+# names of the first and last commit directories in a temporary file.
+#
+# Contributed by David Hampton <hampton@cisco.com>
+#
+
+############################################################
+#
+# Configurable options
+#
+############################################################
+#
+# Do cisco Systems, Inc. specific nonsense.
+#
+$cisco_systems = 1;
+
+#
+# Recipient of all mail messages
+#
+$mailto = "sw-notification@cisco.com";
+
+############################################################
+#
+# Constants
+#
+############################################################
+$STATE_NONE = 0;
+$STATE_CHANGED = 1;
+$STATE_ADDED = 2;
+$STATE_REMOVED = 3;
+$STATE_LOG = 4;
+
+$LAST_FILE = "/tmp/#cvs.lastdir";
+$CHANGED_FILE = "/tmp/#cvs.files.changed";
+$ADDED_FILE = "/tmp/#cvs.files.added";
+$REMOVED_FILE = "/tmp/#cvs.files.removed";
+$LOG_FILE = "/tmp/#cvs.files.log";
+$FILE_PREFIX = "#cvs.files";
+
+$VERSION_FILE = "version";
+$TRUNKREV_FILE = "TrunkRev";
+$CHANGES_FILE = "Changes";
+$CHANGES_TEMP = "Changes.tmp";
+
+############################################################
+#
+# Subroutines
+#
+############################################################
+
+sub format_names {
+ local($dir, @files) = @_;
+ local(@lines);
+ $lines[0] = sprintf(" %-08s", $dir);
+ foreach $file (@files) {
+ if (length($lines[$#lines]) + length($file) > 60) {
+ $lines[++$#lines] = sprintf(" %8s", " ");
+ }
+ $lines[$#lines] .= " ".$file;
+ }
+ @lines;
+}
+
+sub cleanup_tmpfiles {
+ local($all) = @_;
+ local($wd, @files);
+
+ $wd = `pwd`;
+ chdir("/tmp");
+ opendir(DIR, ".");
+ if ($all == 1) {
+ push(@files, grep(/$id$/, readdir(DIR)));
+ } else {
+ push(@files, grep(/^$FILE_PREFIX.*$id$/, readdir(DIR)));
+ }
+ closedir(DIR);
+ foreach (@files) {
+ unlink $_;
+ }
+ chdir($wd);
+}
+
+sub write_logfile {
+ local($filename, @lines) = @_;
+ open(FILE, ">$filename") || die ("Cannot open log file $filename.\n");
+ print(FILE join("\n", @lines), "\n");
+ close(FILE);
+}
+
+sub append_to_file {
+ local($filename, $dir, @files) = @_;
+ if (@files) {
+ local(@lines) = &format_names($dir, @files);
+ open(FILE, ">>$filename") || die ("Cannot open file $filename.\n");
+ print(FILE join("\n", @lines), "\n");
+ close(FILE);
+ }
+}
+
+sub write_line {
+ local($filename, $line) = @_;
+ open(FILE, ">$filename") || die("Cannot open file $filename.\n");
+ print(FILE $line, "\n");
+ close(FILE);
+}
+
+sub read_line {
+ local($line);
+ local($filename) = @_;
+ open(FILE, "<$filename") || die("Cannot open file $filename.\n");
+ $line = <FILE>;
+ close(FILE);
+ chop($line);
+ $line;
+}
+
+sub read_file {
+ local(@text);
+ local($filename, $leader) = @_;
+ open(FILE, "<$filename") || return ();
+ while (<FILE>) {
+ chop;
+ push(@text, sprintf(" %-10s %s", $leader, $_));
+ $leader = "";
+ }
+ close(FILE);
+ @text;
+}
+
+sub read_logfile {
+ local(@text);
+ local($filename, $leader) = @_;
+ open(FILE, "<$filename") || die ("Cannot open log file $filename.\n");
+ while (<FILE>) {
+ chop;
+ push(@text, $leader.$_);
+ }
+ close(FILE);
+ @text;
+}
+
+sub bump_version {
+ local($trunkrev, $editnum, $version);
+
+ $trunkrev = &read_line("$ENV{'CVSROOT'}/$repository/$TRUNKREV_FILE");
+ $editnum = &read_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE");
+ &write_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE", $editnum+1);
+ $version = $trunkrev . "(" . $editnum . ")";
+}
+
+sub build_header {
+ local($version) = @_;
+ local($header);
+ local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
+ $header = sprintf("%-8s %s %02d/%02d/%02d %02d:%02d:%02d",
+ $login, $version, $year%100, $mon+1, $mday,
+ $hour, $min, $sec);
+}
+
+sub do_changes_file {
+ local($changes, $tmpchanges);
+ local(@text) = @_;
+
+ $changes = "$ENV{'CVSROOT'}/$repository/$CHANGES_FILE";
+ $tmpchanges = "$ENV{'CVSROOT'}/$repository/$CHANGES_TEMP";
+ if (rename($changes, $tmpchanges) != 1) {
+ die("Cannot rename $changes to $tmpchanges.\n");
+ }
+ open(CHANGES, ">$changes") || die("Cannot open $changes.\n");
+ open(TMPCHANGES, "<$tmpchanges") || die("Cannot open $tmpchanges.\n");
+ print(CHANGES join("\n", @text), "\n\n");
+ print(CHANGES <TMPCHANGES>);
+ close(CHANGES);
+ close(TMPCHANGES);
+ unlink($tmpchanges);
+}
+
+sub mail_notification {
+ local($name, @text) = @_;
+ open(MAIL, "| mail -s \"Source Repository Modification\" $name");
+ print(MAIL join("\n", @text));
+ close(MAIL);
+}
+
+#############################################################
+#
+# Main Body
+#
+############################################################
+
+#
+# Initialize basic variables
+#
+$id = getpgrp();
+$state = $STATE_NONE;
+$login = getlogin || (getpwuid($<))[0] || die("Unknown user $<.\n");
+@files = split(' ', $ARGV[0]);
+@path = split('/', $files[0]);
+$repository = @path[0];
+if ($#path == 0) {
+ $dir = ".";
+} else {
+ $dir = join('/', @path[1..$#path]);
+}
+#print("ARGV - ", join(":", @ARGV), "\n");
+#print("files - ", join(":", @files), "\n");
+#print("path - ", join(":", @path), "\n");
+#print("dir - ", $dir, "\n");
+#print("id - ", $id, "\n");
+
+#
+# Check for a new directory first. This will always appear as a
+# single item in the argument list, and an empty log message.
+#
+if ($ARGV[0] =~ /New directory/) {
+ $version = &bump_version if ($cisco_systems != 0);
+ $header = &build_header($version);
+ @text = ();
+ push(@text, $header);
+ push(@text, "");
+ push(@text, " ".$ARGV[0]);
+ &do_changes_file(@text) if ($cisco_systems != 0);
+ &mail_notification($mailto, @text);
+ exit 0;
+}
+
+#
+# Iterate over the body of the message collecting information.
+#
+while (<STDIN>) {
+ chop; # Drop the newline
+ if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
+ if (/^Added Files/) { $state = $STATE_ADDED; next; }
+ if (/^Removed Files/) { $state = $STATE_REMOVED; next; }
+ if (/^Log Message/) { $state = $STATE_LOG; next; }
+ s/^[ \t\n]+//; # delete leading space
+ s/[ \t\n]+$//; # delete trailing space
+
+ push (@changed_files, split) if ($state == $STATE_CHANGED);
+ push (@added_files, split) if ($state == $STATE_ADDED);
+ push (@removed_files, split) if ($state == $STATE_REMOVED);
+ push (@log_lines, $_) if ($state == $STATE_LOG);
+}
+
+#
+# Strip leading and trailing blank lines from the log message. Also
+# compress multiple blank lines in the body of the message down to a
+# single blank line.
+#
+while ($#log_lines > -1) {
+ last if ($log_lines[0] ne "");
+ shift(@log_lines);
+}
+while ($#log_lines > -1) {
+ last if ($log_lines[$#log_lines] ne "");
+ pop(@log_lines);
+}
+for ($i = $#log_lines; $i > 0; $i--) {
+ if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
+ splice(@log_lines, $i, 1);
+ }
+}
+
+#
+# Find the log file that matches this log message
+#
+for ($i = 0; ; $i++) {
+ last if (! -e "$LOG_FILE.$i.$id");
+ @text = &read_logfile("$LOG_FILE.$i.$id", "");
+ last if ($#text == -1);
+ last if (join(" ", @log_lines) eq join(" ", @text));
+}
+
+#
+# Spit out the information gathered in this pass.
+#
+&write_logfile("$LOG_FILE.$i.$id", @log_lines);
+&append_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
+&append_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
+&append_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
+
+#
+# Check whether this is the last directory. If not, quit.
+#
+$_ = &read_line("$LAST_FILE.$id");
+exit 0 if (! grep(/$files[0]$/, $_));
+
+#
+# This is it. The commits are all finished. Lump everything together
+# into a single message, fire a copy off to the mailing list, and drop
+# it on the end of the Changes file.
+#
+# Get the full version number
+#
+$version = &bump_version if ($cisco_systems != 0);
+$header = &build_header($version);
+
+#
+# Produce the final compilation of the log messages
+#
+@text = ();
+push(@text, $header);
+push(@text, "");
+for ($i = 0; ; $i++) {
+ last if (! -e "$LOG_FILE.$i.$id");
+ push(@text, &read_file("$CHANGED_FILE.$i.$id", "Modified:"));
+ push(@text, &read_file("$ADDED_FILE.$i.$id", "Added:"));
+ push(@text, &read_file("$REMOVED_FILE.$i.$id", "Removed:"));
+ push(@text, " Log:");
+ push(@text, &read_logfile("$LOG_FILE.$i.$id", " "));
+ push(@text, "");
+}
+if ($cisco_systems != 0) {
+ @ddts = grep(/^CSCdi/, split(' ', join(" ", @text)));
+ $text[0] .= " " . join(" ", @ddts);
+}
+#
+# Put the log message at the beginning of the Changes file and mail
+# out the notification.
+#
+&do_changes_file(@text) if ($cisco_systems != 0);
+&mail_notification($mailto, @text);
+&cleanup_tmpfiles(1);
+exit 0;
diff --git a/gnu/usr.bin/cvs/contrib/mfpipe.pl b/gnu/usr.bin/cvs/contrib/mfpipe.pl
new file mode 100644
index 000000000000..74cc5e1e8d06
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/mfpipe.pl
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+#
+# From: clyne@niwot.scd.ucar.EDU (John Clyne)
+# Date: Fri, 28 Feb 92 09:54:21 MST
+#
+# BTW, i wrote a perl script that is similar to 'nfpipe' except that in
+# addition to logging to a file it provides a command line option for mailing
+# change notices to a group of users. Obviously you probably wouldn't want
+# to mail every change. But there may be certain directories that are commonly
+# accessed by a group of users who would benefit from an email notice.
+# Especially if they regularly beat on the same directory. Anyway if you
+# think anyone would be interested here it is.
+#
+# mfpipe.pl,v 1.1 1992/03/02 01:22:41 berliner Exp
+#
+#
+# File: mfpipe
+#
+# Author: John Clyne
+# National Center for Atmospheric Research
+# PO 3000, Boulder, Colorado
+#
+# Date: Wed Feb 26 18:34:53 MST 1992
+#
+# Description: Tee standard input to mail a list of users and to
+# a file. Used by CVS logging.
+#
+# Usage: mfpipe [-f file] [user@host...]
+#
+# Environment: CVSROOT
+# Path to CVS root.
+#
+# Files:
+#
+#
+# Options: -f file
+# Capture output to 'file'
+#
+
+$header = "Log Message:\n";
+
+$mailcmd = "| mail -s 'CVS update notice'";
+$whoami = `whoami`;
+chop $whoami;
+$date = `date`;
+chop $date;
+
+$cvsroot = $ENV{'CVSROOT'};
+
+while (@ARGV) {
+ $arg = shift @ARGV;
+
+ if ($arg eq '-f') {
+ $file = shift @ARGV;
+ }
+ else {
+ $users = "$users $arg";
+ }
+}
+
+if ($users) {
+ $mailcmd = "$mailcmd $users";
+ open(MAIL, $mailcmd) || die "Execing $mail: $!\n";
+}
+
+if ($file) {
+ $logfile = "$cvsroot/LOG/$file";
+ open(FILE, ">> $logfile") || die "Opening $logfile: $!\n";
+}
+
+print FILE "$whoami $date--------BEGIN LOG ENTRY-------------\n" if ($logfile);
+
+while (<>) {
+ print FILE $log if ($log && $logfile);
+
+ print FILE $_ if ($logfile);
+ print MAIL $_ if ($users);
+
+ $log = "log: " if ($_ eq $header);
+}
+
+close FILE;
+die "Write failed" if $?;
+close MAIL;
+die "Mail failed" if $?;
+
+exit 0;
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog
new file mode 100644
index 000000000000..fab9a7dc9a36
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog
@@ -0,0 +1,119 @@
+Tue Apr 7 09:11:27 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.02.
+
+ * pcl-cvs.el (cvs-diff-backup, cvs-edit-done, cvs-status): Call
+ save-some-buffers.
+
+ * pcl-cvs.el (cvs-diff-backup-extractor): Fixed syntax error.
+
+ * Makefile, README, compile-all.el, dist-makefile, pcl-cvs.el,
+ pcl-cvs.texinfo (XXRELEASEXX): A magic string that is substituted
+ for the current release number when a distribution is made.
+ (Release 1.01 says that it is release 1.00).
+
+ * pcl-cvs.el (cvs-find-file): Added missing pair of parenthesis.
+
+Mon Mar 30 14:25:26 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.01.
+
+ * pcl-cvs.el (cvs-parse-buffer): The message when waiting for a
+ lock has been changed.
+
+Sun Mar 29 05:29:57 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.00.
+
+ * pcl-cvs.el (cvs-do-update, cvs-sentinel, cvs-parse-buffer):
+ Major rewrite of buffer and window selection and handling.
+ The *cvs* buffer is now killed whenever a new "cvs update" is
+ initiated. The -update buffer is replaced with the *cvs*
+ buffer when the update is completed.
+
+Sat Mar 28 21:03:05 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-delete-unused-temporary-buffers): Fixed it.
+
+ * pcl-cvs.el (cvs-auto-remove-handled): New variable.
+ * pcl-cvs.el (cvs-edit-done): Use it.
+ * pcl-cvs.texinfo (Customization, Removing handled entries):
+ Document it.
+
+ * pcl-cvs.el (cvs-mode): Turn of the undo feature. It really
+ isn't useful in a cookie buffer...
+
+ * pcl-cvs.el (cvs-edit-done): Committing a file now looks more
+ like diffing a file. The window handling is better.
+ * pcl-cvs.el (cvs-use-temp-buffer): The &optional switch is no
+ longer needed.
+
+Mon Mar 23 00:20:33 1992 Per Cederqvist (ceder@robin)
+
+ * Release 0.97.
+
+ * pcl-cvs.el (default-directory): Make sure it always ends in a
+ slash. fileinfo->dir does NOT end in a slash, and I had forgotten
+ to call file-name-as-directory in various places.
+
+ * pcl-cvs.el (cvs-diff-backup-extractor): Signal an error if a
+ fileinfo without backup file is given.
+
+ * pcl-cvs.el (cvs-mode): Added documentation.
+
+ * pcl-cvs.el (cvs-execute-list): Fix the order of files in the
+ same directory.
+
+ * pcl-cvs.el (cvs-log-flags, cvs-status-flags): New variables.
+ * pcl-cvs.el (cvs-log, cvs-status): Use them.
+ * pcl-cvs.texinfo (Customization): Document them.
+
+ * pcl-cvs.el (cvs-diff-backup): Filter non-backup-diffable files
+ at an earlier stage, like cvs-commit does.
+
+ * pcl-cvs.el (cvs-diff-flags): New variable.
+ * pcl-cvs.el (cvs-diff-backup): Use it.
+ * pcl-cvs.texinfo (Customization): Document it.
+
+ * pcl-cvs.el (cvs-execute-single-file-list): Remove &rest before
+ last argument. No callers needed updating.
+
+ * pcl-cvs.el (cvs-execute-list): Remove the &rest before the last
+ argument (constant-args). Update all callers of cvs-execute-list
+ to use the new calling convention.
+ * pcl-cvs.el (cvs-cvs-diff-flags): Now a list of strings instead
+ of a string.
+ * pcl-cvs.texinfo (Customization): Document the change to
+ cvs-cvs-diff-flags.
+
+ * Release 0.96.
+
+ * pcl-cvs.el (cvs-cvs-diff-flags): New variable.
+ * pcl-cvs.el (cvs-diff-cvs): Use it.
+ * pcl-cvs.texinfo (Customization, Viewing differences): Document it.
+
+ * pcl-cvs.el (cvs-use-temp-buffe): Don't switch to the temporary
+ buffer. Use display-buffer and set-buffer instead. This way
+ cvs-log, cvs-status, cvs-diff-cvs and friends don't select the
+ temporary buffer. The cursor will remain in the *cvs* buffer.
+
+Sun Mar 22 21:50:18 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-find-file, cvs-find-file-other-window): Don't
+ prompt when reading in a directory in dired.
+
+ * Makefile (pcl-cvs-$(VER)): Include pcl-cvs-startup.el in the
+ distribution.
+
+ * dist-makefile (pcl-cvs.dvi): Don't fail even if texindex does
+ not exist.
+
+ * pcl-cvs.texinfo (@setchapternewpage): Changed from 'off' to 'on'.
+ * pcl-cvs.texinfo (Variable index): Joined into function index.
+ * pcl-cvs.texinfo (Key index): add a description about the key.
+ * pcl-cvs.texinfo: Many other small changes.
+
+Wed Mar 18 01:58:38 1992 Per Cederqvist (ceder@leopold)
+
+ * Use GNU General Public License version 2.
+
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL
new file mode 100644
index 000000000000..8c89053d9674
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL
@@ -0,0 +1,83 @@
+This text is copied from the TeXinfo manual for pcl-cvs.
+
+Installation of the pcl-cvs program
+===================================
+
+ 1. Edit the file `Makefile' to reflect the situation at your site.
+ The only things you have to change is the definition of
+ `lispdir' and `infodir'. The elisp files will be copied to
+ `lispdir', and the info file to `infodir'.
+
+ 2. Configure pcl-cvs.el
+
+ There are a couple of paths that you have to check to make
+ sure that they match you system. They appear early in the file
+ pcl-cvs.el.
+
+ *NOTE:* If your system is running emacs 18.57 or earlier
+ you MUST uncomment the line that says:
+
+ (setq delete-exited-processes nil)
+
+ Setting `delete-exited-processes' to `nil' works around a bug
+ in emacs that causes it to dump core. The bug was fixed in
+ emacs 18.58.
+
+ 3. Type `make install' in the source directory. This will
+ byte-compile all `.el' files and copy both the `.el' and the
+ `.elc' into the directory you specified in step 1.
+
+ If you don't want to install the `.el' files but only the
+ `.elc' files (the byte-compiled files), you can type ``make
+ install_elc'' instead of ``make install''.
+
+ If you only want to create the compiled elisp files, but
+ don't want to install them, you can type `make elcfiles'
+ instead. This is what happens if you only type `make' without
+ parameters.
+
+ 4. Edit the file `default.el' in your emacs lisp directory (usually
+ `/usr/gnu/emacs/lisp' or something similar) and enter the
+ contents of the file `pcl-cvs-startup.el' into it. It contains
+ a couple of `auto-load's that facilitates the use of pcl-cvs.
+
+
+
+
+Installation of the on-line manual.
+===================================
+
+ 1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing
+ `make info'. If you don't have the program `makeinfo' you can
+ get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as
+ `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version
+ there when you read this), or you could use the preformatted
+ info file `pcl-cvs.info' that is included in the distribution
+ (type `cp pcl-cvs.info pcl-cvs').
+
+ 2. Move the info file `pcl-cvs' to your standard info directory.
+ This might be called something like `/usr/gnu/emacs/info'.
+
+ 3. Edit the file `dir' in the info directory and enter one line to
+ contain a pointer to the info file `pcl-cvs'. The line can, for
+ instance, look like this:
+
+ * Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+
+
+
+
+How to make typeset documentation from pcl-cvs.texinfo
+======================================================
+
+ If you have TeX installed at your site, you can make a typeset
+manual from `pcl-cvs.texinfo'.
+
+ 1. Run TeX by typing ``make pcl-cvs.dvi''. You will not get the
+ indices unless you have the `texindex' program.
+
+ 2. Convert the resulting device independent file `pcl-cvs.dvi' to a
+ form which your printer can output and print it. If you have a
+ postscript printer there is a program, `dvi2ps', which does.
+ There is also a program which comes together with TeX, `dvips',
+ which you can use.
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile b/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile
new file mode 100644
index 000000000000..f0ded69d19a7
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile
@@ -0,0 +1,78 @@
+# Makefile,v 1.2 1992/04/07 20:49:07 berliner Exp
+# Makefile for pcl-cvs release 1.02.
+# Copyright (C) 1992 Per Cederqvist
+#
+# 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.
+
+# This is the directory in which the ELFILES and ELCFILES will be
+# installed.
+
+lispdir = /usr/local/lib/elisp
+
+# Where to install the info file.
+
+prefix=/usr/local
+infodir = $(prefix)/info
+
+#
+# The rest of this file should not need to be modified.
+#
+
+# Just in case...
+SHELL = /bin/sh
+
+ELFILES = pcl-cvs.el cookie.el elib-dll.el elib-node.el
+ELCFILES = pcl-cvs.elc cookie.elc elib-dll.elc elib-node.elc
+INFOFILES = pcl-cvs
+TEXTMPS = pcl-cvs.aux pcl-cvs.log pcl-cvs.toc pcl-cvs.dvi pcl-cvs.cp \
+ pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky pcl-cvs.pg \
+ pcl-cvs.cps pcl-cvs.fns pcl-cvs.kys pcl-cvs.pgs pcl-cvs.tps \
+ pcl-cvs.vrs
+
+INSTALL = install
+INSTALL_DATA = $(INSTALL)
+
+elcfiles:
+ emacs -batch -l ./compile-all.el -f compile-pcl-cvs
+
+all: elcfiles info
+
+# Don't install the info file yet, since it requires makeinfo
+# version 2.something (and version 1.something is distributed with emacs).
+#
+# install: install_elc install_info
+install: install_elc
+ for i in $(ELFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
+
+install_elc: elcfiles
+ for i in $(ELCFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
+
+install_info: pcl-cvs
+ $(INSTALL_DATA) pcl-cvs $(infodir)/pcl-cvs
+
+info pcl-cvs: pcl-cvs.texinfo
+ makeinfo +fill-column=70 pcl-cvs.texinfo
+
+pcl-cvs.dvi: pcl-cvs.texinfo
+ tex pcl-cvs.texinfo
+ -texindex pcl-cvs.cp pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky \
+ pcl-cvs.pg
+ tex pcl-cvs.texinfo
+
+mostlyclean clean realclean:
+ rm -f *~ core $(ELCFILES) $(INFOFILES) $(TEXTMPS)
+
+tags TAGS:
+ etags *.el
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/README b/gnu/usr.bin/cvs/contrib/pcl-cvs/README
new file mode 100644
index 000000000000..6f0a5feb2675
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/README
@@ -0,0 +1,14 @@
+README,v 1.2 1992/04/07 20:49:09 berliner Exp
+
+This is the readme file for pcl-cvs, release 1.02.
+
+Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+There is some configuration that needs to be done in pcl-cvs.el to get
+it to work. See the instructions in file INSTALL.
+
+Full documentation is in pcl-cvs.texinfo. Since it requires makeinfo
+2.14 a preformatted info file is also included (pcl-cvs.info).
+
+ ceder@lysator.liu.se
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el
new file mode 100644
index 000000000000..74f1bca1b215
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el
@@ -0,0 +1,52 @@
+;;;; compile-all.el,v 1.2 1992/04/07 20:49:10 berliner Exp
+;;;; This file byte-compiles all .el files in pcl-cvs release 1.02.
+;;;;
+;;;; Copyright (C) 1991 Inge Wallin
+;;;;
+;;;; This file is part of the GNU Emacs lisp library, Elib.
+;;;;
+;;;; GNU Elib is free software; you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation; either version 1, or (at your option)
+;;;; any later version.
+;;;;
+;;;; GNU Elib 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 Emacs; see the file COPYING. If not, write to
+;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;;;
+
+
+(setq elib-files '("elib-node"
+ "elib-dll"
+ "cookie"
+ "pcl-cvs"))
+
+
+(defun compile-file-if-necessary (file)
+ "Compile the Elib file FILE if necessary.
+
+This is done if FILE.el is newer than FILE.elc or if FILE.elc doesn't exist."
+ (let ((el-name (concat file ".el"))
+ (elc-name (concat file ".elc")))
+ (if (or (not (file-exists-p elc-name))
+ (file-newer-than-file-p el-name elc-name))
+ (progn
+ (message (format "Byte-compiling %s..." el-name))
+ (byte-compile-file el-name)))))
+
+
+(defun compile-pcl-cvs ()
+ "Byte-compile all uncompiled files of elib.
+Be sure to have . in load-path since a number of files in elib
+depend on other files and we always want the newer one even if
+a previous version of elib exists."
+
+ (interactive)
+ (setq load-path (append '(".") load-path))
+ (mapcar (function compile-file-if-necessary)
+ elib-files))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el
new file mode 100644
index 000000000000..8bd4bdff6ce0
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el
@@ -0,0 +1,884 @@
+;;; cookie.el,v 1.2 1992/04/07 20:49:12 berliner Exp
+;;; cookie.el -- Utility to display cookies in buffers
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; 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.
+
+;;;; TO-DO: Byt namn! tin -> wrapper (eller n}got b{ttre).
+
+;;; Note that this file is still under development. Comments,
+;;; enhancements and bug fixes are welcome.
+;;; Send them to ceder@lysator.liu.se.
+
+(defun impl nil (error "Not yet implemented!"))
+
+;;; Cookie is a package that imlements a connection between an
+;;; elib-dll and the contents of a buffer. Possible uses are dired
+;;; (have all files in a list, and show them), buffer-list,
+;;; kom-prioritize (in the LysKOM elisp client) and others. pcl-cvs.el
+;;; uses cookie.el.
+;;;
+;;; A cookie buffer contains a header, any number of cookies, and a
+;;; footer. The header and footer are constant strings that are given
+;;; to cookie-create when the buffer is placed under cookie. Each cookie
+;;; is displayed in the buffer by calling a user-supplied function
+;;; that takes a cookie and returns a string. The string may be
+;;; empty, or contain any number of lines. An extra newline is always
+;;; appended unless the string is empty.
+;;;
+;;; Cookie does not affect the mode of the buffer in any way. It
+;;; merely makes it easy to connect an underlying data representation
+;;; to the buffer contents.
+;;;
+;;; The cookie-node data type:
+;;; start-marker
+;;; ;; end-marker This field is no longer present.
+;;; cookie The user-supplied element.
+;;;
+;;; A dll of cookie-nodes are held in the buffer local variable
+;;; cake-tin.
+;;;
+;;; A tin is an object that contains one cookie. You can get the next
+;;; and previous tin.
+;;;
+
+(require 'elib-dll)
+(provide 'cookie)
+
+(defvar cookies nil
+ "A doubly linked list that contains the underlying data representation
+for the contents of a cookie buffer. The package elib-dll is used to
+manipulate this list.")
+
+(defvar cookie-pretty-printer nil
+ "The function that is used to pretty-print a cookie in this buffer.")
+
+(defvar cookie-header nil
+ "The tin that holds the header cookie.")
+
+(defvar cookie-footer nil
+ "The tin that holds the footer cookie.")
+
+(defvar cookie-last-tin nil
+ "The tin the cursor was positioned at, the last time the cookie
+package checked the cursor position. Buffer local in all buffers
+the cookie package works on. You may set this if your package
+thinks it knows where the cursor will be the next time this
+package is called. It can speed things up.
+
+It must never be set to a tin that has been deleted.")
+
+;;; ================================================================
+;;; Internal functions for use in the cookie package
+
+(put 'cookie-set-buffer 'lisp-indent-hook 1)
+
+(defmacro cookie-set-buffer (buffer &rest forms)
+
+ ;; Execute FORMS with BUFFER selected as current buffer.
+ ;; Return value of last form in FORMS. INTERNAL USE ONLY.
+
+ (let ((old-buffer (make-symbol "old-buffer")))
+ (` (let (((, old-buffer) (current-buffer)))
+ (set-buffer (get-buffer-create (, buffer)))
+ (unwind-protect
+ (progn (,@ forms))
+ (set-buffer (, old-buffer)))))))
+
+
+(defmacro cookie-filter-hf (tin)
+
+ ;; Evaluate TIN once and return it. BUT if it is
+ ;; equal to cookie-header or cookie-footer return nil instead.
+ ;; INTERNAL USE ONLY.
+
+ (let ((tempvar (make-symbol "tin")))
+ (` (let (((, tempvar) (, tin)))
+ (if (or (eq (, tempvar) cookie-header)
+ (eq (, tempvar) cookie-footer))
+ nil
+ (, tempvar))))))
+
+
+;;; cookie-tin
+;;; Constructor:
+
+(defun cookie-create-tin (start-marker
+ cookie)
+ ;; Create a tin. INTERNAL USE ONLY.
+ (cons 'COOKIE-TIN (vector start-marker nil cookie)))
+
+
+;;; Selectors:
+
+(defun cookie-tin-start-marker (cookie-tin)
+ ;; Get start-marker from cookie-tin. INTERNAL USE ONLY.
+ (elt (cdr cookie-tin) 0))
+
+;(defun cookie-tin-end-marker (cookie-tin)
+; ;;Get end-marker from cookie-tin. INTERNAL USE ONLY.
+; (elt (cdr cookie-tin) 1))
+
+(defun cookie-tin-cookie-safe (cookie-tin)
+ ;; Get cookie from cookie-tin. INTERNAL USE ONLY.
+ ;; Returns nil if given nil as input.
+ ;; This is the same as cookie-tin-cookie in version 18.57
+ ;; of emacs, but elt should signal an error when given nil
+ ;; as input (according to the info files).
+ (elt (cdr cookie-tin) 2))
+
+(defun cookie-tin-cookie (cookie-tin)
+ ;; Get cookie from cookie-tin. INTERNAL USE ONLY.
+ (elt (cdr cookie-tin) 2))
+
+
+;;; Modifiers:
+
+(defun set-cookie-tin-start-marker (cookie-tin newval)
+ ;; Set start-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+ (aset (cdr cookie-tin) 0 newval))
+
+;(defun set-cookie-tin-end-marker (cookie-tin newval)
+; ;; Set end-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+; (aset (cdr cookie-tin) 1 newval))
+
+(defun set-cookie-tin-cookie (cookie-tin newval)
+ ;; Set cookie in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+ (aset (cdr cookie-tin) 2 newval))
+
+
+
+;;; Predicate:
+
+(defun cookie-tin-p (object)
+ ;; Return t if OBJECT is a tin. INTERNAL USE ONLY.
+ (eq (car-safe object) 'COOKIE-TIN))
+
+;;; end of cookie-tin data type.
+
+
+(defun cookie-create-tin-and-insert (cookie string pos)
+ ;; Insert STRING at POS in current buffer. Remember start
+ ;; position. Create a tin containing them and the COOKIE.
+ ;; INTERNAL USE ONLY.
+
+ (save-excursion
+ (goto-char pos)
+ ;; Remember the position as a number so that it doesn't move
+ ;; when we insert the string.
+ (let ((start (if (markerp pos)
+ (marker-position pos)
+ pos)))
+ ;; Use insert-before-markers so that the marker for the
+ ;; next cookie is updated.
+ (insert-before-markers string)
+ (insert-before-markers ?\n)
+ (cookie-create-tin (copy-marker start) cookie))))
+
+
+(defun cookie-delete-tin-internal (tin)
+ ;; Delete a cookie from the buffer. INTERNAL USE ONLY.
+ ;; Can not be used on the footer.
+ (delete-region (cookie-tin-start-marker (dll-element cookies tin))
+ (cookie-tin-start-marker
+ (dll-element cookies
+ (dll-next cookies tin)))))
+
+
+
+(defun cookie-refresh-tin (tin)
+ ;; Redisplay the cookie represented by TIN. INTERNAL USE ONLY.
+ ;; Can not be used on the footer.
+
+ (save-excursion
+ ;; First, remove the string:
+ (delete-region (cookie-tin-start-marker (dll-element cookies tin))
+ (1- (marker-position
+ (cookie-tin-start-marker
+ (dll-element cookies
+ (dll-next cookies tin))))))
+
+ ;; Calculate and insert the string.
+
+ (goto-char (cookie-tin-start-marker (dll-element cookies tin)))
+ (insert
+ (funcall cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies tin))))))
+
+
+;;; ================================================================
+;;; The public members of the cookie package
+
+
+(defun cookie-cookie (buffer tin)
+ "Get the cookie from a TIN. Args: BUFFER TIN."
+ (cookie-set-buffer buffer
+ (cookie-tin-cookie (dll-element cookies tin))))
+
+
+
+
+(defun cookie-create (buffer pretty-printer &optional header footer)
+
+ "Start to use the cookie package in BUFFER.
+BUFFER may be a buffer or a buffer name. It is created if it does not exist.
+Beware that the entire contents of the buffer will be erased.
+PRETTY-PRINTER is a function that takes one cookie and returns a string
+to be displayed in the buffer. The string may be empty. If it is not
+empty a newline will be added automatically. It may span several lines.
+Optional third argument HEADER is a string that will always be present
+at the top of the buffer. HEADER should end with a newline. Optionaly
+fourth argument FOOTER is similar, and will always be inserted at the
+bottom of the buffer."
+
+ (cookie-set-buffer buffer
+
+ (erase-buffer)
+
+ (make-local-variable 'cookie-last-tin)
+ (make-local-variable 'cookie-pretty-printer)
+ (make-local-variable 'cookie-header)
+ (make-local-variable 'cookie-footer)
+ (make-local-variable 'cookies)
+
+ (setq cookie-last-tin nil)
+ (setq cookie-pretty-printer pretty-printer)
+ (setq cookies (dll-create))
+
+ (dll-enter-first cookies
+ (cookie-create-tin-and-insert
+ header header 0))
+ (setq cookie-header (dll-nth cookies 0))
+
+ (dll-enter-last cookies
+ (cookie-create-tin-and-insert
+ footer footer (point-max)))
+ (setq cookie-footer (dll-nth cookies -1))
+
+ (goto-char (point-min))
+ (forward-line 1)))
+
+
+(defun cookie-set-header (buffer header)
+ "Change the header. Args: BUFFER HEADER."
+ (impl))
+
+
+(defun cookie-set-footer (buffer header)
+ "Change the footer. Args: BUFFER FOOTER."
+ (impl))
+
+
+
+(defun cookie-enter-first (buffer cookie)
+ "Enter a COOKIE first in BUFFER.
+Args: BUFFER COOKIE."
+
+ (cookie-set-buffer buffer
+
+ ;; It is always safe to insert an element after the first element,
+ ;; because the header is always present. (dll-nth cookies 0) should
+ ;; never return nil.
+
+ (dll-enter-after
+ cookies
+ (dll-nth cookies 0)
+ (cookie-create-tin-and-insert
+ cookie
+ (funcall cookie-pretty-printer cookie)
+ (cookie-tin-start-marker
+ (dll-element cookies (dll-nth cookies 1)))))))
+
+
+
+(defun cookie-enter-last (buffer cookie)
+ "Enter a COOKIE last in BUFFER.
+Args: BUFFER COOKIE."
+
+ (cookie-set-buffer buffer
+
+ ;; Remember that the header and footer are always present. There
+ ;; is no need to check if (dll-nth cookies -2) returns nil.
+
+ (dll-enter-before
+ cookies
+ (dll-nth cookies -1)
+ (cookie-create-tin-and-insert
+ cookie
+ (funcall cookie-pretty-printer cookie)
+ (cookie-tin-start-marker (dll-last cookies))))))
+
+
+(defun cookie-enter-after (buffer node cookie)
+ (impl))
+
+
+(defun cookie-enter-before (buffer node cookie)
+ (impl))
+
+
+
+(defun cookie-next (buffer tin)
+ "Get the next tin. Args: BUFFER TIN.
+Returns nil if TIN is nil or the last cookie."
+ (if tin
+ (cookie-set-buffer buffer
+ (cookie-filter-hf (dll-next cookies tin)))))
+
+
+
+(defun cookie-previous (buffer tin)
+ "Get the previous tin. Args: BUFFER TIN.
+Returns nil if TIN is nil or the first cookie."
+ (if tin
+ (cookie-set-buffer buffer
+ (cookie-filter-hf (dll-previous cookies tin)))))
+
+
+(defun cookie-nth (buffer n)
+
+ "Return the Nth tin. Args: BUFFER N.
+N counts from zero. Nil is returned if there is less than N cookies.
+If N is negative, return the -(N+1)th last element.
+Thus, (cookie-nth dll 0) returns the first node,
+and (cookie-nth dll -1) returns the last node.
+
+Use cookie-cookie to extract the cookie from the tin."
+
+ (cookie-set-buffer buffer
+
+ ;; Skip the header (or footer, if n is negative).
+ (if (< n 0)
+ (setq n (1- n))
+ (setq n (1+ n)))
+
+ (cookie-filter-hf (dll-nth cookies n))))
+
+
+
+(defun cookie-delete (buffer tin)
+ "Delete a cookie. Args: BUFFER TIN."
+
+ (cookie-set-buffer buffer
+ (if (eq cookie-last-tin tin)
+ (setq cookie-last-tin nil))
+
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin)))
+
+
+
+(defun cookie-delete-first (buffer)
+ "Delete first cookie and return it. Args: BUFFER.
+Returns nil if there is no cookie left."
+
+ (cookie-set-buffer buffer
+
+ ;; We have to check that we do not try to delete the footer.
+
+ (let ((tin (dll-nth cookies 1))) ;Skip the header.
+ (if (eq tin cookie-footer)
+ nil
+ (cookie-delete-tin-internal tin)
+ (cookie-tin-cookie (dll-delete cookies tin))))))
+
+
+
+(defun cookie-delete-last (buffer)
+ "Delete last cookie and return it. Args: BUFFER.
+Returns nil if there is no cookie left."
+
+ (cookie-set-buffer buffer
+
+ ;; We have to check that we do not try to delete the header.
+
+ (let ((tin (dll-nth cookies -2))) ;Skip the footer.
+ (if (eq tin cookie-header)
+ nil
+ (cookie-delete-tin-internal tin)
+ (cookie-tin-cookie (dll-delete cookies tin))))))
+
+
+
+(defun cookie-first (buffer)
+
+ "Return the first cookie in BUFFER. The cookie is not removed."
+
+ (cookie-set-buffer buffer
+ (let ((tin (cookie-filter-hf (dll-nth cookies -1))))
+ (if tin
+ (cookie-tin-cookie-safe
+ (dll-element cookies tin))))))
+
+
+(defun cookie-last (buffer)
+
+ "Return the last cookie in BUFFER. The cookie is not removed."
+
+ (cookie-set-buffer buffer
+ (let ((tin (cookie-filter-hf (dll-nth cookies -2))))
+ (if tin
+ (cookie-tin-cookie-safe
+ (dll-element cookies tin))))))
+
+
+(defun cookie-empty (buffer)
+
+ "Return true if there are no cookies in BUFFER."
+
+ (cookie-set-buffer buffer
+ (eq (dll-nth cookies 1) cookie-footer)))
+
+
+(defun cookie-length (buffer)
+
+ "Return number of cookies in BUFFER."
+
+ ;; Don't count the footer and header.
+
+ (cookie-set-buffer buffer
+ (- (dll-length cookies) 2)))
+
+
+(defun cookie-all (buffer)
+
+ "Return a list of all cookies in BUFFER."
+
+ (cookie-set-buffer buffer
+ (let (result
+ (tin (dll-nth cookies -2)))
+ (while (not (eq tin cookie-header))
+ (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
+ result))
+ (setq tin (dll-previous cookies tin)))
+ result)))
+
+(defun cookie-clear (buffer)
+
+ "Remove all cookies in buffer."
+
+ (cookie-set-buffer buffer
+ (cookie-create buffer cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies cookie-header))
+ (cookie-tin-cookie (dll-element cookies cookie-footer)))))
+
+
+
+(defun cookie-map (map-function buffer &rest map-args)
+
+ "Apply MAP-FUNCTION to all cookies in BUFFER.
+MAP-FUNCTION is applied to the first element first.
+If MAP-FUNCTION returns non-nil the cookie will be refreshed.
+
+Note that BUFFER will be current buffer when MAP-FUNCTION is called.
+
+If more than two arguments are given to cookie-map, remaining
+arguments will be passed to MAP-FUNCTION."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ result)
+
+ (while (not (eq tin cookie-footer))
+
+ (if (apply map-function
+ (cookie-tin-cookie (dll-element cookies tin))
+ map-args)
+ (cookie-refresh-tin tin))
+
+ (setq tin (dll-next cookies tin))))))
+
+
+
+(defun cookie-map-reverse (map-function buffer &rest map-args)
+
+ "Apply MAP-FUNCTION to all cookies in BUFFER.
+MAP-FUNCTION is applied to the last cookie first.
+If MAP-FUNCTION returns non-nil the cookie will be refreshed.
+
+Note that BUFFER will be current buffer when MAP-FUNCTION is called.
+
+If more than two arguments are given to cookie-map, remaining
+arguments will be passed to MAP-FUNCTION."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply map-function
+ (cookie-tin-cookie (dll-element cookies tin))
+ map-args)
+ (cookie-refresh-tin tin))
+
+ (setq tin (dll-previous cookies tin))))))
+
+
+
+(defun cookie-enter-cookies (buffer cookie-list)
+
+ "Insert all cookies in the list COOKIE-LIST last in BUFFER.
+Args: BUFFER COOKIE-LIST."
+
+ (while cookie-list
+ (cookie-enter-last buffer (car cookie-list))
+ (setq cookie-list (cdr cookie-list))))
+
+
+(defun cookie-filter (buffer predicate)
+
+ "Remove all cookies in BUFFER for which PREDICATE returns nil.
+Note that BUFFER will be current-buffer when PREDICATE is called.
+
+The PREDICATE is called with one argument, the cookie."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ next)
+ (while (not (eq tin cookie-footer))
+ (setq next (dll-next cookies tin))
+ (if (funcall predicate (cookie-tin-cookie (dll-element cookies tin)))
+ nil
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin))
+ (setq tin next)))))
+
+
+(defun cookie-filter-tins (buffer predicate)
+
+ "Remove all cookies in BUFFER for which PREDICATE returns nil.
+Note that BUFFER will be current-buffer when PREDICATE is called.
+
+The PREDICATE is called with one argument, the tin."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ next)
+ (while (not (eq tin cookie-footer))
+ (setq next (dll-next cookies tin))
+ (if (funcall predicate tin)
+ nil
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin))
+ (setq tin next)))))
+
+(defun cookie-pos-before-middle-p (pos tin1 tin2)
+
+ "Return true if POS is in the first half of the region defined by TIN1 and
+TIN2."
+
+ (< pos (/ (+ (cookie-tin-start-marker (dll-element cookeis tin1))
+ (cookie-tin-start-marker (dll-element cookeis tin2)))
+ 2)))
+
+
+(defun cookie-get-selection (buffer pos &optional guess force-guess)
+
+ "Return the tin the POS is within.
+Args: BUFFER POS &optional GUESS FORCE-GUESS.
+GUESS should be a tin that it is likely that POS is near. If FORCE-GUESS
+is non-nil GUESS is always used as a first guess, otherwise the first
+guess is the first tin, last tin, or GUESS, whichever is nearest to
+pos in the BUFFER.
+
+If pos points within the header, the first cookie is returned.
+If pos points within the footer, the last cookie is returned.
+Nil is returned if there is no cookie.
+
+It is often good to specify cookie-last-tin as GUESS, but remember
+that cookie-last-tin is buffer local in all buffers that cookie
+operates on."
+
+ (cookie-set-buffer buffer
+
+ (cond
+ ; No cookies present?
+ ((eq (dll-nth cookies 1) (dll-nth cookies -1))
+ nil)
+
+ ; Before first cookie?
+ ((< pos (cookie-tin-start-marker
+ (dll-element cookies (dll-nth cookies 1))))
+ (dll-nth cookies 1))
+
+ ; After last cookie?
+ ((>= pos (cookie-tin-start-marker (dll-last cookies)))
+ (dll-nth cookies -2))
+
+ ; We now now that pos is within a cookie.
+ (t
+ ; Make an educated guess about which of the three known
+ ; cookies (the first, the last, or GUESS) is nearest.
+ (setq
+ guess
+ (cond
+ (force-guess guess)
+ (guess
+ (cond
+ ;; Closest to first cookie?
+ ((cookie-pos-before-middle-p
+ pos guess
+ (dll-nth cookies 1))
+ (dll-nth cookies 1))
+ ;; Closest to GUESS?
+ ((cookie-pos-before-middle-p
+ pos guess
+ cookie-footer)
+ guess)
+ ;; Closest to last cookie.
+ (t (dll-previous cookies cookie-footer))))
+ (t
+ ;; No guess given.
+ (cond
+ ;; First half?
+ ((cookie-pos-before-middle-p
+ pos (dll-nth cookies 1)
+ cookie-footer)
+ (dll-nth cookies 1))
+ (t (dll-previous cookies cookie-footer))))))
+
+ ;; GUESS is now a "best guess".
+
+ ;; Find the correct cookie. First determine in which direction
+ ;; it lies, and then move in that direction until it is found.
+
+ (cond
+ ;; Is pos after the guess?
+ ((>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
+
+ ;; Loop until we are exactly one cookie too far down...
+ (while (>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
+ (setq guess (dll-next cookies guess)))
+
+ ;; ...and return the previous cookie.
+ (dll-previous cookies guess))
+
+ ;; Pos is before guess
+ (t
+
+ (while (< pos (cookie-tin-start-marker (dll-element cookiess guess)))
+ (setq guess (dll-previous cookies guess)))
+
+ guess))))))
+
+
+(defun cookie-start-marker (buffer tin)
+
+ "Return start-position of a cookie in BUFFER.
+Args: BUFFER TIN.
+The marker that is returned should not be modified in any way,
+and is only valid until the contents of the cookie buffer changes."
+
+ (cookie-set-buffer buffer
+ (cookie-tin-start-marker (dll-element cookies tin))))
+
+
+(defun cookie-end-marker (buffer tin)
+
+ "Return end-position of a cookie in BUFFER.
+Args: BUFFER TIN.
+The marker that is returned should not be modified in any way,
+and is only valid until the contents of the cookie buffer changes."
+
+ (cookie-set-buffer buffer
+ (cookie-tin-start-marker
+ (dll-element cookies (dll-next cookies tin)))))
+
+
+
+(defun cookie-refresh (buffer)
+
+ "Refresh all cookies in BUFFER.
+Cookie-pretty-printer will be called for all cookies and the new result
+displayed.
+
+See also cookie-invalidate-tins."
+
+ (cookie-set-buffer buffer
+
+ (erase-buffer)
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies cookie-header))
+ (point) buffer)
+ (insert (cookie-tin-cookie (dll-element cookies cookie-header)))
+ (insert "\n")
+
+ (let ((tin (dll-nth cookies 1)))
+ (while (not (eq tin cookie-footer))
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies tin))
+ (point) buffer)
+ (insert
+ (funcall cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies tin))))
+ (insert "\n")
+ (setq tin (dll-next cookies tin))))
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies cookie-footer))
+ (point) buffer)
+ (insert (cookie-tin-cookie (dll-element cookies cookie-footer)))
+ (insert "\n")))
+
+
+(defun cookie-invalidate-tins (buffer &rest tins)
+
+ "Refresh some cookies.
+Args: BUFFER &rest TINS."
+
+ (cookie-set-buffer buffer
+
+ (while tins
+ (cookie-refresh-tin (car tins))
+ (setq tins (cdr tins)))))
+
+
+;;; Cookie movement commands.
+
+(defun cookie-set-goal-column (buffer goal)
+ "Set goal-column for BUFFER.
+Args: BUFFER GOAL.
+goal-column is made buffer-local."
+ (cookie-set-buffer buffer
+ (make-local-variable 'goal-column)
+ (setq goal-column goal)))
+
+
+(defun cookie-previous-cookie (buffer pos arg)
+ "Move point to the ARGth previous cookie.
+Don't move if we are at the first cookie.
+ARG is the prefix argument when called interactively.
+Args: BUFFER POS ARG.
+Sets cookie-last-tin to the cookie we move to."
+
+ (interactive (list (current-buffer) (point)
+ (prefix-numeric-value current-prefix-arg)))
+
+ (cookie-set-buffer buffer
+ (setq cookie-last-tin
+ (cookie-get-selection buffer pos cookie-last-tin))
+
+ (while (and cookie-last-tin (> arg 0))
+ (setq arg (1- arg))
+ (setq cookie-last-tin
+ (dll-previous cookies cookie-last-tin)))
+
+ ;; Never step above the first cookie.
+
+ (if (null (cookie-filter-hf cookie-last-tin))
+ (setq cookie-last-tin (dll-nth cookies 1)))
+
+ (goto-char
+ (cookie-tin-start-marker
+ (dll-element cookies cookie-last-tin)))
+
+ (if goal-column
+ (move-to-column goal-column))))
+
+
+
+(defun cookie-next-cookie (buffer pos arg)
+ "Move point to the ARGth next cookie.
+Don't move if we are at the last cookie.
+ARG is the prefix argument when called interactively.
+Args: BUFFER POS ARG.
+Sets cookie-last-tin to the cookie we move to."
+
+ (interactive (list (current-buffer) (point)
+ (prefix-numeric-value current-prefix-arg)))
+
+ (cookie-set-buffer buffer
+ (setq cookie-last-tin
+ (cookie-get-selection buffer pos cookie-last-tin))
+
+ (while (and cookie-last-tin (> arg 0))
+ (setq arg (1- arg))
+ (setq cookie-last-tin
+ (dll-next cookies cookie-last-tin)))
+
+ (if (null (cookie-filter-hf cookie-last-tin))
+ (setq cookie-last-tin (dll-nth cookies -2)))
+
+ (goto-char
+ (cookie-tin-start-marker
+ (dll-element cookies cookie-last-tin)))
+
+ (if goal-column
+ (move-to-column goal-column))))
+
+
+(defun cookie-collect-tins (buffer predicate &rest predicate-args)
+
+ "Return a list of all tins in BUFFER whose cookie PREDICATE
+returns true for.
+PREDICATE is a function that takes a cookie as its argument.
+The tins on the returned list will appear in the same order
+as in the buffer. You should not rely on in which order PREDICATE
+is called. Note that BUFFER is current-buffer when PREDICATE
+is called. (If you call cookie-collect with another buffer set
+as current-buffer and need to access buffer-local variables
+from that buffer within PREDICATE you must send them via
+PREDICATE-ARGS).
+
+If more than two arguments are given to cookie-collect the remaining
+arguments will be passed to PREDICATE.
+
+Use cookie-cookie to get the cookie from the tin."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply predicate
+ (cookie-tin-cookie (dll-element cookies tin))
+ predicate-args)
+ (setq result (cons tin result)))
+
+ (setq tin (dll-previous cookies tin)))
+ result)))
+
+
+(defun cookie-collect-cookies (buffer predicate &rest predicate-args)
+
+ "Return a list of all cookies in BUFFER that PREDICATE
+returns true for.
+PREDICATE is a function that takes a cookie as its argument.
+The cookie on the returned list will appear in the same order
+as in the buffer. You should not rely on in which order PREDICATE
+is called. Note that BUFFER is current-buffer when PREDICATE
+is called. (If you call cookie-collect with another buffer set
+as current-buffer and need to access buffer-local variables
+from that buffer within PREDICATE you must send them via
+PREDICATE-ARGS).
+
+If more than two arguments are given to cookie-collect the remaining
+arguments will be passed to PREDICATE."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply predicate
+ (cookie-tin-cookie (dll-element cookies tin))
+ predicate-args)
+ (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
+ result)))
+
+ (setq tin (dll-previous cookies tin)))
+ result)))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el
new file mode 100644
index 000000000000..733ff86f46c0
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el
@@ -0,0 +1,298 @@
+;;; elib-dll-debug -- A slow implementation of elib-dll for debugging.
+;;; elib-dll-debug.el,v 1.2 1992/04/07 20:49:13 berliner Exp
+;;; Copyright (C) 1991,1992 Per Cederqvist
+;;;
+;;; 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.
+
+
+;;; This is a plug-in replacement for elib-dll.el. It is dreadfully
+;;; slow, but it facilitates debugging. Don't trust the comments in
+;;; this file too much.
+(provide 'elib-dll)
+
+;;;
+;;; A doubly linked list consists of one cons cell which holds the tag
+;;; 'DL-LIST in the car cell and the list in the cdr
+;;; cell. The doubly linked list is implemented as a normal list. You
+;;; should use elib-dll.el and not this package in debugged code. This
+;;; package is not written for speed...
+;;;
+
+;;; ================================================================
+;;; Internal functions for use in the doubly linked list package
+
+(defun dll-get-dummy-node (dll)
+
+ ;; Return the dummy node. INTERNAL USE ONLY.
+ dll)
+
+(defun dll-list-nodes (dll)
+
+ ;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
+
+ (cdr dll))
+
+(defun dll-set-from-node-list (dll list)
+
+ ;; Set the contents of DLL to the nodes in LIST.
+ ;; INTERNAL USE ONLY.
+
+ (setcdr dll list))
+
+(defun dll-get-node-before (dll node)
+ ;; Return the node in DLL that points to NODE. Use
+ ;; (dll-get-node-before some-list nil) to get the last node.
+ ;; INTERNAL USE ONLY.
+ (while (and dll (not (eq (cdr dll) node)))
+ (setq dll (cdr dll)))
+ (if (not dll)
+ (error "Node not on list"))
+ dll)
+
+(defmacro dll-insert-after (node element)
+ (let ((node-v (make-symbol "node"))
+ (element-v (make-symbol "element")))
+ (` (let (((, node-v) (, node))
+ ((, element-v) (, element)))
+ (setcdr (, node-v) (cons (, element-v) (cdr (, node-v))))))))
+
+;;; ===================================================================
+;;; The public functions which operate on doubly linked lists.
+
+(defmacro dll-element (dll node)
+
+ "Get the element of a NODE in a doubly linked list DLL.
+Args: DLL NODE."
+
+ (` (car (, node))))
+
+
+(defun dll-create ()
+ "Create an empty doubly linked list."
+ (cons 'DL-LIST nil))
+
+
+(defun dll-p (object)
+ "Return t if OBJECT is a doubly linked list, otherwise return nil."
+ (eq (car-safe object) 'DL-LIST))
+
+
+(defun dll-enter-first (dll element)
+ "Add an element first on a doubly linked list.
+Args: DLL ELEMENT."
+ (setcdr dll (cons element (cdr dll))))
+
+
+(defun dll-enter-last (dll element)
+ "Add an element last on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-insert-after (dll-get-node-before dll nil) element))
+
+
+(defun dll-enter-after (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
+Args: DLL NODE ELEMENT."
+
+ (dll-get-node-before dll node)
+ (dll-insert-after node element))
+
+
+(defun dll-enter-before (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
+Args: DLL NODE ELEMENT."
+
+ (dll-insert-after (dll-get-node-before dll node) element))
+
+
+
+(defun dll-next (dll node)
+ "Return the node after NODE, or nil if NODE is the last node.
+Args: DLL NODE."
+
+ (dll-get-node-before dll node)
+ (cdr node))
+
+
+(defun dll-previous (dll node)
+ "Return the node before NODE, or nil if NODE is the first node.
+Args: DLL NODE."
+
+ (dll-get-node-before dll node))
+
+
+(defun dll-delete (dll node)
+
+ "Delete NODE from the doubly linked list DLL.
+Args: DLL NODE. Return the element of node."
+
+ ;; This is a no-op when applied to the dummy node. This will return
+ ;; nil if applied to the dummy node since it always contains nil.
+
+ (setcdr (dll-get-node-before dll node) (cdr node)))
+
+
+(defun dll-delete-first (dll)
+
+ "Delete the first NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (setcdr dll (cdr (cdr dll))))
+
+
+(defun dll-delete-last (dll)
+
+ "Delete the last NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (setcdr dll (dll-get-node-before dll nil) nil))
+
+
+(defun dll-first (dll)
+
+ "Return the first element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (car (cdr dll)))
+
+
+
+
+(defun dll-last (dll)
+
+ "Return the last element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (car (dll-get-node-before dll nil)))
+
+
+
+(defun dll-nth (dll n)
+
+ "Return the Nth node from the doubly linked list DLL.
+ Args: DLL N
+N counts from zero. If DLL is not that long, nil is returned.
+If N is negative, return the -(N+1)th last element.
+Thus, (dll-nth dll 0) returns the first node,
+and (dll-nth dll -1) returns the last node."
+
+ ;; Branch 0 ("follow left pointer") is used when n is negative.
+ ;; Branch 1 ("follow right pointer") is used otherwise.
+
+ (if (>= n 0)
+ (nthcdr n (cdr dll))
+ (unwind-protect
+ (progn (setcdr dll (nreverse (cdr dll)))
+ (nthcdr (- n) dll))
+ (setcdr dll (nreverse (cdr dll))))))
+
+(defun dll-empty (dll)
+
+ "Return t if the doubly linked list DLL is empty, nil otherwise"
+
+ (not (cdr dll)))
+
+(defun dll-length (dll)
+
+ "Returns the number of elements in the doubly linked list DLL."
+
+ (length (cdr dll)))
+
+
+
+(defun dll-copy (dll &optional element-copy-fnc)
+
+ "Return a copy of the doubly linked list DLL.
+If optional second argument ELEMENT-COPY-FNC is non-nil it should be
+a function that takes one argument, an element, and returns a copy of it.
+If ELEMENT-COPY-FNC is not given the elements are not copied."
+
+ (if element-copy-fnc
+ (cons 'DL-LIST (mapcar element-copy-fnc (cdr dll)))
+ (copy-sequence dll)))
+
+
+(defun dll-all (dll)
+
+ "Return all elements on the double linked list DLL as an ordinary list."
+
+ (cdr dll))
+
+
+(defun dll-clear (dll)
+
+ "Clear the doubly linked list DLL, i.e. make it completely empty."
+
+ (setcdr dll nil))
+
+
+(defun dll-map (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the first element first."
+
+ (mapcar map-function (cdr dll)))
+
+
+(defun dll-map-reverse (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the last element first."
+
+ (unwind-protect
+ (setcdr dll (nreverse (cdr dll)))
+ (mapcar map-function (cdr dll))
+ (setcdr dll (nreverse (cdr dll)))))
+
+
+(defun dll-create-from-list (list)
+
+ "Given an elisp LIST create a doubly linked list with the same elements."
+
+ (cons 'DL-LIST list))
+
+
+
+(defun dll-sort (dll predicate)
+
+ "Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
+Returns the sorted list. DLL is modified by side effects.
+PREDICATE is called with two elements of DLL, and should return T
+if the first element is \"less\" than the second."
+
+ (setcdr dll (sort (cdr dll) predicate))
+ dll)
+
+
+(defun dll-filter (dll predicate)
+
+ "Remove all elements in the doubly linked list DLL for which PREDICATE
+return nil."
+
+ (let* ((prev dll)
+ (node (cdr dll)))
+
+ (while node
+ (cond
+ ((funcall predicate (car node))
+ (setq prev node))
+ (t
+ (setcdr prev (cdr node))))
+ (setq node (cdr node)))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el
new file mode 100644
index 000000000000..855bd19e8ee0
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el
@@ -0,0 +1,386 @@
+;;; elib-dll.el,v 1.2 1992/04/07 20:49:15 berliner Exp
+;;; elib-dll.el -- Some primitives for Doubly linked lists.
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; 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.
+
+;;; Mail bug reports to ceder@lysator.liu.se.
+
+(require 'elib-node)
+(provide 'elib-dll)
+
+;;;
+;;; A doubly linked list consists of one cons cell which holds the tag
+;;; 'DL-LIST in the car cell and a pointer to a dummy node in the cdr
+;;; cell. The doubly linked list is implemented as a circular list
+;;; with the dummy node first and last. The dummy node is recognized
+;;; by comparing it to the node which the cdr of the cons cell points
+;;; to.
+;;;
+
+;;; ================================================================
+;;; Internal functions for use in the doubly linked list package
+
+(defun dll-get-dummy-node (dll)
+
+ ;; Return the dummy node. INTERNAL USE ONLY.
+ (cdr dll))
+
+(defun dll-list-nodes (dll)
+
+ ;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
+
+ (let* ((result nil)
+ (dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (setq result (cons node result))
+ (setq node (elib-node-left node)))
+
+ result))
+
+(defun dll-set-from-node-list (dll list)
+
+ ;; Set the contents of DLL to the nodes in LIST.
+ ;; INTERNAL USE ONLY.
+
+ (dll-clear dll)
+ (let* ((dummy (dll-get-dummy-node dll))
+ (left dummy))
+ (while list
+ (elib-node-set-left (car list) left)
+ (elib-node-set-right left (car list))
+ (setq left (car list))
+ (setq list (cdr list)))
+
+ (elib-node-set-right left dummy)
+ (elib-node-set-left dummy left)))
+
+
+;;; ===================================================================
+;;; The public functions which operate on doubly linked lists.
+
+(defmacro dll-element (dll node)
+
+ "Get the element of a NODE in a doubly linked list DLL.
+Args: DLL NODE."
+
+ (` (elib-node-data (, node))))
+
+
+(defun dll-create ()
+ "Create an empty doubly linked list."
+ (let ((dummy-node (elib-node-create nil nil nil)))
+ (elib-node-set-right dummy-node dummy-node)
+ (elib-node-set-left dummy-node dummy-node)
+ (cons 'DL-LIST dummy-node)))
+
+(defun dll-p (object)
+ "Return t if OBJECT is a doubly linked list, otherwise return nil."
+ (eq (car-safe object) 'DL-LIST))
+
+(defun dll-enter-first (dll element)
+ "Add an element first on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-enter-after
+ dll
+ (dll-get-dummy-node dll)
+ element))
+
+
+(defun dll-enter-last (dll element)
+ "Add an element last on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-enter-before
+ dll
+ (dll-get-dummy-node dll)
+ element))
+
+
+(defun dll-enter-after (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
+Args: DLL NODE ELEMENT."
+
+ (let ((new-node (elib-node-create
+ node (elib-node-right node)
+ element)))
+ (elib-node-set-left (elib-node-right node) new-node)
+ (elib-node-set-right node new-node)))
+
+
+(defun dll-enter-before (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
+Args: DLL NODE ELEMENT."
+
+ (let ((new-node (elib-node-create
+ (elib-node-left node) node
+ element)))
+ (elib-node-set-right (elib-node-left node) new-node)
+ (elib-node-set-left node new-node)))
+
+
+
+(defun dll-next (dll node)
+ "Return the node after NODE, or nil if NODE is the last node.
+Args: DLL NODE."
+
+ (if (eq (elib-node-right node) (dll-get-dummy-node dll))
+ nil
+ (elib-node-right node)))
+
+
+(defun dll-previous (dll node)
+ "Return the node before NODE, or nil if NODE is the first node.
+Args: DLL NODE."
+
+ (if (eq (elib-node-left node) (dll-get-dummy-node dll))
+ nil
+ (elib-node-left node)))
+
+
+(defun dll-delete (dll node)
+
+ "Delete NODE from the doubly linked list DLL.
+Args: DLL NODE. Return the element of node."
+
+ ;; This is a no-op when applied to the dummy node. This will return
+ ;; nil if applied to the dummy node since it always contains nil.
+
+ (elib-node-set-right (elib-node-left node) (elib-node-right node))
+ (elib-node-set-left (elib-node-right node) (elib-node-left node))
+ (dll-element dll node))
+
+
+
+(defun dll-delete-first (dll)
+
+ "Delete the first NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (dll-delete dll (elib-node-right (dll-get-dummy-node dll))))
+
+
+(defun dll-delete-last (dll)
+
+ "Delete the last NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (dll-delete dll (elib-node-left (dll-get-dummy-node dll))))
+
+
+(defun dll-first (dll)
+
+ "Return the first element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (if (eq (elib-node-right (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll))
+ nil
+ (elib-node-data (elib-node-right (dll-get-dummy-node dll)))))
+
+
+
+
+(defun dll-last (dll)
+
+ "Return the last element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (if (eq (elib-node-left (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll))
+ nil
+ (elib-node-data (elib-node-left (dll-get-dummy-node dll)))))
+
+
+
+(defun dll-nth (dll n)
+
+ "Return the Nth node from the doubly linked list DLL.
+ Args: DLL N
+N counts from zero. If DLL is not that long, nil is returned.
+If N is negative, return the -(N+1)th last element.
+Thus, (dll-nth dll 0) returns the first node,
+and (dll-nth dll -1) returns the last node."
+
+ ;; Branch 0 ("follow left pointer") is used when n is negative.
+ ;; Branch 1 ("follow right pointer") is used otherwise.
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (branch (if (< n 0) 0 1))
+ (node (elib-node-branch dummy branch)))
+
+ (if (< n 0)
+ (setq n (- -1 n)))
+
+ (while (and (not (eq dummy node))
+ (> n 0))
+ (setq node (elib-node-branch node branch))
+ (setq n (1- n)))
+
+ (if (eq dummy node)
+ nil
+ node)))
+
+
+(defun dll-empty (dll)
+
+ "Return t if the doubly linked list DLL is empty, nil otherwise"
+
+ (eq (elib-node-left (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll)))
+
+(defun dll-length (dll)
+
+ "Returns the number of elements in the doubly linked list DLL."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy))
+ (n 0))
+
+ (while (not (eq node dummy))
+ (setq node (elib-node-right node))
+ (setq n (1+ n)))
+
+ n))
+
+
+
+(defun dll-copy (dll &optional element-copy-fnc)
+
+ "Return a copy of the doubly linked list DLL.
+If optional second argument ELEMENT-COPY-FNC is non-nil it should be
+a function that takes one argument, an element, and returns a copy of it.
+If ELEMENT-COPY-FNC is not given the elements are not copied."
+
+ (let ((result (dll-create))
+ (node (dll-nth dll 0)))
+ (if element-copy-fnc
+
+ ;; Copy the elements with the user-supplied function.
+ (while node
+ (dll-enter-last result
+ (funcall element-copy-fnc
+ (dll-element dll node)))
+ (setq node (dll-next dll node)))
+
+ ;; Don't try to copy the elements - they might be
+ ;; circular lists, or anything at all...
+ (while node
+ (dll-enter-last result (dll-element dll node))
+ (setq node (dll-next dll node))))
+
+ result))
+
+
+
+(defun dll-all (dll)
+
+ "Return all elements on the double linked list DLL as an ordinary list."
+
+ (let* ((result nil)
+ (dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (setq result (cons (dll-element dll node) result))
+ (setq node (elib-node-left node)))
+
+ result))
+
+
+(defun dll-clear (dll)
+
+ "Clear the doubly linked list DLL, i.e. make it completely empty."
+
+ (elib-node-set-left (dll-get-dummy-node dll) (dll-get-dummy-node dll))
+ (elib-node-set-right (dll-get-dummy-node dll) (dll-get-dummy-node dll)))
+
+
+(defun dll-map (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the first element first."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy)))
+
+ (while (not (eq node dummy))
+ (funcall map-function (dll-element dll node))
+ (setq node (elib-node-right node)))))
+
+
+(defun dll-map-reverse (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the last element first."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (funcall map-function (dll-element dll node))
+ (setq node (elib-node-left node)))))
+
+
+(defun dll-create-from-list (list)
+
+ "Given an elisp LIST create a doubly linked list with the same elements."
+
+ (let ((dll (dll-create)))
+ (while list
+ (dll-enter-last dll (car list))
+ (setq list (cdr list)))
+ dll))
+
+
+
+(defun dll-sort (dll predicate)
+
+ "Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
+Returns the sorted list. DLL is modified by side effects.
+PREDICATE is called with two elements of DLL, and should return T
+if the first element is \"less\" than the second."
+
+ (dll-set-from-node-list
+ dll (sort (dll-list-nodes dll)
+ (function (lambda (x1 x2)
+ (funcall predicate
+ (dll-element dll x1)
+ (dll-element dll x2))))))
+ dll)
+
+
+(defun dll-filter (dll predicate)
+
+ "Remove all elements in the doubly linked list DLL for which PREDICATE
+return nil."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy))
+ next)
+
+ (while (not (eq node dummy))
+ (setq next (elib-node-right node))
+ (if (funcall predicate (dll-element dll node))
+ nil
+ (dll-delete dll node))
+ (setq node next))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el
new file mode 100644
index 000000000000..6c476a35ef3d
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el
@@ -0,0 +1,89 @@
+;;;; elib-node.el,v 1.2 1992/04/07 20:49:16 berliner Exp
+;;;; This file implements the nodes used in binary trees and
+;;;; doubly linked lists
+;;;;
+;;;; Copyright (C) 1991 Inge Wallin
+;;;;
+;;;; This file is part of the GNU Emacs lisp library, Elib.
+;;;;
+;;;; GNU Elib is free software; you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation; either version 1, or (at your option)
+;;;; any later version.
+;;;;
+;;;; GNU Elib 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 Emacs; see the file COPYING. If not, write to
+;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;;;
+;;;; Author: Inge Wallin
+;;;;
+
+;;;
+;;; A node is implemented as an array with three elements, using
+;;; (elt node 0) as the left pointer
+;;; (elt node 1) as the right pointer
+;;; (elt node 2) as the data
+;;;
+;;; Some types of trees, e.g. AVL trees, need bigger nodes, but
+;;; as long as the first three parts are the left pointer, the
+;;; right pointer and the data field, these macros can be used.
+;;;
+
+
+(provide 'elib-node)
+
+
+(defmacro elib-node-create (left right data)
+ "Create a tree node from LEFT, RIGHT and DATA."
+ (` (vector (, left) (, right) (, data))))
+
+
+(defmacro elib-node-left (node)
+ "Return the left pointer of NODE."
+ (` (aref (, node) 0)))
+
+
+(defmacro elib-node-right (node)
+ "Return the right pointer of NODE."
+ (` (aref (, node) 1)))
+
+
+(defmacro elib-node-data (node)
+ "Return the data of NODE."
+ (` (aref (, node) 2)))
+
+
+(defmacro elib-node-set-left (node newleft)
+ "Set the left pointer of NODE to NEWLEFT."
+ (` (aset (, node) 0 (, newleft))))
+
+
+(defmacro elib-node-set-right (node newright)
+ "Set the right pointer of NODE to NEWRIGHT."
+ (` (aset (, node) 1 (, newright))))
+
+
+(defmacro elib-node-set-data (node newdata)
+ "Set the data of NODE to NEWDATA."
+ (` (aset (, node) 2 (, newdata))))
+
+
+
+(defmacro elib-node-branch (node branch)
+ "Get value of a branch of a node.
+NODE is the node, and BRANCH is the branch.
+0 for left pointer, 1 for right pointer and 2 for the data."
+ (` (aref (, node) (, branch))))
+
+
+(defmacro elib-node-set-branch (node branch newval)
+ "Set value of a branch of a node.
+NODE is the node, and BRANCH is the branch.
+0 for left pointer, 1 for the right pointer and 2 for the data.
+NEWVAL is new value of the branch."
+ (` (aset (, node) (, branch) (, newval))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el
new file mode 100644
index 000000000000..27bb57cddd84
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el
@@ -0,0 +1,6 @@
+;;; pcl-cvs-startup.el,v 1.2 1992/04/07 20:49:17 berliner Exp
+(autoload 'cvs-update "pcl-cvs"
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer and run cvs-mode on it.
+If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+ t)
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el
new file mode 100644
index 000000000000..99da3695ad7d
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el
@@ -0,0 +1,1476 @@
+;;; pcl-cvs.el,v 1.2 1992/04/07 20:49:19 berliner Exp
+;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later. Release 1.02.
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; 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.
+
+;;;; See below for installation instructions.
+;;;;
+;;;; There is an TeXinfo file that describes this package. The GNU
+;;;; General Public License is included in that file. You should read
+;;;; it to get the most from this package.
+
+;;; Don't try to use this with CVS 1.2 or earlier. It won't work. Get
+;;; CVS 1.3.
+
+;;; Mail questions and bug reports to ceder@lysator.liu.se.
+
+(require 'cookie)
+(provide 'pcl-cvs)
+
+;;; -------------------------------------------------------
+;;; START OF THINGS TO CHECK WHEN INSTALLING
+
+(defvar cvs-program "/usr/gnu/bin/cvs"
+ "*Full path to the cvs executable.")
+
+(defvar cvs-diff-program "/usr/gnu/bin/diff"
+ "*Full path to the diff program.")
+
+(defvar cvs-rm-program "/usr/gnu/bin/rm"
+ "*Full path to the rm program. Typically /bin/rm.")
+
+;; Uncomment the following line if you are running on 18.57 or earlier.
+;(setq delete-exited-processes nil)
+;; Emacs version 18.57 and earlier is likely to crash if
+;; delete-exited-processes is t, since the sentinel uses lots of
+;; memory, and 18.57 forgets to GCPROT a variable if
+;; delete-exited-processes is t.
+
+;;; END OF THINGS TO CHECK WHEN INSTALLING
+;;; --------------------------------------------------------
+
+(defvar cvs-bakprefix ".#"
+ "The prefix that CVS prepends to files when rcsmerge'ing.")
+
+(defvar cvs-erase-input-buffer nil
+ "*Non-nil if input buffers should be cleared before asking for new info.")
+
+(defvar cvs-auto-remove-handled nil
+ "*Non-nil if cvs-remove-handled should be called automatically.
+If this is set to any non-nil value entries that does not need to be
+checked in will be removed from the *cvs* buffer after every cvs-commit
+command.")
+
+(defconst cvs-cursor-column 14
+ "Column to position cursor in in cvs-mode.
+Column 0 is left-most column.")
+
+(defvar cvs-mode-map nil
+ "Keymap for the cvs mode.")
+
+(defvar cvs-edit-mode-map nil
+ "Keymap for the cvs edit mode (used when editing cvs log messages).")
+
+(defvar cvs-buffer-name "*cvs*"
+ "Name of the cvs buffer.")
+
+(defvar cvs-commit-prompt-buffer "*cvs-commit-message*"
+ "Name of buffer in which the user is prompted for a log message when
+committing files.")
+
+(defvar cvs-temp-buffer-name "*cvs-tmp*"
+ "*Name of the cvs temporary buffer.
+Output from cvs is placed here by synchronous commands.")
+
+(defvar cvs-cvs-diff-flags nil
+ "*List of strings to use as flags to pass to ``cvs diff''.
+Used by cvs-diff-cvs.
+Set this to '("-u") to get a Unidiff format, or '("-c") to get context diffs.")
+
+(defvar cvs-status-flags nil
+ "*List of strings to pass to ``cvs status''.")
+
+(defvar cvs-log-flags nil
+ "*List of strings to pass to ``cvs log''.")
+
+(defvar cvs-diff-flags nil
+ "*List of strings to use as flags to pass to ``diff''.
+Do not confuse with cvs-cvs-diff-flags. Used by cvs-diff-backup.")
+
+(defvar cvs-buffers-to-delete nil
+ "List of temporary buffers that should be discarded as soon as possible.
+Due to a bug in emacs 18.57 the sentinel can't discard them reliably.")
+
+;; You are NOT allowed to disable this message by default. However, you
+;; are encouraged to inform your users that by adding
+;; (setq cvs-inhibit-copyright-message t)
+;; to their .emacs they can get rid of it. Just don't add that line
+;; to your default.el!
+(defvar cvs-inhibit-copyright-message nil
+ "*Don't display a Copyright message in the ``*cvs*'' buffer.")
+
+(defvar cvs-startup-message
+ (if cvs-inhibit-copyright-message
+ "PCL-CVS release 1.02"
+ "PCL-CVS release 1.02. Copyright (C) 1992 Per Cederqvist
+Pcl-cvs comes with absolutely no warranty; for details consult the manual.
+This is free software, and you are welcome to redistribute it under certain
+conditions; again, consult the TeXinfo manual for details.")
+ "*Startup message for CVS.")
+
+(defvar cvs-cvs-buffer nil
+ "Internal to pcl-cvs.el.
+This variable exists in the *cvs-commit-message* buffer and names
+the *cvs* buffer.")
+
+;;; The cvs data structure:
+;;;
+;;; When the `cvs update' is ready we parse the output. Every file
+;;; that is affected in some way is added as a cookie of fileinfo
+;;; (as defined below).
+;;;
+
+;;; cvs-fileinfo
+;;;
+;;; marked t/nil
+;;; type One of
+;;; UPDATED - file copied from repository
+;;; MODIFIED - modified by you, unchanged in
+;;; repository
+;;; ADDED - added by you, not yet committed
+;;; REMOVED - removed by you, not yet committed
+;;; CVS-REMOVED- removed, since file no longer exists
+;;; in the repository.
+;;; MERGED - successful merge
+;;; CONFLICT - conflict when merging
+;;; REM-CONFLICT-removed in repository, changed locally.
+;;; MOD-CONFLICT-removed locally, changed in repository.
+;;; DIRCHANGE - A change of directory.
+;;; UNKNOWN - An unknown file.
+;;; MOVE-AWAY - A file that is in the way.
+;;; REPOS-MISSING- The directory is removed from the
+;;; repository. Go fetch a backup.
+;;; dir Directory the file resides in. Should not end with
+;;; slash.
+;;; file-name The file name.
+;;; backup-file Name of the backup file if MERGED or CONFLICT.
+;;; cvs-diff-buffer A buffer that contains a 'cvs diff file'.
+;;; backup-diff-buffer A buffer that contains a 'diff file backup-file'.
+;;; full-log The output from cvs, unparsed.
+;;; mod-time Modification time of file used for *-diff-buffer.
+;;; handled True if this file doesn't require further action.
+;;;
+;;; Constructor:
+
+;;; cvs-fileinfo
+
+;;; Constructor:
+
+(defun cvs-create-fileinfo (type
+ dir
+ file-name
+ full-log)
+ "Create a fileinfo from all parameters.
+Arguments: TYPE DIR FILE-NAME FULL-LOG.
+A fileinfo has the following fields:
+
+ marked t/nil
+ type One of
+ UPDATED - file copied from repository
+ MODIFIED - modified by you, unchanged in
+ repository
+ ADDED - added by you, not yet committed
+ REMOVED - removed by you, not yet committed
+ CVS-REMOVED- removed, since file no longer exists
+ in the repository.
+ MERGED - successful merge
+ CONFLICT - conflict when merging
+ REM-CONFLICT-removed in repository, but altered
+ locally.
+ MOD-CONFLICT-removed locally, changed in repository.
+ DIRCHANGE - A change of directory.
+ UNKNOWN - An unknown file.
+ MOVE-AWAY - A file that is in the way.
+ REPOS-MISSING- The directory has vanished from the
+ repository.
+ dir Directory the file resides in. Should not end with slash.
+ file-name The file name.
+ backup-file Name of the backup file if MERGED or CONFLICT.
+ cvs-diff-buffer A buffer that contains a 'cvs diff file'.
+ backup-diff-buffer A buffer that contains a 'diff file backup-file'.
+ full-log The output from cvs, unparsed.
+ mod-time Modification time of file used for *-diff-buffer.
+ handled True if this file doesn't require further action."
+ (cons
+ 'CVS-FILEINFO
+ (vector nil nil type dir file-name nil nil nil full-log nil)))
+
+
+;;; Selectors:
+
+(defun cvs-fileinfo->handled (cvs-fileinfo)
+ "Get the `handled' field from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 0))
+
+(defun cvs-fileinfo->marked (cvs-fileinfo)
+ "Check if CVS-FILEINFO is marked."
+ (elt (cdr cvs-fileinfo) 1))
+
+(defun cvs-fileinfo->type (cvs-fileinfo)
+ "Get type from CVS-FILEINFO.
+Type is one of UPDATED, MODIFIED, ADDED, REMOVED, CVS-REMOVED, MERGED,
+CONFLICT, REM-CONFLICT, MOD-CONFLICT, DIRCHANGE, UNKNOWN, MOVE-AWAY
+or REPOS-MISSING."
+ (elt (cdr cvs-fileinfo) 2))
+
+(defun cvs-fileinfo->dir (cvs-fileinfo)
+ "Get dir from CVS-FILEINFO.
+The directory name does not end with a slash. "
+ (elt (cdr cvs-fileinfo) 3))
+
+(defun cvs-fileinfo->file-name (cvs-fileinfo)
+ "Get file-name from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 4))
+
+(defun cvs-fileinfo->backup-file (cvs-fileinfo)
+ "Get backup-file from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 5))
+
+(defun cvs-fileinfo->cvs-diff-buffer (cvs-fileinfo)
+ "Get cvs-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 6))
+
+(defun cvs-fileinfo->backup-diff-buffer (cvs-fileinfo)
+ "Get backup-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 7))
+
+(defun cvs-fileinfo->full-log (cvs-fileinfo)
+ "Get full-log from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 8))
+
+(defun cvs-fileinfo->mod-time (cvs-fileinfo)
+ "Get mod-time from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 9))
+
+;;; Modifiers:
+
+(defun cvs-set-fileinfo->handled (cvs-fileinfo newval)
+ "Set handled in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 0 newval))
+
+(defun cvs-set-fileinfo->marked (cvs-fileinfo newval)
+ "Set marked in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 1 newval))
+
+(defun cvs-set-fileinfo->type (cvs-fileinfo newval)
+ "Set type in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 2 newval))
+
+(defun cvs-set-fileinfo->dir (cvs-fileinfo newval)
+ "Set dir in CVS-FILEINFO to NEWVAL.
+The directory should now end with a slash."
+ (aset (cdr cvs-fileinfo) 3 newval))
+
+(defun cvs-set-fileinfo->file-name (cvs-fileinfo newval)
+ "Set file-name in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 4 newval))
+
+(defun cvs-set-fileinfo->backup-file (cvs-fileinfo newval)
+ "Set backup-file in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 5 newval))
+
+(defun cvs-set-fileinfo->cvs-diff-buffer (cvs-fileinfo newval)
+ "Set cvs-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 6 newval))
+
+(defun cvs-set-fileinfo->backup-diff-buffer (cvs-fileinfo newval)
+ "Set backup-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 7 newval))
+
+(defun cvs-set-fileinfo->full-log (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 8 newval))
+
+(defun cvs-set-fileinfo->mod-time (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 9 newval))
+
+
+
+;;; Predicate:
+
+(defun cvs-fileinfo-p (object)
+ "Return t if OBJECT is a cvs-fileinfo."
+ (eq (car-safe object) 'CVS-FILEINFO))
+
+;;;; End of types.
+
+(defun cvs-use-temp-buffer ()
+ "Display a temporary buffer in another window and select it.
+The selected window will not be changed. The temporary buffer will
+be erased and writable."
+
+ (display-buffer (get-buffer-create cvs-temp-buffer-name))
+ (set-buffer cvs-temp-buffer-name)
+ (setq buffer-read-only nil)
+ (erase-buffer))
+
+; Too complicated to handle all the cases that are generated.
+; Maybe later.
+;(defun cvs-examine (directory &optional local)
+; "Run a 'cvs -n update' in the current working directory.
+;That is, check what needs to be done, but don't change the disc.
+;Feed the output to a *cvs* buffer and run cvs-mode on it.
+;If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+; (interactive (list (read-file-name "CVS Update (directory): "
+; nil default-directory nil)
+; current-prefix-arg))
+; (cvs-do-update directory local 'noupdate))
+
+(defun cvs-update (directory &optional local)
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer and run cvs-mode on it.
+If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+ (interactive (list (read-file-name "CVS Update (directory): "
+ nil default-directory nil)
+ current-prefix-arg))
+ (cvs-do-update directory local nil))
+
+(defun cvs-filter (predicate list &rest extra-args)
+ "Apply PREDICATE to each element on LIST.
+Args: PREDICATE LIST &rest EXTRA-ARGS.
+Return a new list consisting of those elements that PREDICATE
+returns non-nil for.
+
+If more than two arguments are given the remaining args are
+passed to PREDICATE."
+ ;; Avoid recursion - this should work for LONG lists also!
+ (let* ((head (cons 'dummy-header nil))
+ (tail head))
+ (while list
+ (if (apply predicate (car list) extra-args)
+ (setq tail (setcdr tail (list (car list)))))
+ (setq list (cdr list)))
+ (cdr head)))
+
+(defun cvs-update-no-prompt ()
+ "Run cvs update in current directory."
+ (interactive)
+ (cvs-do-update default-directory nil nil))
+
+(defun cvs-do-update (directory local dont-change-disc)
+ "Do a 'cvs update' in DIRECTORY.
+If LOCAL is non-nil 'cvs update -l' is executed.
+If DONT-CHANGE-DISC is non-nil 'cvs -n update' is executed.
+Both LOCAL and DONT-CHANGE-DISC may be non-nil simultaneously.
+
+*Note*: DONT-CHANGE-DISC does not yet work. The parser gets confused."
+ (save-some-buffers)
+ (let* ((this-dir (file-name-as-directory (expand-file-name directory)))
+ (use-this-window (equal (buffer-name (current-buffer))
+ cvs-buffer-name))
+ (update-buffer (generate-new-buffer
+ (concat (file-name-nondirectory
+ (substring this-dir 0 -1))
+ "-update")))
+ cvs-process args)
+
+ ;; The *cvs* buffer is killed to avoid confusion - is the update ready
+ ;; or not?
+ (if (get-buffer cvs-buffer-name)
+ (kill-buffer cvs-buffer-name))
+
+ ;; Generate "-n update -l".
+ (if local (setq args (list "-l")))
+ (setq args (cons "update" args))
+ (if dont-change-disc (setq args (cons "-n" args)))
+
+ ;; Set up the buffer that receives the output from "cvs update".
+ (if use-this-window
+ (switch-to-buffer update-buffer)
+ (set-buffer update-buffer)
+ (display-buffer update-buffer))
+
+ (setq default-directory this-dir)
+ (setq cvs-process
+ (let ((process-connection-type nil)) ; Use a pipe, not a pty.
+ (apply 'start-process "cvs" update-buffer cvs-program args)))
+
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status cvs-process))))
+ (set-buffer-modified-p (buffer-modified-p)) ; Update the mode line.
+ (set-process-sentinel cvs-process 'cvs-sentinel)
+
+ ;; Work around a bug in emacs 18.57 and earlier.
+ (setq cvs-buffers-to-delete
+ (cvs-delete-unused-temporary-buffers cvs-buffers-to-delete))))
+
+(defun cvs-delete-unused-temporary-buffers (list)
+ "Delete all buffers on LIST that is not visible.
+Return a list of all buffers that still is alive."
+
+ (cond
+ ((null list) nil)
+ ((get-buffer-window (car list))
+ (cons (car list)
+ (cvs-delete-unused-temporary-buffers (cdr list))))
+ (t
+ (kill-buffer (car list))
+ (cvs-delete-unused-temporary-buffers (cdr list)))))
+
+
+(put 'cvs-mode 'mode-class 'special)
+
+(defun cvs-mode ()
+ "\\<cvs-mode-map>Mode used for pcl-cvs, a frontend to CVS.
+
+To get the *cvs* buffer you should use ``\\[cvs-update]''.
+
+Full documentation is in the TeXinfo file. These are the most useful commands:
+
+\\[cookie-previous-cookie] Move up. \\[cookie-next-cookie] Move down.
+\\[cvs-commit] Commit file. \\[cvs-update-no-prompt] Reupdate directory.
+\\[cvs-mark] Mark file/dir. \\[cvs-unmark] Unmark file/dir.
+\\[cvs-mark-all-files] Mark all files. \\[cvs-unmark-all-files] Unmark all files.
+\\[cvs-find-file] Edit file/run Dired. \\[cvs-find-file-other-window] Find file or run Dired in other window.
+\\[cvs-remove-handled] Remove processed entries. \\[cvs-add-change-log-entry-other-window] Write ChangeLog in other window.
+\\[cvs-add] Add to repository. \\[cvs-remove-file] Remove file.
+\\[cvs-diff-cvs] Diff between base revision. \\[cvs-diff-backup] Diff backup file.
+\\[cvs-acknowledge] Delete line from buffer. \\[cvs-ignore] Add file to the .cvsignore file.
+\\[cvs-log] Run ``cvs log''. \\[cvs-status] Run ``cvs status''.
+
+Entry to this mode runs cvs-mode-hook.
+This description is updated for release 1.02 of pcl-cvs.
+All bindings:
+\\{cvs-mode-map}"
+ (interactive)
+ (setq major-mode 'cvs-mode)
+ (setq mode-name "CVS")
+ (setq buffer-read-only nil)
+ (buffer-flush-undo (current-buffer))
+ (make-local-variable 'goal-column)
+ (setq goal-column cvs-cursor-column)
+ (use-local-map cvs-mode-map)
+ (run-hooks 'cvs-mode-hook))
+
+(defun cvs-sentinel (proc msg)
+ "Sentinel for the cvs update process.
+This is responsible for parsing the output from the cvs update when
+it is finished."
+ (cond
+ ((null (buffer-name (process-buffer proc)))
+ ;; buffer killed
+ (set-process-buffer proc nil))
+ ((memq (process-status proc) '(signal exit))
+ (let* ((obuf (current-buffer))
+ (omax (point-max))
+ (opoint (point)))
+ ;; save-excursion isn't the right thing if
+ ;; process-buffer is current-buffer
+ (unwind-protect
+ (progn
+ (set-buffer (process-buffer proc))
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status proc))))
+ (cvs-parse-buffer)
+ (setq cvs-buffers-to-delete
+ (cons (process-buffer proc) cvs-buffers-to-delete)))
+ (set-buffer-modified-p (buffer-modified-p)))
+ (if (equal obuf (process-buffer proc))
+ nil
+ (set-buffer (process-buffer proc))
+ (if (< opoint omax)
+ (goto-char opoint))
+ (set-buffer obuf))))))
+
+(defun cvs-skip-line (regexp errormsg &optional arg)
+ "Like forward-line, but check that the skipped line matches REGEXP.
+If it doesn't match REGEXP (error ERRORMSG) is called.
+If optional ARG, a number, is given the ARGth parenthesized expression
+in the REGEXP is returned as a string.
+Point should be in column 1 when this function is called."
+ (cond
+ ((looking-at regexp)
+ (forward-line 1)
+ (if arg
+ (buffer-substring (match-beginning arg)
+ (match-end arg))))
+ (t
+ (error errormsg))))
+
+(defun cvs-get-current-dir (dirname)
+ "Return current working directory, suitable for cvs-parse-buffer.
+Args: DIRNAME.
+Concatenates default-directory and DIRNAME to form an absolute path."
+ (if (string= "." dirname)
+ (substring default-directory 0 -1)
+ (concat default-directory dirname)))
+
+
+(defun cvs-parse-buffer ()
+ "Parse the current buffer and select a *cvs* buffer.
+Signals an error if unexpected output was detected in the buffer."
+ (goto-char (point-min))
+ (let ((buf (get-buffer-create cvs-buffer-name))
+ (current-dir default-directory)
+ (root-dir default-directory)
+ (parse-buf (current-buffer)))
+
+ (cookie-create
+ buf 'cvs-pp cvs-startup-message ;Se comment above cvs-startup-message.
+ "---------- End -----")
+
+ (cookie-enter-first
+ buf
+ (cvs-create-fileinfo
+ 'DIRCHANGE current-dir
+ nil ""))
+
+ (while (< (point) (point-max))
+ (cond
+
+ ;; CVS is descending a subdirectory.
+
+ ((looking-at "cvs update: Updating \\(.*\\)$")
+ (setq current-dir
+ (cvs-get-current-dir
+ (buffer-substring (match-beginning 1) (match-end 1))))
+
+ ;; Omit empty directories.
+ (if (eq (cvs-fileinfo->type (cookie-last buf))
+ 'DIRCHANGE)
+ (cookie-delete-last buf))
+
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'DIRCHANGE current-dir
+ nil (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; File removed, since it is removed (by third party) in repository.
+
+ ((or (looking-at "cvs update: warning: \\(.*\\) is not (any longer) \
+pertinent")
+ (looking-at "cvs update: \\(.*\\) is no longer in the repository"))
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'CVS-REMOVED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; File removed by you, but recreated by cvs. Ignored.
+
+ ((looking-at "cvs update: warning: .* was lost$")
+ (forward-line 1))
+
+ ;; A file that has been created by you, but added to the cvs
+ ;; repository by another.
+
+ ((looking-at "^cvs update: move away \\(.*\\); it is in the way$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'MOVE-AWAY current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; Empty line. Probably inserted by mistake by user (or developer :-)
+ ;; Ignore.
+
+ ((looking-at "^$")
+ (forward-line 1))
+
+ ;; Cvs waits for a lock. Ignore.
+
+ ((looking-at
+ "^cvs update: \\[..:..:..\\] waiting for .*lock in ")
+ (forward-line 1))
+
+ ;; File removed in repository, but edited by you.
+
+ ((looking-at
+ "cvs update: conflict: \\(.*\\) is modified but no longer \
+in the repository$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REM-CONFLICT current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ((looking-at
+ "cvs update: conflict: removed \\(.*\\) was modified by second party")
+ (cvs-create-fileinfo
+ 'MOD-CONFLICT current-dir
+ (buffer-substring (match-beginning 1) (match-end 1))
+ (buffer-substring (match-beginning 0) (match-end 0)))
+ (forward-line 1))
+
+ ((looking-at "cvs update: in directory ")
+ (let ((start (point)))
+ (forward-line 1)
+ (cvs-skip-line
+ (regexp-quote "cvs [update aborted]: there is no repository ")
+ "Unexpected cvs output.")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REPOS-MISSING current-dir
+ nil
+ (buffer-substring start (point))))))
+
+ ;; The file is copied from the repository.
+
+ ((looking-at "U \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'UPDATED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0)))))
+ (cvs-set-fileinfo->handled fileinfo t)
+ fileinfo))
+ (forward-line 1))
+
+ ;; The file is modified by the user, and untouched in the repository.
+
+ ((looking-at "M \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'MODIFIED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; The file is "cvs add"ed, but not "cvs ci"ed.
+
+ ((looking-at "A \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'ADDED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; The file is "cvs remove"ed, but not "cvs ci"ed.
+
+ ((looking-at "R \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REMOVED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; Unknown file.
+
+ ((looking-at "? \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'UNKNOWN current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+ (t
+
+ ;; CVS has decided to merge someone elses changes into this
+ ;; document. This leads to a lot of garbage being printed.
+ ;; First there is two lines that contains no information
+ ;; that we skip (but we check that we recognize them).
+
+ (let ((complex-start (point))
+ initial-revision filename)
+
+ (cvs-skip-line "^RCS file: .*$" "Parse error.")
+ (setq initial-revision
+ (cvs-skip-line "^retrieving revision \\(.*\\)$"
+ "Unexpected output from cvs." 1))
+ (cvs-skip-line "^retrieving revision .*$"
+ "Unexpected output from cvs.")
+
+ ;; Get the file name from the next line.
+
+ (setq
+ filename
+ (cvs-skip-line
+ "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$"
+ "Unexpected output from cvs."
+ 1))
+
+ (cond
+
+ ;; The file was successfully merged.
+
+ ((looking-at "^M ")
+ (forward-line 1)
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'MERGED current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+ (cvs-set-fileinfo->backup-file
+ fileinfo
+ (concat cvs-bakprefix filename "." initial-revision))
+ (cookie-enter-last
+ buf fileinfo)))
+
+ ;; A conflicting merge.
+
+ (t
+ (cvs-skip-line "^merge: overlaps during merge$"
+ "Unexpected output from cvs.")
+ (cvs-skip-line "^cvs update: conflicts found in "
+ "Unexpected output from cvs.")
+ (cvs-skip-line "^C " "Unexpected cvs output.")
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'CONFLICT current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+
+ (cvs-set-fileinfo->backup-file
+ fileinfo
+ (concat cvs-bakprefix filename "." initial-revision))
+
+ (cookie-enter-last buf fileinfo))))))))
+
+ ;; All parsing is done.
+
+ ;; If the last entry is a directory, remove it.
+ (if (eq (cvs-fileinfo->type (cookie-last buf))
+ 'DIRCHANGE)
+ (cookie-delete-last buf))
+
+ (set-buffer buf)
+ (cvs-mode)
+ (setq cookie-last-tin (cookie-nth buf 0))
+ (goto-char (point-min))
+ (cookie-previous-cookie buf (point-min) 1)
+ (setq default-directory root-dir)
+ (if (get-buffer-window parse-buf)
+ (set-window-buffer (get-buffer-window parse-buf) buf)
+ (display-buffer buf))))
+
+
+(defun cvs-pp (fileinfo)
+ "Pretty print FILEINFO into a string."
+
+ (let ((a (cvs-fileinfo->type fileinfo))
+ (s (if (cvs-fileinfo->marked fileinfo)
+ "*" " "))
+ (f (cvs-fileinfo->file-name fileinfo))
+ (ci (if (cvs-fileinfo->handled fileinfo)
+ " " "ci")))
+ (cond
+ ((eq a 'UPDATED)
+ (format "%s Updated %s" s f))
+ ((eq a 'MODIFIED)
+ (format "%s Modified %s %s" s ci f))
+ ((eq a 'MERGED)
+ (format "%s Merged %s %s" s ci f))
+ ((eq a 'CONFLICT)
+ (format "%s Conflict %s" s f))
+ ((eq a 'ADDED)
+ (format "%s Added %s %s" s ci f))
+ ((eq a 'REMOVED)
+ (format "%s Removed %s %s" s ci f))
+ ((eq a 'UNKNOWN)
+ (format "%s Unknown %s" s f))
+ ((eq a 'CVS-REMOVED)
+ (format "%s Removed from repository: %s" s f))
+ ((eq a 'REM-CONFLICT)
+ (format "%s Conflict: Removed from repository, changed by you: %s" s f))
+ ((eq a 'MOD-CONFLICT)
+ (format "%s Conflict: Removed by you, changed in repository: %s" s f))
+ ((eq a 'DIRCHANGE)
+ (format "\nIn directory %s:"
+ (cvs-fileinfo->dir fileinfo)))
+ ((eq a 'MOVE-AWAY)
+ (format "%s Move away %s - it is in the way" s f))
+ ((eq a 'REPOS-MISSING)
+ (format " This repository is missing! Remove this dir manually."))
+ (t
+ (format "%s Internal error! %s" s f)))))
+
+
+;;; You can define your own keymap in .emacs. pcl-cvs.el won't overwrite it.
+
+(if cvs-mode-map
+ nil
+ (setq cvs-mode-map (make-keymap))
+ (suppress-keymap cvs-mode-map)
+ (define-key cvs-mode-map " " 'cookie-next-cookie)
+ (define-key cvs-mode-map "?" 'describe-mode)
+ (define-key cvs-mode-map "A" 'cvs-add-change-log-entry-other-window)
+ (define-key cvs-mode-map "M" 'cvs-mark-all-files)
+ (define-key cvs-mode-map "U" 'cvs-unmark-all-files)
+ (define-key cvs-mode-map "\C-?" 'cvs-unmark-up)
+ (define-key cvs-mode-map "\C-n" 'cookie-next-cookie)
+ (define-key cvs-mode-map "\C-p" 'cookie-previous-cookie)
+ (define-key cvs-mode-map "a" 'cvs-add)
+ (define-key cvs-mode-map "b" 'cvs-diff-backup)
+ (define-key cvs-mode-map "c" 'cvs-commit)
+ (define-key cvs-mode-map "d" 'cvs-diff-cvs)
+ (define-key cvs-mode-map "f" 'cvs-find-file)
+ (define-key cvs-mode-map "g" 'cvs-update-no-prompt)
+ (define-key cvs-mode-map "i" 'cvs-ignore)
+ (define-key cvs-mode-map "l" 'cvs-log)
+ (define-key cvs-mode-map "m" 'cvs-mark)
+ (define-key cvs-mode-map "n" 'cookie-next-cookie)
+ (define-key cvs-mode-map "o" 'cvs-find-file-other-window)
+ (define-key cvs-mode-map "p" 'cookie-previous-cookie)
+ (define-key cvs-mode-map "r" 'cvs-remove-file)
+ (define-key cvs-mode-map "s" 'cvs-status)
+ (define-key cvs-mode-map "\C-k" 'cvs-acknowledge)
+ (define-key cvs-mode-map "x" 'cvs-remove-handled)
+ (define-key cvs-mode-map "u" 'cvs-unmark))
+
+
+(defun cvs-get-marked ()
+ "Return a list of all selected tins.
+If there are any marked tins, return them.
+Otherwise, if the cursor selects a directory, return all files in it.
+Otherwise return (a list containing) the file the cursor points to, or
+an empty list if it doesn't point to a file at all."
+
+ (cond
+ ;; Any marked cookies?
+ ((cookie-collect-tins (current-buffer)
+ 'cvs-fileinfo->marked))
+ ;; Nope.
+ (t
+ (let ((sel (cookie-get-selection
+ (current-buffer) (point) cookie-last-tin)))
+ (cond
+ ;; If a directory is selected, all it members are returned.
+ ((and sel (eq (cvs-fileinfo->type
+ (cookie-cookie (current-buffer) sel))
+ 'DIRCHANGE))
+ (cookie-collect-tins
+ (current-buffer) 'cvs-dir-member-p
+ (cvs-fileinfo->dir (cookie-cookie (current-buffer) sel))))
+ (t
+ (list sel)))))))
+
+
+(defun cvs-dir-member-p (fileinfo dir)
+ "Return true if FILEINFO represents a file in directory DIR."
+ (and (not (eq (cvs-fileinfo->type fileinfo) 'DIRCHANGE))
+ (string= (cvs-fileinfo->dir fileinfo) dir)))
+
+(defun cvs-dir-empty-p (cvs-buf tin)
+ "Return non-nil if TIN is a directory that is empty.
+Args: CVS-BUF TIN."
+ (and (eq (cvs-fileinfo->type (cookie-cookie cvs-buf tin)) 'DIRCHANGE)
+ (or (not (cookie-next cvs-buf tin))
+ (eq (cvs-fileinfo->type (cookie-cookie cvs-buf
+ (cookie-next cvs-buf tin)))
+ 'DIRCHANGE))))
+
+(defun cvs-remove-handled ()
+ "Remove all lines that are handled.
+Empty directories are removed."
+ (interactive)
+ ;; Pass one: remove files that are handled.
+ (cookie-filter (current-buffer)
+ (function
+ (lambda (fileinfo) (not (cvs-fileinfo->handled fileinfo)))))
+ ;; Pass two: remove empty directories.
+ (cookie-filter-tins (current-buffer)
+ (function
+ (lambda (tin)
+ (not (cvs-dir-empty-p (current-buffer) tin))))))
+
+(defun cvs-mark (pos)
+ "Mark a fileinfo. Args: POS.
+If the fileinfo is a directory, all the contents of that directory are
+marked instead. A directory can never be marked.
+POS is a buffer position."
+
+ (interactive "d")
+
+ (let* ((tin (cookie-get-selection
+ (current-buffer) pos cookie-last-tin))
+ (sel (cookie-cookie (current-buffer) tin)))
+
+ (cond
+ ;; Does POS point to a directory? If so, mark all files in that directory.
+ ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
+ (cookie-map
+ (function (lambda (f dir)
+ (cond
+ ((cvs-dir-member-p f dir)
+ (cvs-set-fileinfo->marked f t)
+ t)))) ;Tell cookie to redisplay this cookie.
+ (current-buffer)
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel t)
+ (cookie-invalidate-tins (current-buffer) tin)
+ (cookie-next-cookie (current-buffer) pos 1)))))
+
+
+(defun cvs-committable (tin cvs-buf)
+ "Check if the TIN is committable.
+It is committable if it
+ a) is not handled and
+ b) is either MODIFIED, ADDED, REMOVED, MERGED or CONFLICT."
+ (let* ((fileinfo (cookie-cookie cvs-buf tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (and (not (cvs-fileinfo->handled fileinfo))
+ (or (eq type 'MODIFIED)
+ (eq type 'ADDED)
+ (eq type 'REMOVED)
+ (eq type 'MERGED)
+ (eq type 'CONFLICT)))))
+
+(defun cvs-commit ()
+
+ "Check in all marked files, or the current file.
+The user will be asked for a log message in a buffer.
+If cvs-erase-input-buffer is non-nil that buffer will be erased.
+Otherwise mark and point will be set around the entire contents of the
+buffer so that it is easy to kill the contents of the buffer with \\[kill-region]."
+
+ (interactive)
+
+ (let* ((cvs-buf (current-buffer))
+ (marked (cvs-filter (function cvs-committable)
+ (cvs-get-marked)
+ cvs-buf)))
+ (if (null marked)
+ (error "Nothing to commit!")
+ (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer))
+ (goto-char (point-min))
+
+ (if cvs-erase-input-buffer
+ (erase-buffer)
+ (push-mark (point-max)))
+ (cvs-edit-mode)
+ (make-local-variable 'cvs-commit-list)
+ (setq cvs-commit-list marked)
+ (make-local-variable 'cvs-cvs-buffer)
+ (setq cvs-cvs-buffer cvs-buf)
+ (message "Press C-c C-c when you are done editing."))))
+
+
+(defun cvs-edit-done ()
+ "Commit the files to the repository."
+ (interactive)
+ (save-some-buffers)
+ (let ((cc-list cvs-commit-list)
+ (cc-buffer cvs-cvs-buffer)
+ (msg-buffer (current-buffer))
+ (msg (buffer-substring (point-min) (point-max))))
+ (pop-to-buffer cc-buffer)
+ (bury-buffer msg-buffer)
+ (cvs-use-temp-buffer)
+ (message "Committing...")
+ (cvs-execute-list cc-list cvs-program (list "commit" "-m" msg))
+ (mapcar (function
+ (lambda (tin)
+ (cvs-set-fileinfo->handled (cookie-cookie cc-buffer tin) t)))
+ cc-list)
+ (apply 'cookie-invalidate-tins cc-buffer cc-list)
+ (set-buffer cc-buffer)
+ (if cvs-auto-remove-handled
+ (cvs-remove-handled)))
+
+ (message "Committing... Done."))
+
+
+(defun cvs-execute-list (tin-list program constant-args)
+ "Run PROGRAM on all elements on TIN-LIST.
+Args: TIN-LIST PROGRAM CONSTANT-ARGS
+The PROGRAM will be called with pwd set to the directory the
+files reside in. CONSTANT-ARGS should be a list of strings. The
+arguments given to the program will be CONSTANT-ARGS followed by all
+the files (from TIN-LIST) that resides in that directory. If the files
+in TIN-LIST resides in different directories the PROGRAM will be run
+once for each directory (if all files in the same directory appears
+after each other."
+
+ (while tin-list
+ (let ((current-dir (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name
+ (car tin-list))))
+ arg-list arg-str)
+
+ ;; Collect all marked files in this directory.
+
+ (while (and tin-list
+ (string=
+ current-dir
+ (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name (car tin-list)))))
+ (setq arg-list
+ (cons (cvs-fileinfo->file-name
+ (cookie-cookie cvs-buffer-name (car tin-list)))
+ arg-list))
+ (setq tin-list (cdr tin-list)))
+
+ (setq arg-list (nreverse arg-list))
+
+ ;; Execute the command on all the files that were collected.
+
+ (setq default-directory (file-name-as-directory current-dir))
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat '(lambda (foo) foo)
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))
+ (goto-char (point-max)))))
+
+
+(defun cvs-execute-single-file-list (tin-list extractor program constant-args)
+ "Run PROGRAM on all elements on TIN-LIST.
+
+Args: TIN-LIST EXTRACTOR PROGRAM CONSTANT-ARGS
+
+The PROGRAM will be called with pwd set to the directory the files
+reside in. CONSTANT-ARGS is a list of strings to pass as arguments to
+PROGRAM. The arguments given to the program will be CONSTANT-ARGS
+followed by the list that EXTRACTOR returns.
+
+EXTRACTOR will be called once for each file on TIN-LIST. It is given
+one argument, the cvs-fileinfo. It can return t, which means ignore
+this file, or a list of arguments to send to the program."
+
+ (while tin-list
+ (let ((default-directory (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name
+ (car tin-list)))))
+ (arg-list
+ (funcall extractor
+ (cookie-cookie cvs-buffer-name (car tin-list)))))
+
+ ;; Execute the command unless extractor returned t.
+
+ (if (eq arg-list t)
+ nil
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat '(lambda (foo) foo)
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))
+ (goto-char (point-max))))
+ (setq tin-list (cdr tin-list))))
+
+
+(defun cvs-edit-mode ()
+ "\\<cvs-edit-mode-map>Mode for editing cvs log messages.
+Commands:
+\\[cvs-edit-done] checks in the file when you are ready.
+This mode is based on fundamental mode."
+ (interactive)
+ (use-local-map cvs-edit-mode-map)
+ (setq major-mode 'cvs-edit-mode)
+ (setq mode-name "CVS Log")
+ (auto-fill-mode 1))
+
+
+(if cvs-edit-mode-map
+ nil
+ (setq cvs-edit-mode-map (make-sparse-keymap))
+ (define-prefix-command 'cvs-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c" 'cvs-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c\C-c" 'cvs-edit-done))
+
+
+(defun cvs-diff-cvs ()
+ "Diff the selected files against the repository.
+The flags the variable cvs-cvs-diff-flags will be passed to ``cvs diff''."
+ (interactive)
+
+ (save-some-buffers)
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "cvsdiffing...")
+ (cvs-execute-list marked cvs-program (cons "diff" cvs-cvs-diff-flags)))
+ (message "cvsdiffing... Done."))
+
+
+(defun cvs-backup-diffable (tin cvs-buf)
+ "Check if the TIN is backup-diffable.
+It must have a backup file to be diffable."
+ (cvs-fileinfo->backup-file (cookie-cookie cvs-buf tin)))
+
+(defun cvs-diff-backup ()
+ "Diff the files against the backup file.
+This command can be used on files that are marked with \"Merged\"
+or \"Conflict\" in the *cvs* buffer.
+
+The flags in cvs-diff-flags will be passed to ``diff''."
+
+ (interactive)
+ (save-some-buffers)
+ (let ((marked (cvs-filter (function cvs-backup-diffable)
+ (cvs-get-marked)
+ (current-buffer))))
+ (if (null marked)
+ (error "No ``Conflict'' or ``Merged'' file selected!"))
+ (cvs-use-temp-buffer)
+ (message "diffing...")
+ (cvs-execute-single-file-list
+ marked 'cvs-diff-backup-extractor cvs-diff-program cvs-diff-flags))
+ (message "diffing... Done."))
+
+
+(defun cvs-diff-backup-extractor (fileinfo)
+ "Return the filename and the name of the backup file as a list.
+Signal an error if there is no backup file."
+ (if (null (cvs-fileinfo->backup-file fileinfo))
+ (error "%s has no backup file."
+ (concat
+ (file-name-as-directory (cvs-fileinfo->dir fileinfo))
+ (cvs-fileinfo->file-name fileinfo))))
+ (list (cvs-fileinfo->file-name fileinfo)
+ (cvs-fileinfo->backup-file fileinfo)))
+
+(defun cvs-find-file-other-window (pos)
+ "Select a buffer containing the file in another window.
+Args: POS"
+ (interactive "d")
+ (save-some-buffers)
+ (let* ((cookie-last-tin
+ (cookie-get-selection (current-buffer) pos cookie-last-tin))
+ (type (cvs-fileinfo->type (cookie-cookie (current-buffer)
+ cookie-last-tin))))
+ (cond
+ ((or (eq type 'REMOVED)
+ (eq type 'CVS-REMOVED))
+ (error "Can't visit a removed file."))
+ ((eq type 'DIRCHANGE)
+ (let ((obuf (current-buffer))
+ (odir default-directory))
+ (setq default-directory
+ (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie (current-buffer) cookie-last-tin))))
+ (dired-other-window default-directory)
+ (set-buffer obuf)
+ (setq default-directory odir)))
+ (t
+ (find-file-other-window (cvs-full-path (current-buffer)
+ cookie-last-tin))))))
+
+(defun cvs-full-path (buffer tin)
+ "Return the full path for the file that is described in TIN.
+Args: BUFFER TIN."
+ (concat
+ (file-name-as-directory
+ (cvs-fileinfo->dir (cookie-cookie buffer tin)))
+ (cvs-fileinfo->file-name (cookie-cookie buffer tin))))
+
+(defun cvs-find-file (pos)
+ "Select a buffer containing the file in another window.
+Args: POS"
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (cookie-last-tin (cookie-get-selection cvs-buf pos cookie-last-tin))
+ (fileinfo (cookie-cookie cvs-buf cookie-last-tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond
+ ((or (eq type 'REMOVED)
+ (eq type 'CVS-REMOVED))
+ (error "Can't visit a removed file."))
+ ((eq type 'DIRCHANGE)
+ (let ((odir default-directory))
+ (setq default-directory
+ (file-name-as-directory (cvs-fileinfo->dir fileinfo)))
+ (dired default-directory)
+ (set-buffer cvs-buf)
+ (setq default-directory odir)))
+ (t
+ (find-file (cvs-full-path cvs-buf cookie-last-tin))))))
+
+(defun cvs-mark-all-files ()
+ "Mark all files.
+Directories are not marked."
+ (interactive)
+ (cookie-map (function (lambda (cookie)
+ (cond
+ ((not (eq (cvs-fileinfo->type cookie) 'DIRCHANGE))
+ (cvs-set-fileinfo->marked cookie t)
+ t))))
+ (current-buffer)))
+
+
+(defun cvs-unmark (pos)
+ "Unmark a fileinfo. Args: POS."
+ (interactive "d")
+
+ (let* ((tin (cookie-get-selection
+ (current-buffer) pos cookie-last-tin))
+ (sel (cookie-cookie (current-buffer) tin)))
+
+ (cond
+ ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
+ (cookie-map
+ (function (lambda (f dir)
+ (cond
+ ((cvs-dir-member-p f dir)
+ (cvs-set-fileinfo->marked f nil)
+ t))))
+ (current-buffer)
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel nil)
+ (cookie-invalidate-tins (current-buffer) tin)
+ (cookie-next-cookie (current-buffer) pos 1)))))
+
+(defun cvs-unmark-all-files ()
+ "Unmark all files.
+Directories are also unmarked, but that doesn't matter, since
+they should always be unmarked."
+ (interactive)
+ (cookie-map (function (lambda (cookie)
+ (cvs-set-fileinfo->marked cookie nil)
+ t))
+ (current-buffer)))
+
+
+(defun cvs-do-removal (cvs-buf tins)
+ "Remove files.
+Args: CVS-BUF TINS.
+CVS-BUF is the cvs buffer. TINS is a list of tins that the
+user wants to delete. The files are deleted. If the type of
+the tin is 'UNKNOWN the tin is removed from the buffer. If it
+is anything else the file is added to a list that should be
+`cvs remove'd and the tin is changed to be of type 'REMOVED.
+
+Returns a list of tins files that should be `cvs remove'd."
+ (cvs-use-temp-buffer)
+ (mapcar 'cvs-insert-full-path tins)
+ (cond
+ ((and tins (yes-or-no-p (format "Delete %d files? " (length tins))))
+ (let (files-to-remove)
+ (while tins
+ (let* ((tin (car tins))
+ (fileinfo (cookie-cookie cvs-buf tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (if (not (or (eq type 'REMOVED) (eq type 'CVS-REMOVED)))
+ (progn
+ (delete-file (cvs-full-path cvs-buf tin))
+ (cond
+ ((or (eq type 'UNKNOWN) (eq type 'MOVE-AWAY))
+ (cookie-delete cvs-buf tin))
+ (t
+ (setq files-to-remove (cons tin files-to-remove))
+ (cvs-set-fileinfo->type fileinfo 'REMOVED)
+ (cvs-set-fileinfo->handled fileinfo nil)
+ (cookie-invalidate-tins cvs-buf tin))))))
+ (setq tins (cdr tins)))
+ files-to-remove))
+ (t nil)))
+
+
+
+(defun cvs-remove-file ()
+ "Remove all marked files."
+ (interactive)
+ (let ((files-to-remove (cvs-do-removal (current-buffer) (cvs-get-marked))))
+ (if (null files-to-remove)
+ nil
+ (cvs-use-temp-buffer)
+ (message "removing from repository...")
+ (cvs-execute-list files-to-remove cvs-program '("remove"))
+ (message "removing from repository... done."))))
+
+(defun cvs-acknowledge ()
+ "Remove all marked files from the buffer."
+ (interactive)
+
+ (mapcar (function (lambda (tin)
+ (cookie-delete (current-buffer) tin)))
+ (cvs-get-marked))
+ (setq cookie-last-tin nil))
+
+
+(defun cvs-unmark-up (pos)
+ "Unmark the file on the previous line.
+Takes one argument POS, a buffer position."
+ (interactive "d")
+ (cookie-previous-cookie (current-buffer) pos 1)
+ (cvs-set-fileinfo->marked (cookie-cookie (current-buffer) cookie-last-tin)
+ nil)
+ (cookie-invalidate-tins (current-buffer) cookie-last-tin))
+
+(defun cvs-add-file-update-buffer (cvs-buf tin)
+ "Subfunction to cvs-add. Internal use only.
+Update the display. Return non-nil if `cvs add' should be called on this
+file. Args: CVS-BUF TIN.
+Returns 'ADD or 'RESURRECT."
+ (let ((fileinfo (cookie-cookie cvs-buf tin)))
+ (cond
+ ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN)
+ (cvs-set-fileinfo->type fileinfo 'ADDED)
+ (cookie-invalidate-tins cvs-buf tin)
+ 'ADD)
+ ((eq (cvs-fileinfo->type fileinfo) 'REMOVED)
+ (cvs-set-fileinfo->type fileinfo 'UPDATED)
+ (cvs-set-fileinfo->handled fileinfo t)
+ (cookie-invalidate-tins cvs-buf tin)
+ 'RESURRECT))))
+
+(defun cvs-add-sub (cvs-buf candidates)
+ "Internal use only.
+Args: CVS-BUF CANDIDATES.
+CANDIDATES is a list of tins. Updates the CVS-BUF and returns a pair of lists.
+The first list is unknown tins that shall be `cvs add -m msg'ed.
+The second list is removed files that shall be `cvs add'ed (resurrected)."
+ (let (add resurrect)
+ (while candidates
+ (let ((type (cvs-add-file-update-buffer cvs-buf (car candidates))))
+ (cond ((eq type 'ADD)
+ (setq add (cons (car candidates) add)))
+ ((eq type 'RESURRECT)
+ (setq resurrect (cons (car candidates) resurrect)))))
+ (setq candidates (cdr candidates)))
+ (cons add resurrect)))
+
+(defun cvs-add ()
+ "Add marked files to the cvs repository."
+ (interactive)
+
+ (let* ((buf (current-buffer))
+ (result (cvs-add-sub buf (cvs-get-marked)))
+ (added (car result))
+ (resurrect (cdr result))
+ (msg (if added (read-from-minibuffer "Enter description: "))))
+
+ (if (or resurrect added)
+ (cvs-use-temp-buffer))
+
+ (cond (resurrect
+ (message "Resurrecting files from repository...")
+ (cvs-execute-list resurrect cvs-program '("add"))
+ (message "Done.")))
+
+ (cond (added
+ (message "Adding new files to repository...")
+ (cvs-execute-list added cvs-program (list "add" "-m" msg))
+ (message "Done.")))))
+
+(defun cvs-ignore ()
+ "Arrange so that CVS ignores the selected files.
+This command ignores files that are not flagged as `Unknown'."
+ (interactive)
+
+ (mapcar (function (lambda (tin)
+ (cond
+ ((eq (cvs-fileinfo->type
+ (cookie-cookie (current-buffer) tin)) 'UNKNOWN)
+ (cvs-append-to-ignore
+ (cookie-cookie (current-buffer) tin))
+ (cookie-delete (current-buffer) tin)))))
+ (cvs-get-marked))
+ (setq cookie-last-tin nil))
+
+(defun cvs-append-to-ignore (fileinfo)
+ "Append the file in fileinfo to the .cvsignore file"
+ (save-window-excursion
+ (set-buffer (find-file-noselect (concat (file-name-as-directory
+ (cvs-fileinfo->dir fileinfo))
+ ".cvsignore")))
+ (goto-char (point-max))
+ (if (not (zerop (current-column)))
+ (insert "\n"))
+ (insert (cvs-fileinfo->file-name fileinfo) "\n")
+ (save-buffer)))
+
+(defun cvs-status ()
+ "Show cvs status for all marked files."
+ (interactive)
+
+ (save-some-buffers)
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs status ...")
+ (cvs-execute-list marked cvs-program (cons "status" cvs-status-flags)))
+ (message "Running cvs status ... Done."))
+
+(defun cvs-log ()
+ "Display the cvs log of all selected files."
+ (interactive)
+
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs log ...")
+ (cvs-execute-list marked cvs-program (cons "log" cvs-log-flags)))
+ (message "Running cvs log ... Done."))
+
+
+(defun cvs-insert-full-path (tin)
+ "Insert full path to the file described in TIN."
+ (insert (format "%s\n" (cvs-full-path cvs-buffer-name tin))))
+
+
+(defun cvs-add-change-log-entry-other-window (pos)
+ "Add a ChangeLog entry in the ChangeLog of the current directory.
+Args: POS."
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (odir default-directory))
+ (setq default-directory
+ (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie
+ cvs-buf
+ (cookie-get-selection cvs-buf pos cookie-last-tin)))))
+ (if (not default-directory) ;In case there was no entries.
+ (setq default-directory odir))
+ (add-change-log-entry-other-window)
+ (set-buffer cvs-buf)
+ (setq default-directory odir)))
+
+
+(defun print-cvs-tin (foo)
+ "Debug utility."
+ (let ((cookie (cookie-cookie (current-buffer) foo))
+ (stream (get-buffer-create "debug")))
+ (princ "==============\n" stream)
+ (princ (cvs-fileinfo->file-name cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->dir cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->full-log cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->marked cookie) stream)
+ (princ "\n" stream)))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info
new file mode 100644
index 000000000000..3c0d3c08e590
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info
@@ -0,0 +1,1367 @@
+Info file pcl-cvs, produced by Makeinfo, -*- Text -*- from input
+file pcl-cvs.texinfo.
+
+ Copyright (C) 1992 Per Cederqvist
+
+ 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.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the section entitled "GNU General Public License" is included
+exactly as in the original, and provided that the entire resulting
+derived work is distributed under the terms of a permission notice
+identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for
+modified versions, except that the section entitled "GNU General
+Public License" and this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
+
+File: pcl-cvs, Node: Top, Next: Copying, Prev: (dir), Up: (dir)
+
+ This info manual describes pcl-cvs which is a GNU Emacs front-end
+to CVS. It works with CVS version 1.3. This manual is updated to
+release 1.02 of pcl-cvs.
+
+* Menu:
+
+* Copying:: GNU General Public License
+* Installation:: How to install pcl-cvs on your system.
+* About pcl-cvs:: Authors and ftp sites.
+
+* Getting started:: An introduction with a walk-through example.
+* Buffer contents:: An explanation of the buffer contents.
+* Commands:: All commands, grouped by type.
+
+* Customization:: How you can tailor pcl-cvs to suit your needs.
+* Future enhancements:: Future enhancements of pcl-cvs.
+* Reporting bugs and ideas:: Where to report bugs.
+
+* Function and Variable Index:: List of functions and variables.
+* Concept Index:: List of concepts.
+* Key Index:: List of keystrokes.
+
+ -- The Detailed Node Listing --
+
+Installation
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+About pcl-cvs
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+Buffer contents
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+Commands
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to `diff' different versions.
+
+
+File: pcl-cvs, Node: Copying, Next: Installation, Prev: Top, Up: Top
+
+GNU GENERAL PUBLIC LICENSE
+**************************
+
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+========
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+This General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit
+to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can
+apply it to your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and
+charge for this service if you wish), that you receive source code
+or can get it if you want it, that you can change the software or
+use pieces of it in new free programs; and that you know you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the
+rights. These restrictions translate to certain responsibilities
+for you if you distribute copies of the software, or if you modify
+it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the
+software, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make
+certain that everyone understands that there is no warranty for this
+free software. If the software is modified by someone else and
+passed on, we want its recipients to know that what they have is not
+the original, so that any problems introduced by others will not
+reflect on the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making
+the program proprietary. To prevent this, we have made it clear
+that any patent must be licensed for everyone's free use or not
+licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 1. This License applies to any program or other work which contains
+ a notice placed by the copyright holder saying it may be
+ distributed under the terms of this General Public License.
+ The "Program", below, refers to any such program or work, and a
+ "work based on the Program" means either the Program or any
+ derivative work under copyright law: that is to say, a work
+ containing the Program or a portion of it, either verbatim or
+ with modifications and/or translated into another language.
+ (Hereinafter, translation is included without limitation in the
+ term "modification".) Each licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification
+ are not covered by this License; they are outside its scope.
+ The act of running the Program is not restricted, and the
+ output from the Program is covered only if its contents
+ constitute a work based on the Program (independent of having
+ been made by running the Program). Whether that is true
+ depends on what the Program does.
+
+ 2. You may copy and distribute verbatim copies of the Program's
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an
+ appropriate copyright notice and disclaimer of warranty; keep
+ intact all the notices that refer to this License and to the
+ absence of any warranty; and give any other recipients of the
+ Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+ 3. You may modify your copy or copies of the Program or any portion
+ of it, thus forming a work based on the Program, and copy and
+ distribute such modifications or work under the terms of
+ Section 1 above, provided that you also meet all of these
+ conditions:
+
+ 1. You must cause the modified files to carry prominent
+ notices stating that you changed the files and the date of
+ any change.
+
+ 2. You must cause any work that you distribute or publish,
+ that in whole or in part contains or is derived from the
+ Program or any part thereof, to be licensed as a whole at
+ no charge to all third parties under the terms of this
+ License.
+
+ 3. If the modified program normally reads commands
+ interactively when run, you must cause it, when started
+ running for such interactive use in the most ordinary way,
+ to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no
+ warranty (or else, saying that you provide a warranty) and
+ that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive
+ but does not normally print such an announcement, your
+ work based on the Program is not required to print an
+ announcement.)
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the
+ Program, and can be reasonably considered independent and
+ separate works in themselves, then this License, and its terms,
+ do not apply to those sections when you distribute them as
+ separate works. But when you distribute the same sections as
+ part of a whole which is a work based on the Program, the
+ distribution of the whole must be on the terms of this License,
+ whose permissions for other licensees extend to the entire
+ whole, and thus to each and every part regardless of who wrote
+ it.
+
+ Thus, it is not the intent of this section to claim rights
+ or contest your rights to work written entirely by you; rather,
+ the intent is to exercise the right to control the distribution
+ of derivative or collective works based on the Program.
+
+ In addition, mere aggregation of another work not based on
+ the Program with the Program (or with a work based on the
+ Program) on a volume of a storage or distribution medium does
+ not bring the other work under the scope of this License.
+
+ 4. You may copy and distribute the Program (or a work based on it,
+ under Section 2) in object code or executable form under the
+ terms of Sections 1 and 2 above provided that you also do one
+ of the following:
+
+ 1. Accompany it with the complete corresponding
+ machine-readable source code, which must be distributed
+ under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ 2. Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than
+ your cost of physically performing source distribution, a
+ complete machine-readable copy of the corresponding source
+ code, to be distributed under the terms of Sections 1 and
+ 2 above on a medium customarily used for software
+ interchange; or,
+
+ 3. Accompany it with the information you received as to the
+ offer to distribute corresponding source code. (This
+ alternative is allowed only for noncommercial distribution
+ and only if you received the program in object code or
+ executable form with such an offer, in accord with
+ Subsection b above.)
+
+ The source code for a work means the preferred form of the
+ work for making modifications to it. For an executable work,
+ complete source code means all the source code for all modules
+ it contains, plus any associated interface definition files,
+ plus the scripts used to control compilation and installation
+ of the executable. However, as a special exception, the source
+ code distributed need not include anything that is normally
+ distributed (in either source or binary form) with the major
+ components (compiler, kernel, and so on) of the operating
+ system on which the executable runs, unless that component
+ itself accompanies the executable.
+
+ If distribution of executable or object code is made by
+ offering access to copy from a designated place, then offering
+ equivalent access to copy the source code from the same place
+ counts as distribution of the source code, even though third
+ parties are not compelled to copy the source along with the
+ object code.
+
+ 5. You may not copy, modify, sublicense, or distribute the Program
+ except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense or distribute the Program
+ is void, and will automatically terminate your rights under
+ this License. However, parties who have received copies, or
+ rights, from you under this License will not have their
+ licenses terminated so long as such parties remain in full
+ compliance.
+
+ 6. You are not required to accept this License, since you have not
+ signed it. However, nothing else grants you permission to
+ modify or distribute the Program or its derivative works.
+ These actions are prohibited by law if you do not accept this
+ License. Therefore, by modifying or distributing the Program
+ (or any work based on the Program), you indicate your
+ acceptance of this License to do so, and all its terms and
+ conditions for copying, distributing or modifying the Program
+ or works based on it.
+
+ 7. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from
+ the original licensor to copy, distribute or modify the Program
+ subject to these terms and conditions. You may not impose any
+ further restrictions on the recipients' exercise of the rights
+ granted herein. You are not responsible for enforcing
+ compliance by third parties to this License.
+
+ 8. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent
+ issues), conditions are imposed on you (whether by court order,
+ agreement or otherwise) that contradict the conditions of this
+ License, they do not excuse you from the conditions of this
+ License. If you cannot distribute so as to satisfy
+ simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not
+ distribute the Program at all. For example, if a patent
+ license would not permit royalty-free redistribution of the
+ Program by all those who receive copies directly or indirectly
+ through you, then the only way you could satisfy both it and
+ this License would be to refrain entirely from distribution of
+ the Program.
+
+ If any portion of this section is held invalid or
+ unenforceable under any particular circumstance, the balance of
+ the section is intended to apply and the section as a whole is
+ intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to
+ infringe any patents or other property right claims or to
+ contest validity of any such claims; this section has the sole
+ purpose of protecting the integrity of the free software
+ distribution system, which is implemented by public license
+ practices. Many people have made generous contributions to the
+ wide range of software distributed through that system in
+ reliance on consistent application of that system; it is up to
+ the author/donor to decide if he or she is willing to
+ distribute software through any other system and a licensee
+ cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is
+ believed to be a consequence of the rest of this License.
+
+ 9. If the distribution and/or use of the Program is restricted in
+ certain countries either by patents or by copyrighted
+ interfaces, the original copyright holder who places the
+ Program under this License may add an explicit geographical
+ distribution limitation excluding those countries, so that
+ distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the
+ limitation as if written in the body of this License.
+
+ 10. The Free Software Foundation may publish revised and/or new
+ versions of the General Public License from time to time. Such
+ new versions will be similar in spirit to the present version,
+ but may differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If
+ the Program specifies a version number of this License which
+ applies to it and "any later version", you have the option of
+ following the terms and conditions either of that version or of
+ any later version published by the Free Software Foundation.
+ If the Program does not specify a version number of this
+ License, you may choose any version ever published by the Free
+ Software Foundation.
+
+ 11. If you wish to incorporate parts of the Program into other free
+ programs whose distribution conditions are different, write to
+ the author to ask for permission. For software which is
+ copyrighted by the Free Software Foundation, write to the Free
+ Software Foundation; we sometimes make exceptions for this.
+ Our decision will be guided by the two goals of preserving the
+ free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 12. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+ WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+ LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
+ WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
+ COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 13. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
+ MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
+ LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+ INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS
+ OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
+ WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
+ HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+Appendix: How to Apply These Terms to Your New Programs
+=======================================================
+
+ If you develop a new program, and you want it to be of the
+greatest possible use to the public, the best way to achieve this is
+to make it free software which everyone can redistribute and change
+under these terms.
+
+ To do so, attach the following notices to the program. It is
+safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+ ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES.
+ Copyright (C) 19YY NAME OF AUTHOR
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Also add information on how to contact you by electronic and
+paper mail.
+
+ If the program is interactive, make it output a short notice like
+this when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+ The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and
+`show c'; they could even be mouse-clicks or menu items--whatever
+suits your program.
+
+ You should also get your employer (if you work as a programmer)
+or your school, if any, to sign a "copyright disclaimer" for the
+program, if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ SIGNATURE OF TY COON, 1 April 1989
+ Ty Coon, President of Vice
+
+ This General Public License does not permit incorporating your
+program into proprietary programs. If your program is a subroutine
+library, you may consider it more useful to permit linking
+proprietary applications with the library. If this is what you want
+to do, use the GNU Library General Public License instead of this
+License.
+
+
+File: pcl-cvs, Node: Installation, Next: About pcl-cvs, Prev: Copying, Up: Top
+
+Installation
+************
+
+ This section describes the installation of pcl-cvs, the GNU Emacs
+CVS front-end. You should install not only the elisp files
+themselves, but also the on-line documentation so that your users
+will know how to use it. You can create typeset documentation from
+the file `pcl-cvs.texinfo' as well as an on-line info file. The
+following steps are also described in the file `INSTALL' in the
+source directory.
+
+* Menu:
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+
+File: pcl-cvs, Node: Pcl-cvs installation, Next: On-line manual installation, Prev: Installation, Up: Installation
+
+Installation of the pcl-cvs program
+===================================
+
+ 1. Edit the file `Makefile' to reflect the situation at your site.
+ The only things you have to change is the definition of
+ `lispdir' and `infodir'. The elisp files will be copied to
+ `lispdir', and the info file to `infodir'.
+
+ 2. Configure pcl-cvs.el
+
+ There are a couple of paths that you have to check to make
+ sure that they match you system. They appear early in the file
+ pcl-cvs.el.
+
+ *NOTE:* If your system is running emacs 18.57 or earlier
+ you MUST uncomment the line that says:
+
+ (setq delete-exited-processes nil)
+
+ Setting `delete-exited-processes' to `nil' works around a bug
+ in emacs that causes it to dump core. The bug was fixed in
+ emacs 18.58.
+
+ 3. Type `make install' in the source directory. This will
+ byte-compile all `.el' files and copy both the `.el' and the
+ `.elc' into the directory you specified in step 1.
+
+ If you don't want to install the `.el' files but only the
+ `.elc' files (the byte-compiled files), you can type ``make
+ install_elc'' instead of ``make install''.
+
+ If you only want to create the compiled elisp files, but
+ don't want to install them, you can type `make elcfiles'
+ instead. This is what happens if you only type `make' without
+ parameters.
+
+ 4. Edit the file `default.el' in your emacs lisp directory (usually
+ `/usr/gnu/emacs/lisp' or something similar) and enter the
+ contents of the file `pcl-cvs-startup.el' into it. It contains
+ a couple of `auto-load's that facilitates the use of pcl-cvs.
+
+
+File: pcl-cvs, Node: On-line manual installation, Next: Typeset manual installation, Prev: Pcl-cvs installation, Up: Installation
+
+Installation of the on-line manual.
+===================================
+
+ 1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing
+ `make info'. If you don't have the program `makeinfo' you can
+ get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as
+ `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version
+ there when you read this), or you could use the preformatted
+ info file `pcl-cvs.info' that is included in the distribution
+ (type `cp pcl-cvs.info pcl-cvs').
+
+ 2. Move the info file `pcl-cvs' to your standard info directory.
+ This might be called something like `/usr/gnu/emacs/info'.
+
+ 3. Edit the file `dir' in the info directory and enter one line to
+ contain a pointer to the info file `pcl-cvs'. The line can, for
+ instance, look like this:
+
+ * Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+
+
+File: pcl-cvs, Node: Typeset manual installation, Prev: On-line manual installation, Up: Installation
+
+How to make typeset documentation from pcl-cvs.texinfo
+======================================================
+
+ If you have TeX installed at your site, you can make a typeset
+manual from `pcl-cvs.texinfo'.
+
+ 1. Run TeX by typing ``make pcl-cvs.dvi''. You will not get the
+ indices unless you have the `texindex' program.
+
+ 2. Convert the resulting device independent file `pcl-cvs.dvi' to a
+ form which your printer can output and print it. If you have a
+ postscript printer there is a program, `dvi2ps', which does.
+ There is also a program which comes together with TeX, `dvips',
+ which you can use.
+
+
+File: pcl-cvs, Node: About pcl-cvs, Next: Getting started, Prev: Installation, Up: Top
+
+About pcl-cvs
+*************
+
+ Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+* Menu:
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+
+File: pcl-cvs, Node: Contributors, Next: Archives, Prev: About pcl-cvs, Up: About pcl-cvs
+
+Contributors to pcl-cvs
+=======================
+
+ Contributions to the package are welcome. I have limited time to
+work on this project, but I will gladly add any code that you
+contribute to me to this package (*note Reporting bugs and ideas::.).
+
+ The following persons have made contributions to pcl-cvs.
+
+ * Brian Berliner wrote CVS, together with some other contributors.
+ Without his work on CVS this package would be useless...
+
+ * Per Cederqvist wrote most of the otherwise unattributed
+ functions in pcl-cvs as well as all documentation.
+
+ * Inge Wallin (`inge@lysator.liu.se') wrote the skeleton to
+ `pcl-cvs.texinfo', and gave useful comments on it. He also
+ wrote the files `elib-node.el' and `compile-all.el'. The file
+ `cookie.el' was inspired by Inge.
+
+ * Linus Tolke (`linus@lysator.liu.se') contributed useful comments
+ on both the functionality and the documentation.
+
+
+File: pcl-cvs, Node: Archives, Prev: Contributors, Up: About pcl-cvs
+
+Where can I get pcl-cvs?
+========================
+
+ This release of pcl-cvs is included in the CVS 1.3 distribution.
+However, since pcl-cvs has had less time to mature (the first line of
+code was written less than a year ago) it is likely that there will
+be a new release of pcl-cvs before the next release of CVS.
+
+ The latest release of pcl-cvs can be fetched via anonymous ftp
+from `ftp.lysator.liu.se', (IP no. 130.236.254.1) in the directory
+`pub/emacs'. If you don't live in Scandinavia you should probably
+check with archie to see if there is a site closer to you that
+archives pcl-cvs.
+
+ New releases will be announced to appropriate newsgroups. If you
+send your email address to me I will add you to my list of people to
+mail when I make a new release.
+
+
+File: pcl-cvs, Node: Getting started, Next: Buffer contents, Prev: About pcl-cvs, Up: Top
+
+Getting started
+***************
+
+ This document assumes that you know what CVS is, and that you at
+least knows the fundamental concepts of CVS. If that is not the
+case you should read the man page for CVS.
+
+ Pcl-cvs is only useful once you have checked out a module. So
+before you invoke it you must have a copy of a module somewhere in
+the file system.
+
+ You invoke pcl-cvs by typing `M-x pcl-cvs RET'. If your emacs
+responds with `[No match]' your system administrator has not
+installed pcl-cvs properly. Try `M-x load-library RET pcl-cvs RET'.
+If that also fails - talk to your root. If it succeeds you might put
+this line in your `.emacs' file so that you don't have to type the
+`load-library' command every time you wish to use pcl-cvs:
+
+ (autoload 'cvs-update "pcl-cvs" nil t)
+
+ The function `cvs-update' will ask for a directory. The command
+`cvs update' will be run in that directory. (It should contain
+files that have been checked out from a CVS archive.) The output
+from `cvs' will be parsed and presented in a table in a buffer called
+`*cvs*'. It might look something like this:
+
+ PCL-CVS release 1.02.
+
+ In directory /users/ceder/FOO/test:
+ Updated bar
+ Updated file.txt
+ Modified ci namechange
+ Updated newer
+
+ In directory /users/ceder/FOO/test/sub:
+ Modified ci ChangeLog
+ ---------- End -----
+
+ In this example the three files (`bar', `file.txt' and `newer')
+that are marked with `Updated' have been copied from the CVS
+repository to `/users/ceder/FOO/test/' since someone else have
+checked in newer versions of them. Two files (`namechange' and
+`sub/ChangeLog') have been modified locally, and needs to be checked
+in.
+
+ You can move the cursor up and down in the buffer with `C-n' and
+`C-p' or `n' and `p'. If you press `c' on one of the `Modified'
+files that file will be checked in to the CVS repository. *Note
+Committing changes::. You can press `x' to get rid of the
+"uninteresting" files that have only been `Updated' (and don't
+require any further action from you).
+
+ You can also easily get a `diff' between your modified file and
+the base version that you started from, and you can get the output
+from `cvs log' and `cvs status' on the listed files simply by
+pressing a key (*note Getting info about files::.).
+
+
+File: pcl-cvs, Node: Buffer contents, Next: Commands, Prev: Getting started, Up: Top
+
+Buffer contents
+***************
+
+ The display contains four columns. They contain, from left to
+right:
+
+ * An asterisk when the file is "marked" (*note Selected files::.).
+
+ * The status of the file. See *Note File status::, for more
+ information.
+
+ * A "need to be checked in"-marker (`ci').
+
+ * The file name.
+
+* Menu:
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+
+File: pcl-cvs, Node: File status, Next: Selected files, Prev: Buffer contents, Up: Buffer contents
+
+File status
+===========
+
+ The `file status' field can have the following values:
+
+`Updated'
+ The file was brought up to date with respect to the repository.
+ This is done for any file that exists in the repository but
+ not in your source, and for files that you haven't changed but
+ are not the most recent versions available in the repository.
+
+`Modified'
+ The file is modified in your working directory, and there
+ was no modification to the same file in the repository.
+
+`Merged'
+ The file is modified in your working directory, and there were
+ modifications in the repository as well as in your copy, but
+ they were merged successfully, without conflict, in your
+ working directory.
+
+`Conflict'
+ A conflict was detected while trying to merge your changes to
+ FILE with changes from the source repository. FILE (the copy
+ in your working directory) is now the output of the `rcsmerge'
+ command on the two versions; an unmodified copy of your file is
+ also in your working directory, with the name `.#FILE.VERSION',
+ where VERSION is the RCS revision that your modified file
+ started from. *Note Viewing differences::, for more details.
+
+`Added'
+ The file has been added by you, but it still needs to be
+ checked in to the repository.
+
+`Removed'
+ The file has been removed by you, but it needs to be checked in
+ to the repository. You can resurrect it by typing `a' (*note
+ Adding and removing files::.).
+
+`Unknown'
+ A file that was detected in your directory, but that neither
+ appears in the repository, nor is present on the list of files
+ that CVS should ignore.
+
+ There are also a few special cases, that rarely occur, which have
+longer strings in the fields:
+
+`Removed from repository'
+ The file has been removed from your directory since someone has
+ removed it from the repository. (It is still present in the
+ Attic directory, so no permanent loss has occurred). This,
+ unlike the other entries in this table, is not an error
+ condition.
+
+`Removed from repository, changed by you'
+ You have modified a file that someone have removed from the
+ repository. You can correct this situation by removing the
+ file manually (see *note Adding and removing files::.).
+
+`Removed by you, changed in repository'
+ You have removed a file, and before you committed the removal
+ someone committed a change to that file. You could use `a' to
+ resurrect the file (see *note Adding and removing files::.).
+
+`Move away FILE - it is in the way'
+ For some reason CVS does not like the file FILE. Rename or
+ remove it.
+
+`This repository is missing! Remove this dir manually.'
+ It is impossible to remove a directory in the CVS repository in
+ a clean way. Someone have tried to remove one, and CVS gets
+ confused. Remove your copy of the directory.
+
+
+File: pcl-cvs, Node: Selected files, Prev: File status, Up: Buffer contents
+
+Selected files
+==============
+
+ Many of the commands works on the current set of "selected" files.
+
+ * If there are any files that are marked they constitute the set
+ of selected files.
+
+ * Otherwise, if the cursor points to a file, that file is the
+ selected file.
+
+ * Otherwise, if the cursor points to a directory, all the files
+ in that directory that appears in the buffer are the selected
+ files.
+
+ This scheme might seem a little complicated, but once one get
+used to it, it is quite powerful.
+
+ *Note Marking files:: tells how you mark and unmark files.
+
+
+File: pcl-cvs, Node: Commands, Next: Customization, Prev: Buffer contents, Up: Top
+
+Commands
+********
+
+ The nodes in this menu contains explanations about all the
+commands that you can use in pcl-cvs. They are grouped together by
+type.
+
+* Menu:
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to `diff' different versions.
+
+
+File: pcl-cvs, Node: Updating the directory, Next: Movement commands, Prev: Commands, Up: Commands
+
+Updating the directory
+======================
+
+`M-x cvs-update'
+ Run a `cvs update' command. You will be asked for the
+ directory in which the `cvs update' will be run. The output
+ will be parsed by pcl-cvs, and the result printed in the
+ `*cvs*' buffer (see *note Buffer contents::. for a description
+ of the contents).
+
+ By default, `cvs-update' will descend recursively into
+ subdirectories. You can avoid that behavior by giving a prefix
+ argument to it (e.g., by typing `C-u M-x cvs-update RET').
+
+ All other commands in pcl-cvs requires that you have a `*cvs*'
+ buffer. This is the command that you use to get one.
+
+`g'
+ This will run `cvs update' again. It will always use the same
+ buffer that was used with the previous `cvs update'. Give a
+ prefix argument to avoid descending into subdirectories. This
+ runs the command `cvs-update-no-prompt'.
+
+
+File: pcl-cvs, Node: Movement commands, Next: Marking files, Prev: Updating the directory, Up: Commands
+
+Movement Commands
+=================
+
+ You can use most normal Emacs commands to move forward and
+backward in the buffer. Some keys are rebound to functions that
+take advantage of the fact that the buffer is a pcl-cvs buffer:
+
+`SPC'
+`C-n'
+`n'
+ These keys move the cursor one file forward, towards the end of
+ the buffer (`cookie-next-cookie').
+
+`C-p'
+`p'
+ These keys move one file backward, towards the beginning of the
+ buffer (`cookie-previous-cookie').
+
+
+File: pcl-cvs, Node: Marking files, Next: Committing changes, Prev: Movement commands, Up: Commands
+
+Marking files
+=============
+
+ Pcl-cvs works on a set of "selected files" (*note Selected
+files::.). You can mark and unmark files with these commands:
+
+`m'
+ This marks the file that the cursor is positioned on. If the
+ cursor is positioned on a directory all files in that directory
+ will be marked. (`cvs-mark').
+
+`u'
+ Unmark the file that the cursor is positioned on. If the cursor
+ is on a directory, all files in that directory will be unmarked.
+ (`cvs-unmark').
+
+`M'
+ Mark *all* files in the buffer (`cvs-mark-all-files').
+
+`U'
+ Unmark *all* files (`cvs-unmark-all-files').
+
+`DEL'
+ Unmark the file on the previous line, and move point to that
+ line (`cvs-unmark-up').
+
+
+File: pcl-cvs, Node: Committing changes, Next: Editing files, Prev: Marking files, Up: Commands
+
+Committing changes
+==================
+
+`c'
+ All files that have a "need to be checked in"-marker (*note
+ Buffer contents::.) can be checked in with the `c' command. It
+ checks in all selected files (*note Selected files::.) (except
+ those who lack the "ci"-marker - they are ignored). Pressing
+ `c' causes `cvs-commit' to be run.
+
+ When you press `c' you will get a buffer called
+ `*cvs-commit-message*'. Enter the log message for the file(s)
+ in it. When you are ready you should press `C-c C-c' to
+ actually commit the files (using `cvs-edit-done').
+
+ Normally the `*cvs-commit-message*' buffer will retain the log
+ message from the previous commit, but if the variable
+ `cvs-erase-input-buffer' is set to a non-nil value the buffer
+ will be erased. Point and mark will always be located around
+ the entire buffer so that you can easily erase it with `C-w'
+ (`kill-region').
+
+
+File: pcl-cvs, Node: Editing files, Next: Getting info about files, Prev: Committing changes, Up: Commands
+
+Editing files
+=============
+
+ There are currently three commands that can be used to find a
+file (that is, load it into a buffer and start editing it there).
+These commands work on the line that the cursor is situated at.
+They ignore any marked files.
+
+`f'
+ Find the file that the cursor points to. Run `dired'
+
+ (*note Dired: (Emacs)Dired.)
+
+ if the cursor points to a directory (`cvs-find-file').
+
+`o'
+ Like `f', but use another window (`cvs-find-file-other-window').
+
+`A'
+ Invoke `add-change-log-entry-other-window' to edit a
+ `ChangeLog' file. The `ChangeLog' will be found in the
+ directory of the file the cursor points to.
+ (`cvs-add-change-log-entry-other-window').
+
+
+File: pcl-cvs, Node: Getting info about files, Next: Adding and removing files, Prev: Editing files, Up: Commands
+
+Getting info about files
+========================
+
+ Both of the following commands can be customized. *Note
+Customization::.
+
+`l'
+ Run `cvs log' on all selected files, and show the result in a
+ temporary buffer (`cvs-log').
+
+`s'
+ Run `cvs status' on all selected files, and show the result in a
+ temporary buffer (`cvs-status').
+
+
+File: pcl-cvs, Node: Adding and removing files, Next: Removing handled entries, Prev: Getting info about files, Up: Commands
+
+Adding and removing files
+=========================
+
+ The following commands are available to make it easy to add and
+remove files from the CVS repository.
+
+`a'
+ Add all selected files. This command can be used on `Unknown'
+ files (see *note File status::.). The status of the file will
+ change to `Added', and you will have to use `c' (`cvs-commit',
+ see *note Committing changes::.) to really add the file to the
+ repository.
+
+ This command can also be used on `Removed' files (before you
+ commit them) to resurrect them.
+
+ Selected files that are neither `Unknown' nor `Removed' will be
+ ignored by this command.
+
+ The command that is run is `cvs-add'.
+
+`r'
+ This command removes the selected files (after prompting for
+ confirmation). The files are `rm'ed from your directory and
+ (unless the status was `Unknown'; *note File status::.) they
+ will also be `cvs remove'd. If the files were `Unknown' they
+ will disappear from the buffer. Otherwise their status will
+ change to `Removed', and you must use `c' (`cvs-commit', *note
+ Committing changes::.) to commit the removal.
+
+ The command that is run is `cvs-remove-file'.
+
+
+File: pcl-cvs, Node: Removing handled entries, Next: Ignoring files, Prev: Adding and removing files, Up: Commands
+
+Removing handled entries
+========================
+
+`x'
+ This command allows you to remove all entries that you have
+ processed. More specifically, the lines for `Updated' files
+ (*note File status::. and files that have been checked in
+ (*note Committing changes::.) are removed from the buffer. If
+ a directory becomes empty the heading for that directory is
+ also removed. This makes it easier to get an overview of what
+ needs to be done.
+
+ The command is called `cvs-remove-handled'. If
+ `cvs-auto-remove-handled' is set to non-`nil' this will
+ automatically be performed after every commit.
+
+`C-k'
+ This command can be used for lines that `cvs-remove-handled'
+ would not delete, but that you want to delete
+ (`cvs-acknowledge').
+
+
+File: pcl-cvs, Node: Ignoring files, Next: Viewing differences, Prev: Removing handled entries, Up: Commands
+
+Ignoring files
+==============
+
+`i'
+ Arrange so that CVS will ignore the selected files. The file
+ names are added to the `.cvsignore' file in the corresponding
+ directory. If the `.cvsignore' doesn't exist it will be
+ created.
+
+ The `.cvsignore' file should normally be added to the
+ repository, but you could ignore it also if you like it better
+ that way.
+
+ This runs `cvs-ignore'.
+
+
+File: pcl-cvs, Node: Viewing differences, Prev: Ignoring files, Up: Commands
+
+Viewing differences
+===================
+
+`d'
+ Display a `cvs diff' between the selected files and the RCS
+ version that they are based on. *Note Customization::
+ describes how you can send flags to `cvs diff'. (The function
+ that does the job is `cvs-diff-cvs').
+
+`b'
+ If CVS finds a conflict while merging two versions of a file
+ (during a `cvs update', *note Updating the directory::.) it
+ will save the original file in a file called `.#FILE.VERSION'
+ where FILE is the name of the file, and VERSION is the RCS
+ version number that your file was based on.
+
+ With the `b' command you can run a `diff' on the files
+ `.#FILE.VERSION' and `FILE'. You can get a context- or Unidiff
+ by setting `cvs-diff-flags' - *note Customization::.. This
+ command only works on files that have status `Conflict' or
+ `Merged'. The name of the command is `cvs-diff-backup'.
+
+
+File: pcl-cvs, Node: Customization, Next: Future enhancements, Prev: Commands, Up: Top
+
+Customization
+*************
+
+ If you have an idea about any customization that would be handy
+but isn't present in this list, please tell me! *Note Reporting
+bugs and ideas:: for info on how to reach me.
+
+`cvs-erase-input-buffer'
+ If set to anything else than `nil' the edit buffer will be
+ erased before you write the log message (*note Committing
+ changes::.).
+
+`cvs-inhibit-copyright-message'
+ The copyright message that is displayed on startup can be
+ annoying after a while. Set this variable to `t' if you want
+ to get rid of it. (But don't set this to `t' in the system
+ defaults file - new users should see this message at least
+ once).
+
+`cvs-cvs-diff-flags'
+ A list of strings to pass as arguments to the `cvs diff'
+ program. This is used by `cvs-diff-cvs' (key `d', *note
+ Viewing differences::.). If you prefer the Unidiff format you
+ could add this line to your `.emacs' file:
+
+ (setq cvs-cvs-diff-flags '("-u"))
+
+`cvs-diff-flags'
+ Like `cvs-cvs-diff-flags', but passed to `diff'. This is used
+ by `cvs-diff-backup' (key `b', *note Viewing differences::.).
+
+`cvs-log-flags'
+ List of strings to send to `cvs log'. Used by `cvs-log' (key
+ `l', *note Getting info about files::.).
+
+`cvs-status-flags'
+ List of strings to send to `cvs status'. Used by `cvs-status'
+ (key `s', *note Getting info about files::.).
+
+`cvs-auto-remove-handled'
+ If this variable is set to any non-`nil' value
+ `cvs-remove-handled' will be called every time you check in
+ files, after the check-in is ready. *Note Removing handled
+ entries::.
+
+
+File: pcl-cvs, Node: Future enhancements, Next: Reporting bugs and ideas, Prev: Customization, Up: Top
+
+Future enhancements
+*******************
+
+ Pcl-cvs is still under development and needs a number of
+enhancements to be called complete. Here is my current wish-list
+for future releases of pcl-cvs:
+
+ * Dired support. I have an experimental `dired-cvs.el' that works
+ together with CVS 1.2. Unfortunately I wrote it on top of a
+ non-standard `dired.el', so it must be rewritten.
+
+ * It should be possible to run commands such as `cvs log', `cvs
+ status' and `cvs commit' directly from a buffer containing a
+ file, instead of having to `cvs-update'. If the directory
+ contains many files the `cvs-update' can take quite some time,
+ especially on a slow machine.
+
+ If you miss something in this wish-list, let me know! I don't
+promise that I will write it, but I will at least try to coordinate
+the efforts of making a good Emacs front end to CVS. See *Note
+Reporting bugs and ideas:: for information about how to reach me.
+
+
+File: pcl-cvs, Node: Reporting bugs and ideas, Next: Function and Variable Index, Prev: Future enhancements, Up: Top
+
+Reporting bugs and ideas
+************************
+
+ If you find a bug or misfeature, don't hesitate to tell me! Send
+email to `ceder@lysator.liu.se'.
+
+ If you have ideas for improvements, or if you have written some
+extensions to this package, I would like to hear from you. I hope
+that you find this package useful!
+
+
+File: pcl-cvs, Node: Function and Variable Index, Next: Concept Index, Prev: Reporting bugs and ideas, Up: Top
+
+Function and Variable Index
+***************************
+
+* Menu:
+
+* cookie-next-cookie: Movement commands.
+* cookie-previous-cookie: Movement commands.
+* cvs-acknowledge: Removing handled entries.
+* cvs-add: Adding and removing files.
+* cvs-add-change-log-entry-other-window: Editing files.
+* cvs-auto-remove-handled (variable): Customization.
+* cvs-commit: Committing changes.
+* cvs-cvs-diff-flags (variable): Customization.
+* cvs-diff-backup: Viewing differences.
+* cvs-diff-cvs: Viewing differences.
+* cvs-diff-flags (variable): Customization.
+* cvs-erase-input-buffer (variable): Committing changes.
+* cvs-erase-input-buffer (variable): Customization.
+* cvs-find-file: Editing files.
+* cvs-find-file-other-window: Editing files.
+* cvs-inhibit-copyright-message (variable): Customization.
+* cvs-log: Getting info about files.
+* cvs-log-flags (variable): Customization.
+* cvs-mark: Marking files.
+* cvs-mark-all-files: Marking files.
+* cvs-remove-file: Adding and removing files.
+* cvs-remove-handled: Removing handled entries.
+* cvs-status: Getting info about files.
+* cvs-status-flags (variable): Customization.
+* cvs-unmark: Marking files.
+* cvs-unmark-all-files: Marking files.
+* cvs-unmark-up: Marking files.
+* cvs-update: Updating the directory.
+* cvs-update-no-prompt: Updating the directory.
+
+
+File: pcl-cvs, Node: Concept Index, Next: Key Index, Prev: Function and Variable Index, Up: Top
+
+Concept Index
+*************
+
+* Menu:
+
+* About pcl-cvs: About pcl-cvs.
+* Active files: Selected files.
+* Added (file status): File status.
+* Adding files: Adding and removing files.
+* Archives: Archives.
+* Author, how to reach: Reporting bugs and ideas.
+* Authors: Contributors.
+* Automatically remove handled files: Customization.
+* Buffer contents: Buffer contents.
+* Bugs, how to report them: Reporting bugs and ideas.
+* Ci: Committing changes.
+* Commit buffer: Committing changes.
+* Committing changes: Committing changes.
+* Conflict (file status): File status.
+* Conflicts, how to resolve them: Viewing differences.
+* Context diff, how to get: Customization.
+* Contributors: Contributors.
+* Copyright message, getting rid of it: Customization.
+* Customization: Customization.
+* Deleting files: Adding and removing files.
+* Diff: Viewing differences.
+* Dired: Editing files.
+* Edit buffer: Committing changes.
+* Editing files: Editing files.
+* Email archives: Archives.
+* Email to the author: Reporting bugs and ideas.
+* Enhancements: Future enhancements.
+* Erasing commit message: Committing changes.
+* Erasing the input buffer: Customization.
+* Example run: Getting started.
+* Expunging uninteresting entries: Removing handled entries.
+* File selection: Selected files.
+* File status: File status.
+* Finding files: Editing files.
+* Ftp-sites: Archives.
+* Generating a typeset manual: Typeset manual installation.
+* Generating the on-line manual: On-line manual installation.
+* Getting pcl-cvs: Archives.
+* Getting rid of the Copyright message.: Customization.
+* Getting rid of uninteresting lines: Removing handled entries.
+* Getting status: Getting info about files.
+* Handled lines, removing them: Removing handled entries.
+* Info-file (how to generate): On-line manual installation.
+* Inhibiting the Copyright message.: Customization.
+* Installation: Installation.
+* Installation of elisp files: Pcl-cvs installation.
+* Installation of on-line manual: On-line manual installation.
+* Installation of typeset manual: Typeset manual installation.
+* Introduction: Getting started.
+* Invoking dired: Editing files.
+* Loading files: Editing files.
+* Log (RCS/cvs command): Getting info about files.
+* Manual installation (on-line): On-line manual installation.
+* Manual installation (typeset): Typeset manual installation.
+* Marked files: Selected files.
+* Marking files: Marking files.
+* Merged (file status): File status.
+* Modified (file status): File status.
+* Move away FILE - it is in the way (file status): File status.
+* Movement Commands: Movement commands.
+* On-line manual (how to generate): On-line manual installation.
+* Printing a manual: Typeset manual installation.
+* Putting files under CVS control: Adding and removing files.
+* Removed (file status): File status.
+* Removed by you, changed in repository (file status): File status.
+* Removed from repository (file status): File status.
+* Removed from repository, changed by you (file status): File status.
+* Removing files: Adding and removing files.
+* Removing uninteresting (processed) lines: Removing handled entries.
+* Reporting bugs and ideas: Reporting bugs and ideas.
+* Resurrecting files: Adding and removing files.
+* Selected files: Selected files.
+* Selecting files (commands to mark files): Marking files.
+* Sites: Archives.
+* Status (cvs command): Getting info about files.
+* TeX - generating a typeset manual: Typeset manual installation.
+* This repository is missing!... (file status): File status.
+* Unidiff, how to get: Customization.
+* Uninteresting entries, getting rid of them: Removing handled entries.
+* Unknown (file status): File status.
+* Updated (file status): File status.
+* Variables, list of all: Customization.
+* Viewing differences: Viewing differences.
+
+
+File: pcl-cvs, Node: Key Index, Prev: Concept Index, Up: Top
+
+Key Index
+*********
+
+* Menu:
+
+* A - add ChangeLog entry: Editing files.
+* C-k - remove selected entries: Removing handled entries.
+* C-n - Move down one file: Movement commands.
+* C-p - Move up one file: Movement commands.
+* DEL - unmark previous file: Marking files.
+* M - marking all files: Marking files.
+* SPC - Move down one file: Movement commands.
+* U - unmark all files: Marking files.
+* a - add a file: Adding and removing files.
+* b - diff backup file: Viewing differences.
+* c - commit files: Committing changes.
+* d - run cvs diff: Viewing differences.
+* f - find file or directory: Editing files.
+* g - Rerun cvs update: Updating the directory.
+* l - run cvs log: Getting info about files.
+* m - marking a file: Marking files.
+* n - Move down one file: Movement commands.
+* o - find file in other window: Editing files.
+* p - Move up on file: Movement commands.
+* r - remove a file: Adding and removing files.
+* s - run cvs status: Getting info about files.
+* u - unmark a file: Marking files.
+* x - remove processed entries: Removing handled entries.
+
+
+
+Tag Table:
+Node: Top1004
+Node: Copying3396
+Node: Installation22716
+Node: Pcl-cvs installation23507
+Node: On-line manual installation25291
+Node: Typeset manual installation26310
+Node: About pcl-cvs27048
+Node: Contributors27417
+Node: Archives28440
+Node: Getting started29287
+Node: Buffer contents31728
+Node: File status32277
+Node: Selected files35303
+Node: Commands35976
+Node: Updating the directory37018
+Node: Movement commands38043
+Node: Marking files38629
+Node: Committing changes39456
+Node: Editing files40502
+Node: Getting info about files41335
+Node: Adding and removing files41805
+Node: Removing handled entries43145
+Node: Ignoring files44058
+Node: Viewing differences44593
+Node: Customization45595
+Node: Future enhancements47326
+Node: Reporting bugs and ideas48394
+Node: Function and Variable Index48842
+Node: Concept Index50743
+Node: Key Index55865
+
+End Tag Table
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo
new file mode 100644
index 000000000000..5ad8db1ede47
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo
@@ -0,0 +1,1437 @@
+\input texinfo @c -*-texinfo-*-
+
+@comment pcl-cvs.texinfo,v 1.2 1992/04/07 20:49:23 berliner Exp
+@comment Documentation for the GNU Emacs CVS mode.
+@comment Copyright (C) 1992 Per Cederqvist
+
+@comment This file is part of the pcl-cvs distribution.
+
+@comment Pcl-cvs is free software; you can redistribute it and/or modify
+@comment it under the terms of the GNU General Public License as published by
+@comment the Free Software Foundation; either version 1, or (at your option)
+@comment any later version.
+
+@comment Pcl-cvs is distributed in the hope that it will be useful,
+@comment but WITHOUT ANY WARRANTY; without even the implied warranty of
+@comment MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+@comment GNU General Public License for more details.
+
+@comment You should have received a copy of the GNU General Public License
+@comment along with pcl-cvs; see the file COPYING. If not, write to
+@comment the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+@setfilename pcl-cvs
+@settitle Pcl-cvs - The Emacs Front-End to CVS
+@setchapternewpage on
+
+@ifinfo
+Copyright @copyright{} 1992 Per Cederqvist
+
+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.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' and
+this permission notice may be included in translations approved by the
+Free Software Foundation instead of in the original English.
+@end ifinfo
+
+@synindex vr fn
+@comment The titlepage section does not appear in the Info file.
+@titlepage
+@sp 4
+@comment The title is printed in a large font.
+@center @titlefont{User's Guide}
+@sp
+@center @titlefont{to}
+@sp
+@center @titlefont{pcl-cvs - the Emacs Front-End to CVS}
+@sp 2
+@center release 1.02
+@comment -release-
+@sp 3
+@center Per Cederqvist
+@sp 3
+@center last updated 29 Mar 1992
+@comment -date-
+
+@comment The following two commands start the copyright page
+@comment for the printed manual. This will not appear in the Info file.
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992 Per Cederqvist
+
+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.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' and
+this permission notice may be included in translations approved by the
+Free Software Foundation instead of in the original English.
+@end titlepage
+
+@comment ================================================================
+@comment The real text starts here
+@comment ================================================================
+
+@node Top, Copying, (dir), (dir)
+@comment node-name, next, previous, up
+
+
+@ifinfo
+This info manual describes pcl-cvs which is a GNU Emacs front-end to
+CVS. It works with CVS version 1.3. This manual is updated to release
+1.02 of pcl-cvs.
+@end ifinfo
+@comment -release-
+
+@menu
+* Copying:: GNU General Public License
+* Installation:: How to install pcl-cvs on your system.
+* About pcl-cvs:: Authors and ftp sites.
+
+* Getting started:: An introduction with a walk-through example.
+* Buffer contents:: An explanation of the buffer contents.
+* Commands:: All commands, grouped by type.
+
+* Customization:: How you can tailor pcl-cvs to suit your needs.
+* Future enhancements:: Future enhancements of pcl-cvs.
+* Reporting bugs and ideas:: Where to report bugs.
+
+* Function and Variable Index:: List of functions and variables.
+* Concept Index:: List of concepts.
+* Key Index:: List of keystrokes.
+
+ --- The Detailed Node Listing ---
+
+Installation
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+About pcl-cvs
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+Buffer contents
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+Commands
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to @samp{diff} different versions.
+@end menu
+
+@node Copying, Installation, Top, Top
+@unnumbered GNU GENERAL PUBLIC LICENSE
+@center Version 2, June 1991
+
+@display
+Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc.
+675 Mass Ave, Cambridge, MA 02139, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@unnumberedsec Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software---to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end ifinfo
+
+@enumerate
+@item
+This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The ``Program'', below,
+refers to any such program or work, and a ``work based on the Program''
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term ``modification''.) Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+@item
+You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+@item
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+@enumerate a
+@item
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+@item
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+@item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+@end enumerate
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+@item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+@enumerate a
+@item
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+@item
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+@item
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+@end enumerate
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+@item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+@item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+@item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+@item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+@item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+@item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+@iftex
+@heading NO WARRANTY
+@end iftex
+@ifinfo
+@center NO WARRANTY
+@end ifinfo
+
+@item
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+@item
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+@end enumerate
+
+@iftex
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+
+@page
+@unnumberedsec Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}
+Copyright (C) 19@var{yy} @var{name of author}
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+@smallexample
+Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author}
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than @samp{show w} and
+@samp{show c}; they could even be mouse-clicks or menu items---whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+@example
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+@var{signature of Ty Coon}, 1 April 1989
+Ty Coon, President of Vice
+@end example
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+@node Installation, About pcl-cvs, Copying, Top
+@comment node-name, next, previous, up
+@chapter Installation
+@cindex Installation
+
+This section describes the installation of pcl-cvs, the GNU Emacs CVS
+front-end. You should install not only the elisp files themselves, but
+also the on-line documentation so that your users will know how to use
+it. You can create typeset documentation from the file
+@file{pcl-cvs.texinfo} as well as an on-line info file. The following
+steps are also described in the file @file{INSTALL} in the source
+directory.
+
+@menu
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+@end menu
+
+@node Pcl-cvs installation, On-line manual installation, Installation, Installation
+@comment node-name, next, previous, up
+@section Installation of the pcl-cvs program
+@cindex Installation of elisp files
+
+@enumerate
+@item
+Edit the file @file{Makefile} to reflect the situation at your site.
+The only things you have to change is the definition of @code{lispdir}
+and @code{infodir}. The elisp files will be copied to @code{lispdir},
+and the info file to @code{infodir}.
+
+@item
+Configure pcl-cvs.el
+
+There are a couple of paths that you have to check to make sure that
+they match you system. They appear early in the file pcl-cvs.el.
+
+@strong{NOTE:} If your system is running emacs 18.57 or earlier you MUST
+uncomment the line that says:
+
+@example
+(setq delete-exited-processes nil)
+@end example
+
+Setting @code{delete-exited-processes} to @code{nil} works around a bug
+in emacs that causes it to dump core. The bug was fixed in emacs
+18.58.@refill
+
+@item
+Type @samp{make install} in the source directory. This will
+byte-compile all @file{.el} files and copy both the @file{.el} and the
+@file{.elc} into the directory you specified in step 1.
+
+If you don't want to install the @file{.el} files but only the
+@file{.elc} files (the byte-compiled files), you can type `@samp{make
+install_elc}' instead of `@samp{make install}'.
+
+If you only want to create the compiled elisp files, but don't want to
+install them, you can type @samp{make elcfiles} instead. This is what
+happens if you only type @samp{make} without parameters.
+
+@item
+Edit the file @file{default.el} in your emacs lisp directory (usually
+@file{/usr/gnu/emacs/lisp} or something similar) and enter the contents
+of the file @file{pcl-cvs-startup.el} into it. It contains a couple of
+@code{auto-load}s that facilitates the use of pcl-cvs.
+
+@end enumerate
+
+@node On-line manual installation, Typeset manual installation, Pcl-cvs installation, Installation
+@comment node-name, next, previous, up
+@section Installation of the on-line manual.
+@cindex Manual installation (on-line)
+@cindex Installation of on-line manual
+@cindex Generating the on-line manual
+@cindex On-line manual (how to generate)
+@cindex Info-file (how to generate)
+
+@enumerate
+@item
+Create the info file @file{pcl-cvs} from @file{pcl-cvs.texinfo} by
+typing @samp{make info}. If you don't have the program @samp{makeinfo}
+you can get it by anonymous ftp from e.g. @samp{ftp.gnu.ai.mit.edu} as
+@file{pub/gnu/texinfo-2.14.tar.Z} (there might be a newer version there
+when you read this), or you could use the preformatted info file
+@file{pcl-cvs.info} that is included in the distribution (type
+@samp{cp pcl-cvs.info pcl-cvs}).@refill
+
+@item
+Move the info file @file{pcl-cvs} to your standard info directory.
+This might be called something like @file{/usr/gnu/emacs/info}.@refill
+
+@item
+Edit the file @file{dir} in the info directory and enter one line to
+contain a pointer to the info file @file{pcl-cvs}. The line can, for
+instance, look like this:@refill
+
+@example
+* Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+@end example
+@end enumerate
+
+@node Typeset manual installation, , On-line manual installation, Installation
+@comment node-name, next, previous, up
+@section How to make typeset documentation from pcl-cvs.texinfo
+@cindex Manual installation (typeset)
+@cindex Installation of typeset manual
+@cindex Printing a manual
+@cindex TeX - generating a typeset manual
+@cindex Generating a typeset manual
+
+If you have @TeX{} installed at your site, you can make a typeset manual
+from @file{pcl-cvs.texinfo}.
+
+@enumerate
+@item
+Run @TeX{} by typing `@samp{make pcl-cvs.dvi}'. You will not get the
+indices unless you have the @code{texindex} program.
+
+@item
+Convert the resulting device independent file @file{pcl-cvs.dvi} to a
+form which your printer can output and print it. If you have a
+postscript printer there is a program, @code{dvi2ps}, which does. There
+is also a program which comes together with @TeX{}, @code{dvips}, which
+you can use.
+
+@end enumerate
+
+@node About pcl-cvs, Getting started, Installation, Top
+@comment node-name, next, previous, up
+@chapter About pcl-cvs
+@cindex About pcl-cvs
+
+Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+@menu
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+@end menu
+
+@node Contributors, Archives, About pcl-cvs, About pcl-cvs
+@comment node-name, next, previous, up
+@section Contributors to pcl-cvs
+@cindex Contributors
+@cindex Authors
+
+Contributions to the package are welcome. I have limited time to work
+on this project, but I will gladly add any code that you contribute to
+me to this package (@pxref{Reporting bugs and ideas}).
+
+The following persons have made contributions to pcl-cvs.
+
+@itemize @bullet
+@item
+Brian Berliner wrote CVS, together with some other contributors.
+Without his work on CVS this package would be useless@dots{}
+
+@item
+Per Cederqvist wrote most of the otherwise unattributed functions in
+pcl-cvs as well as all documentation.
+
+@item
+Inge Wallin (@samp{inge@@lysator.liu.se}) wrote the skeleton to
+@file{pcl-cvs.texinfo}, and gave useful comments on it. He also wrote
+the files @file{elib-node.el} and @file{compile-all.el}. The file
+@file{cookie.el} was inspired by Inge.@refill
+
+@item
+Linus Tolke (@samp{linus@@lysator.liu.se}) contributed useful comments
+on both the functionality and the documentation.@refill
+
+@end itemize
+
+
+@node Archives, , Contributors, About pcl-cvs
+@comment node-name, next, previous, up
+@section Where can I get pcl-cvs?
+@cindex Sites
+@cindex Archives
+@cindex Ftp-sites
+@cindex Getting pcl-cvs
+@cindex Email archives
+
+This release of pcl-cvs is included in the CVS 1.3 distribution.
+However, since pcl-cvs has had less time to mature (the first line of
+code was written less than a year ago) it is likely that there will be a
+new release of pcl-cvs before the next release of CVS.
+
+The latest release of pcl-cvs can be fetched via anonymous ftp from
+@code{ftp.lysator.liu.se}, (IP no. 130.236.254.1) in the directory
+@code{pub/emacs}. If you don't live in Scandinavia you should probably
+check with archie to see if there is a site closer to you that archives
+pcl-cvs.
+
+New releases will be announced to appropriate newsgroups. If you send
+your email address to me I will add you to my list of people to mail
+when I make a new release.
+
+@node Getting started, Buffer contents, About pcl-cvs, Top
+@comment node-name, next, previous, up
+@chapter Getting started
+@cindex Introduction
+@cindex Example run
+
+This document assumes that you know what CVS is, and that you at least
+knows the fundamental concepts of CVS. If that is not the case you
+should read the man page for CVS.
+
+Pcl-cvs is only useful once you have checked out a module. So before
+you invoke it you must have a copy of a module somewhere in the file
+system.
+
+You invoke pcl-cvs by typing @kbd{M-x pcl-cvs RET}. If your emacs
+responds with @samp{[No match]} your system administrator has not
+installed pcl-cvs properly. Try @kbd{M-x load-library RET pcl-cvs RET}.
+If that also fails - talk to your root. If it succeeds you might put
+this line in your @file{.emacs} file so that you don't have to type the
+@samp{load-library} command every time you wish to use pcl-cvs:
+
+@example
+(autoload 'cvs-update "pcl-cvs" nil t)
+@end example
+
+The function @code{cvs-update} will ask for a directory. The command
+@samp{cvs update} will be run in that directory. (It should contain
+files that have been checked out from a CVS archive.) The output from
+@code{cvs} will be parsed and presented in a table in a buffer called
+@samp{*cvs*}. It might look something like this:
+
+@example
+PCL-CVS release 1.02.
+@comment -release-
+
+In directory /users/ceder/FOO/test:
+ Updated bar
+ Updated file.txt
+ Modified ci namechange
+ Updated newer
+
+In directory /users/ceder/FOO/test/sub:
+ Modified ci ChangeLog
+---------- End -----
+@end example
+
+In this example the three files (@file{bar}, @file{file.txt} and
+@file{newer}) that are marked with @samp{Updated} have been copied from
+the CVS repository to @file{/users/ceder/FOO/test/} since someone else
+have checked in newer versions of them. Two files (@file{namechange}
+and @file{sub/ChangeLog}) have been modified locally, and needs to be
+checked in.
+
+You can move the cursor up and down in the buffer with @kbd{C-n} and
+@kbd{C-p} or @kbd{n} and @kbd{p}. If you press @kbd{c} on one of the
+@samp{Modified} files that file will be checked in to the CVS
+repository. @xref{Committing changes}. You can press @kbd{x} to get rid
+of the "uninteresting" files that have only been @samp{Updated} (and
+don't require any further action from you).@refill
+
+You can also easily get a @samp{diff} between your modified file and the
+base version that you started from, and you can get the output from
+@samp{cvs log} and @samp{cvs status} on the listed files simply by
+pressing a key (@pxref{Getting info about files}).
+
+@node Buffer contents, Commands, Getting started, Top
+@comment node-name, next, previous, up
+@chapter Buffer contents
+@cindex Buffer contents
+
+The display contains four columns. They contain, from left to right:
+
+@itemize @bullet
+@item
+An asterisk when the file is @dfn{marked} (@pxref{Selected
+files}).@refill
+@item
+The status of the file. See @xref{File status}, for more information.@refill
+@item
+A "need to be checked in"-marker (@samp{ci}).
+@item
+The file name.
+@end itemize
+
+@menu
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+@end menu
+
+@node File status, Selected files, Buffer contents, Buffer contents
+@comment node-name, next, previous, up
+@section File status
+@cindex File status
+@cindex Updated (file status)
+@cindex Modified (file status)
+@cindex Merged (file status)
+@cindex Conflict (file status)
+@cindex Added (file status)
+@cindex Removed (file status)
+@cindex Unknown (file status)
+@cindex Removed from repository (file status)
+@cindex Removed from repository, changed by you (file status)
+@cindex Removed by you, changed in repository (file status)
+@cindex Move away @var{file} - it is in the way (file status)
+@cindex This repository is missing!@dots{} (file status)
+
+The @samp{file status} field can have the following values:
+
+@table @samp
+@item Updated
+The file was brought up to date with respect to the repository. This is
+done for any file that exists in the repository but not in your source,
+and for files that you haven't changed but are not the most recent
+versions available in the repository.@refill
+
+@item Modified
+The file is modified in your working directory, and there was no
+modification to the same file in the repository.@refill
+
+@item Merged
+The file is modified in your working directory, and there were
+modifications in the repository as well as in your copy, but they were
+merged successfully, without conflict, in your working directory.@refill
+
+@item Conflict
+A conflict was detected while trying to merge your changes to @var{file}
+with changes from the source repository. @var{file} (the copy in your
+working directory) is now the output of the @samp{rcsmerge} command on
+the two versions; an unmodified copy of your file is also in your
+working directory, with the name @file{.#@var{file}.@var{version}},
+where @var{version} is the RCS revision that your modified file started
+from. @xref{Viewing differences}, for more details.@refill
+
+@item Added
+The file has been added by you, but it still needs to be checked in to
+the repository.@refill
+
+@item Removed
+The file has been removed by you, but it needs to be checked in to the
+repository. You can resurrect it by typing @kbd{a} (@pxref{Adding and
+removing files}).@refill
+
+@item Unknown
+A file that was detected in your directory, but that neither appears in
+the repository, nor is present on the list of files that CVS should
+ignore.@refill
+
+@end table
+
+There are also a few special cases, that rarely occur, which have longer
+strings in the fields:
+
+@table @samp
+@item Removed from repository
+The file has been removed from your directory since someone has removed
+it from the repository. (It is still present in the Attic directory, so
+no permanent loss has occurred). This, unlike the other entries in this
+table, is not an error condition.@refill
+
+@item Removed from repository, changed by you
+You have modified a file that someone have removed from the repository.
+You can correct this situation by removing the file manually (see
+@pxref{Adding and removing files}).@refill
+
+@item Removed by you, changed in repository
+You have removed a file, and before you committed the removal someone
+committed a change to that file. You could use @kbd{a} to resurrect the
+file (see @pxref{Adding and removing files}).@refill
+
+@item Move away @var{file} - it is in the way
+For some reason CVS does not like the file @var{file}. Rename or remove
+it.@refill
+
+@item This repository is missing! Remove this dir manually.
+It is impossible to remove a directory in the CVS repository in a clean
+way. Someone have tried to remove one, and CVS gets confused. Remove
+your copy of the directory.@refill
+@end table
+
+@node Selected files, , File status, Buffer contents
+@comment node-name, next, previous, up
+@section Selected files
+@cindex Selected files
+@cindex Marked files
+@cindex File selection
+@cindex Active files
+
+Many of the commands works on the current set of @dfn{selected} files.
+
+@itemize @bullet
+@item
+If there are any files that are marked they constitute the set of
+selected files.@refill
+@item
+Otherwise, if the cursor points to a file, that file is the selected
+file.@refill
+@item
+Otherwise, if the cursor points to a directory, all the files in that
+directory that appears in the buffer are the selected files.
+@end itemize
+
+This scheme might seem a little complicated, but once one get used to
+it, it is quite powerful.
+
+@xref{Marking files} tells how you mark and unmark files.
+
+@node Commands, Customization, Buffer contents, Top
+@comment node-name, next, previous, up
+@chapter Commands
+
+@iftex
+This chapter describes all the commands that you can use in pcl-cvs.
+@end iftex
+@ifinfo
+The nodes in this menu contains explanations about all the commands that
+you can use in pcl-cvs. They are grouped together by type.
+@end ifinfo
+
+@menu
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to @samp{diff} different versions.
+@end menu
+
+@node Updating the directory, Movement commands, Commands, Commands
+@comment node-name, next, previous, up
+@section Updating the directory
+@findex cvs-update
+@findex cvs-update-no-prompt
+@kindex g - Rerun @samp{cvs update}
+
+
+@table @kbd
+
+@item M-x cvs-update
+Run a @samp{cvs update} command. You will be asked for the directory in
+which the @samp{cvs update} will be run. The output will be parsed by
+pcl-cvs, and the result printed in the @samp{*cvs*} buffer (see
+@pxref{Buffer contents} for a description of the contents).@refill
+
+By default, @samp{cvs-update} will descend recursively into
+subdirectories. You can avoid that behavior by giving a prefix
+argument to it (e.g., by typing @kbd{C-u M-x cvs-update RET}).@refill
+
+All other commands in pcl-cvs requires that you have a @samp{*cvs*}
+buffer. This is the command that you use to get one.@refill
+
+@item g
+This will run @samp{cvs update} again. It will always use the same
+buffer that was used with the previous @samp{cvs update}. Give a prefix
+argument to avoid descending into subdirectories. This runs the command
+@samp{cvs-update-no-prompt}.@refill
+@end table
+@node Movement commands, Marking files, Updating the directory, Commands
+@comment node-name, next, previous, up
+@section Movement Commands
+@cindex Movement Commands
+@findex cookie-next-cookie
+@findex cookie-previous-cookie
+@kindex SPC - Move down one file
+@kindex C-n - Move down one file
+@kindex n - Move down one file
+@kindex C-p - Move up one file
+@kindex p - Move up on file
+
+You can use most normal Emacs commands to move forward and backward in
+the buffer. Some keys are rebound to functions that take advantage of
+the fact that the buffer is a pcl-cvs buffer:
+
+
+@table @kbd
+@item SPC
+@itemx C-n
+@itemx n
+These keys move the cursor one file forward, towards the end of the
+buffer (@code{cookie-next-cookie}).
+
+@item C-p
+@itemx p
+These keys move one file backward, towards the beginning of the buffer
+(@code{cookie-previous-cookie}).
+@end table
+
+@node Marking files, Committing changes, Movement commands, Commands
+@comment node-name, next, previous, up
+@section Marking files
+@cindex Selecting files (commands to mark files)
+@cindex Marking files
+@kindex m - marking a file
+@kindex M - marking all files
+@kindex u - unmark a file
+@kindex U - unmark all files
+@kindex DEL - unmark previous file
+@findex cvs-mark
+@findex cvs-unmark
+@findex cvs-mark-all-files
+@findex cvs-unmark-all-files
+@findex cvs-unmark-up
+
+Pcl-cvs works on a set of @dfn{selected files} (@pxref{Selected files}).
+You can mark and unmark files with these commands:
+
+@table @kbd
+@item m
+This marks the file that the cursor is positioned on. If the cursor is
+positioned on a directory all files in that directory will be marked.
+(@code{cvs-mark}).
+
+@item u
+Unmark the file that the cursor is positioned on. If the cursor is on a
+directory, all files in that directory will be unmarked.
+(@code{cvs-unmark}).@refill
+
+@item M
+Mark @emph{all} files in the buffer (@code{cvs-mark-all-files}).
+
+@item U
+Unmark @emph{all} files (@code{cvs-unmark-all-files}).
+
+@item @key{DEL}
+Unmark the file on the previous line, and move point to that line
+(@code{cvs-unmark-up}).
+@end table
+
+@node Committing changes, Editing files, Marking files, Commands
+@comment node-name, next, previous, up
+@section Committing changes
+@cindex Committing changes
+@cindex Ci
+@findex cvs-commit
+@kindex c - commit files
+@vindex cvs-erase-input-buffer (variable)
+@cindex Commit buffer
+@cindex Edit buffer
+@cindex Erasing commit message
+
+@table @kbd
+@item c
+All files that have a "need to be checked in"-marker (@pxref{Buffer
+contents}) can be checked in with the @kbd{c} command. It checks in all
+selected files (@pxref{Selected files}) (except those who lack the
+"ci"-marker - they are ignored). Pressing @kbd{c} causes
+@code{cvs-commit} to be run.@refill
+
+When you press @kbd{c} you will get a buffer called
+@samp{*cvs-commit-message*}. Enter the log message for the file(s) in
+it. When you are ready you should press @kbd{C-c C-c} to actually
+commit the files (using @code{cvs-edit-done}).
+
+Normally the @samp{*cvs-commit-message*} buffer will retain the log
+message from the previous commit, but if the variable
+@code{cvs-erase-input-buffer} is set to a non-nil value the buffer will
+be erased. Point and mark will always be located around the entire
+buffer so that you can easily erase it with @kbd{C-w}
+(@samp{kill-region}).@refill
+@end table
+
+@node Editing files, Getting info about files, Committing changes, Commands
+@comment node-name, next, previous, up
+@section Editing files
+
+@cindex Editing files
+@cindex Finding files
+@cindex Loading files
+@cindex Dired
+@cindex Invoking dired
+@findex cvs-find-file
+@findex cvs-find-file-other-window
+@findex cvs-add-change-log-entry-other-window
+@kindex f - find file or directory
+@kindex o - find file in other window
+@kindex A - add ChangeLog entry
+
+There are currently three commands that can be used to find a file (that
+is, load it into a buffer and start editing it there). These commands
+work on the line that the cursor is situated at. They ignore any marked
+files.
+
+@table @kbd
+@item f
+Find the file that the cursor points to. Run @samp{dired}
+@ifinfo
+(@pxref{Dired,,,Emacs})
+@end ifinfo
+if the cursor points to a directory (@code{cvs-find-file}).@refill
+
+@item o
+Like @kbd{f}, but use another window
+(@code{cvs-find-file-other-window}).@refill
+
+@item A
+Invoke @samp{add-change-log-entry-other-window} to edit a
+@samp{ChangeLog} file. The @samp{ChangeLog} will be found in the
+directory of the file the cursor points to.
+(@code{cvs-add-change-log-entry-other-window}).@refill
+@end table
+
+@node Getting info about files, Adding and removing files, Editing files, Commands
+@comment node-name, next, previous, up
+@section Getting info about files
+@cindex Status (cvs command)
+@cindex Log (RCS/cvs command)
+@cindex Getting status
+@kindex l - run @samp{cvs log}
+@kindex s - run @samp{cvs status}
+@findex cvs-log
+@findex cvs-status
+
+Both of the following commands can be customized.
+@xref{Customization}.@refill
+
+@table @kbd
+@item l
+Run @samp{cvs log} on all selected files, and show the result in a
+temporary buffer (@code{cvs-log}).
+
+@item s
+Run @samp{cvs status} on all selected files, and show the result in a
+temporary buffer (@code{cvs-status}).
+@end table
+
+@node Adding and removing files, Removing handled entries, Getting info about files, Commands
+@comment node-name, next, previous, up
+@section Adding and removing files
+@cindex Adding files
+@cindex Removing files
+@cindex Resurrecting files
+@cindex Deleting files
+@cindex Putting files under CVS control
+@kindex a - add a file
+@kindex r - remove a file
+@findex cvs-add
+@findex cvs-remove-file
+
+The following commands are available to make it easy to add and remove
+files from the CVS repository.
+
+@table @kbd
+@item a
+Add all selected files. This command can be used on @samp{Unknown}
+files (see @pxref{File status}). The status of the file will change to
+@samp{Added}, and you will have to use @kbd{c} (@samp{cvs-commit}, see
+@pxref{Committing changes}) to really add the file to the
+repository.@refill
+
+This command can also be used on @samp{Removed} files (before you commit
+them) to resurrect them.
+
+Selected files that are neither @samp{Unknown} nor @samp{Removed} will
+be ignored by this command.
+
+The command that is run is @code{cvs-add}.
+
+@item r
+This command removes the selected files (after prompting for
+confirmation). The files are @samp{rm}ed from your directory and
+(unless the status was @samp{Unknown}; @pxref{File status}) they will
+also be @samp{cvs remove}d. If the files were @samp{Unknown} they will
+disappear from the buffer. Otherwise their status will change to
+@samp{Removed}, and you must use @kbd{c} (@samp{cvs-commit},
+@pxref{Committing changes}) to commit the removal.@refill
+
+The command that is run is @code{cvs-remove-file}.
+@end table
+
+@node Removing handled entries, Ignoring files, Adding and removing files, Commands
+@comment node-name, next, previous, up
+@section Removing handled entries
+@cindex Expunging uninteresting entries
+@cindex Uninteresting entries, getting rid of them
+@cindex Getting rid of uninteresting lines
+@cindex Removing uninteresting (processed) lines
+@cindex Handled lines, removing them
+@kindex x - remove processed entries
+@kindex C-k - remove selected entries
+@findex cvs-remove-handled
+@findex cvs-acknowledge
+
+@table @kbd
+@item x
+This command allows you to remove all entries that you have processed.
+More specifically, the lines for @samp{Updated} files (@pxref{File
+status} and files that have been checked in (@pxref{Committing changes})
+are removed from the buffer. If a directory becomes empty the heading
+for that directory is also removed. This makes it easier to get an
+overview of what needs to be done.
+
+The command is called @code{cvs-remove-handled}. If
+@samp{cvs-auto-remove-handled} is set to non-@samp{nil} this will
+automatically be performed after every commit.@refill
+
+@item C-k
+This command can be used for lines that @samp{cvs-remove-handled} would
+not delete, but that you want to delete (@code{cvs-acknowledge}).
+@end table
+
+@node Ignoring files, Viewing differences, Removing handled entries, Commands
+@comment node-name, next, previous, up
+@section Ignoring files
+
+@table @kbd
+@item i
+Arrange so that CVS will ignore the selected files. The file names are
+added to the @file{.cvsignore} file in the corresponding directory. If
+the @file{.cvsignore} doesn't exist it will be created.
+
+The @file{.cvsignore} file should normally be added to the repository,
+but you could ignore it also if you like it better that way.
+
+This runs @code{cvs-ignore}.
+@end table
+
+@node Viewing differences, , Ignoring files, Commands
+@comment node-name, next, previous, up
+@section Viewing differences
+@cindex Diff
+@cindex Conflicts, how to resolve them
+@cindex Viewing differences
+@kindex d - run @samp{cvs diff}
+@kindex b - diff backup file
+@findex cvs-diff-cvs
+@findex cvs-diff-backup
+
+@table @kbd
+@item d
+Display a @samp{cvs diff} between the selected files and the RCS version
+that they are based on. @xref{Customization} describes how you can send
+flags to @samp{cvs diff}. (The function that does the job is
+@code{cvs-diff-cvs}).@refill
+
+@item b
+If CVS finds a conflict while merging two versions of a file (during a
+@samp{cvs update}, @pxref{Updating the directory}) it will save the
+original file in a file called @file{.#@var{FILE}.@var{VERSION}} where
+@var{FILE} is the name of the file, and @var{VERSION} is the RCS version
+number that your file was based on.
+
+With the @kbd{b} command you can run a @samp{diff} on the files
+@file{.#@var{FILE}.@var{VERSION}} and @file{@var{FILE}}. You can get a
+context- or Unidiff by setting @samp{cvs-diff-flags} -
+@pxref{Customization}. This command only works on files that have
+status @samp{Conflict} or @samp{Merged}. The name of the command is
+@code{cvs-diff-backup}. @refill
+@end table
+
+@node Customization, Future enhancements, Commands, Top
+@comment node-name, next, previous, up
+@chapter Customization
+@vindex cvs-erase-input-buffer (variable)
+@vindex cvs-inhibit-copyright-message (variable)
+@vindex cvs-cvs-diff-flags (variable)
+@vindex cvs-diff-flags (variable)
+@vindex cvs-log-flags (variable)
+@vindex cvs-status-flags (variable)
+@vindex cvs-auto-remove-handled (variable)
+@cindex Inhibiting the Copyright message.
+@cindex Copyright message, getting rid of it
+@cindex Getting rid of the Copyright message.
+@cindex Customization
+@cindex Variables, list of all
+@cindex Erasing the input buffer
+@cindex Context diff, how to get
+@cindex Unidiff, how to get
+@cindex Automatically remove handled files
+
+If you have an idea about any customization that would be handy but
+isn't present in this list, please tell me! @xref{Reporting bugs and
+ideas} for info on how to reach me.@refill
+
+@table @samp
+@item cvs-erase-input-buffer
+If set to anything else than @samp{nil} the edit buffer will be erased
+before you write the log message (@pxref{Committing changes}).
+
+@item cvs-inhibit-copyright-message
+The copyright message that is displayed on startup can be annoying after
+a while. Set this variable to @samp{t} if you want to get rid of it.
+(But don't set this to @samp{t} in the system defaults file - new users
+should see this message at least once).
+
+@item cvs-cvs-diff-flags
+A list of strings to pass as arguments to the @samp{cvs diff} program.
+This is used by @samp{cvs-diff-cvs} (key @kbd{d}, @pxref{Viewing
+differences}). If you prefer the Unidiff format you could add this line
+to your @file{.emacs} file:@refill
+
+@example
+(setq cvs-cvs-diff-flags '("-u"))
+@end example
+
+@item cvs-diff-flags
+Like @samp{cvs-cvs-diff-flags}, but passed to @samp{diff}. This is used
+by @samp{cvs-diff-backup} (key @kbd{b}, @pxref{Viewing differences}).
+
+@item cvs-log-flags
+List of strings to send to @samp{cvs log}. Used by @samp{cvs-log} (key
+@kbd{l}, @pxref{Getting info about files}).
+
+@item cvs-status-flags
+List of strings to send to @samp{cvs status}. Used by @samp{cvs-status}
+(key @kbd{s}, @pxref{Getting info about files}).
+
+@item cvs-auto-remove-handled
+If this variable is set to any non-@samp{nil} value
+@samp{cvs-remove-handled} will be called every time you check in files,
+after the check-in is ready. @xref{Removing handled entries}.@refill
+
+@end table
+@node Future enhancements, Reporting bugs and ideas, Customization, Top
+@comment node-name, next, previous, up
+@chapter Future enhancements
+@cindex Enhancements
+
+Pcl-cvs is still under development and needs a number of enhancements to
+be called complete. Here is my current wish-list for future releases of
+pcl-cvs:
+
+@itemize @bullet
+@item
+Dired support. I have an experimental @file{dired-cvs.el} that works
+together with CVS 1.2. Unfortunately I wrote it on top of a
+non-standard @file{dired.el}, so it must be rewritten.@refill
+
+@item
+It should be possible to run commands such as @samp{cvs log}, @samp{cvs
+status} and @samp{cvs commit} directly from a buffer containing a file,
+instead of having to @samp{cvs-update}. If the directory contains many
+files the @samp{cvs-update} can take quite some time, especially on a
+slow machine.
+@end itemize
+
+
+If you miss something in this wish-list, let me know! I don't promise
+that I will write it, but I will at least try to coordinate the efforts
+of making a good Emacs front end to CVS. See @xref{Reporting bugs and
+ideas} for information about how to reach me.@refill
+
+
+@node Reporting bugs and ideas, Function and Variable Index, Future enhancements, Top
+@comment node-name, next, previous, up
+@chapter Reporting bugs and ideas
+@cindex Reporting bugs and ideas
+@cindex Bugs, how to report them
+@cindex Author, how to reach
+@cindex Email to the author
+
+If you find a bug or misfeature, don't hesitate to tell me! Send email
+to @samp{ceder@@lysator.liu.se}.
+
+If you have ideas for improvements, or if you have written some
+extensions to this package, I would like to hear from you. I hope that
+you find this package useful!
+
+
+@node Function and Variable Index, Concept Index, Reporting bugs and ideas, Top
+@comment node-name, next, previous, up
+@unnumbered Function and Variable Index
+
+@printindex fn
+
+@node Concept Index, Key Index, Function and Variable Index, Top
+@comment node-name, next, previous, up
+@unnumbered Concept Index
+
+@printindex cp
+
+@node Key Index, , Concept Index, Top
+@comment node-name, next, previous, up
+@unnumbered Key Index
+
+@printindex ky
+
+@summarycontents
+@contents
+@bye
diff --git a/gnu/usr.bin/cvs/contrib/rcs-to-cvs b/gnu/usr.bin/cvs/contrib/rcs-to-cvs
new file mode 100644
index 000000000000..1a241b9a33ef
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcs-to-cvs
@@ -0,0 +1,208 @@
+#!/bin/csh
+#
+# rcs-to-cvs,v 1.3 1992/04/10 03:04:25 berliner Exp
+# Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+#
+# Copyright (c) 1989, 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.0 kit.
+#
+#############################################################################
+# #
+# This script is used to check in sources that previously was under RCS or #
+# no source control system. #
+# #
+# Usage: rcs-to-cvs repository #
+# #
+# The repository is the directory where the sources should #
+# be deposited.
+# #
+# checkin 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 0.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. #
+# #
+#############################################################################
+
+set vbose = 0
+set message = ""
+set cvsbin = /usr/gnu/bin
+set rcsbin = /usr/gnu/bin
+set grep = /bin/grep
+set message_file = /usr/tmp/checkin.$$
+set got_one = 0
+
+if ( $#argv < 1 ) then
+ echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
+ exit 1
+endif
+while ( $#argv )
+ switch ( $argv[1] )
+ case -v:
+ set vbose = 1
+ breaksw
+ case -m:
+ shift
+ echo $argv[1] > $message_file
+ set got_one = 1
+ breaksw
+ case -f:
+ shift
+ set message_file = $argv[1]
+ set got_one = 2
+ breaksw
+ default:
+ break
+ endsw
+ shift
+end
+if ( $#argv < 1 ) then
+ echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
+ exit 1
+endif
+set repository = $argv[1]
+shift
+
+if ( ! $?CVSROOT ) then
+ echo "Please set the environmental variable CVSROOT to the root"
+ echo " of the tree you wish to update"
+ exit 1
+endif
+
+if ( $got_one == 0 ) then
+ echo "Please Edit this file to contain the RCS log information" >$message_file
+ echo "to be associated with this file (please remove these lines)">>$message_file
+ if ( $?EDITOR ) then
+ $EDITOR $message_file > /dev/tty
+ else
+ /usr/ucb/vi $message_file > /dev/tty
+ endif
+ set got_one = 1
+endif
+
+umask 22
+
+set update_dir = ${CVSROOT}/${repository}
+if ( -d SCCS ) then
+ echo SCCS files detected!
+ exit 1
+endif
+if ( -d RCS ) then
+ $rcsbin/co RCS/* >& /dev/null
+endif
+foreach name ( * .[a-zA-Z0-9]* )
+ echo $name
+ if ( "$name" == SCCS ) then
+ continue
+ endif
+ if ( "$name" == RCS ) then
+ continue
+ endif
+ if ( $vbose ) then
+ echo "Updating ${repository}/${name}"
+ endif
+ if ( -d "$name" ) then
+ if ( ! -d "${update_dir}/${name}" ) then
+ echo "WARNING: Creating new directory ${repository}/${name}"
+ mkdir "${update_dir}/${name}"
+ if ( $status ) then
+ echo "ERROR: mkdir failed - aborting"
+ exit 1
+ endif
+ endif
+ chdir "$name"
+ if ( $status ) then
+ echo "ERROR: Couldn\'t chdir to "$name" - aborting"
+ exit 1
+ endif
+ if ( $vbose ) then
+ rcs-to-cvs -v -f $message_file "${repository}/${name}"
+ else
+ rcs-to-cvs -f $message_file "${repository}/${name}"
+ endif
+ if ( $status ) then
+ exit 1
+ endif
+ chdir ..
+ else # if not directory
+ if ( ! -f "$name" ) then
+ echo "WARNING: "$name" is neither a regular file"
+ echo " nor a directory - ignored"
+ continue
+ endif
+ set file = "${update_dir}/${name},v"
+ set new = 0
+ set comment = ""
+ grep -s '\$Log.*\$' "${name}"
+ if ( $status == 0 ) then # If $Log keyword
+ set myext = ${name:e}
+ set knownext = 0
+ foreach xx ( "c" "csh" "e" "f" "h" "l" "mac" "me" "mm" "ms" "p" "r" "red" "s" "sh" "sl" "cl" "ml" "el" "tex" "y" "ye" "yr" "" )
+ if ( "${myext}" == "${xx}" ) then
+ set knownext = 1
+ break
+ endif
+ end
+ if ( $knownext == 0 ) then
+ echo For file ${file}:
+ grep '\$Log.*\$' "${name}"
+ echo -n "Please insert a comment leader for file ${name} > "
+ set comment = $<
+ endif
+ endif
+ 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 ( "${comment}" != "" ) then
+ $rcsbin/rcs -q -i -c"${comment}" -t${message_file} -m'.' "$file"
+ endif
+ $rcsbin/ci -q -u0.1 -t${message_file} -m'.' "$file"
+ if ( $status ) then
+ echo "ERROR: Initial check-in of $file failed - aborting"
+ exit 1
+ endif
+ set new = 1
+ endif
+ else
+ set file = "${update_dir}/Attic/${name},v"
+ echo "WARNING: IGNORED: ${repository}/Attic/${name}"
+ continue
+ endif
+ else # File existed
+ echo ERROR: File exists: Ignored: "$file"
+ continue
+# set headbranch = `sed -n '/^head/p; /^branch/p; 2q' $file`
+# if ( $#headbranch != 2 && $#headbranch != 4 ) then
+# echo "ERROR: corrupted RCS file $file - aborting"
+# endif
+# set head = "$headbranch[2]"
+# set branch = ""
+# if ( $#headbranch == 4 ) then
+# set branch = "$headbranch[4]"
+# endif
+# if ( "$head" == "1.1;" && "$branch" != "1.1.1;" ) then
+# ${rcsbin}/rcsdiff -q -r1.1 $file > /dev/null
+# if ( ! $status ) then
+# set new = 1
+# endif
+# else
+# if ( "$branch" != "1.1.1;" ) then
+# echo -n "WARNING: Updating locally modified file "
+# echo "${repository}/${name}"
+# endif
+# endif
+ endif
+ endif
+end
+if ( $got_one == 1 ) rm $message_file
diff --git a/gnu/usr.bin/cvs/contrib/rcslock.pl b/gnu/usr.bin/cvs/contrib/rcslock.pl
new file mode 100644
index 000000000000..db09b4bf2ade
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcslock.pl
@@ -0,0 +1,234 @@
+#!/usr/bin/perl
+
+# Author: John Rouillard (rouilj@cs.umb.edu)
+# Supported: Yeah right. (Well what do you expect for 2 hours work?)
+# Blame-to: rouilj@cs.umb.edu
+# Complaints to: Anybody except Brian Berliner, he's blameless for
+# this script.
+# Acknowlegements: The base code for this script has been acquired
+# from the log.pl script.
+
+# rcslock.pl - A program to prevent commits when a file to be ckecked
+# in is locked in the repository.
+
+# There are times when you need exclusive access to a file. This
+# often occurs when binaries are checked into the repository, since
+# cvs's (actually rcs's) text based merging mechanism won't work. This
+# script allows you to use the rcs lock mechanism (rcs -l) to make
+# sure that no changes to a repository are able to be committed if
+# those changes would result in a locked file being changed.
+
+# WARNING:
+# This script will work only if locking is set to strict.
+#
+
+# Setup:
+# Add the following line to the commitinfo file:
+
+# ALL /local/location/for/script/lockcheck [options]
+
+# Where ALL is replaced by any suitable regular expression.
+# Options are -v for verbose info, or -d for debugging info.
+# The %s will provide the repository directory name and the names of
+# all changed files.
+
+# Use:
+# When a developer needs exclusive access to a version of a file, s/he
+# should use "rcs -l" in the repository tree to lock the version they
+# are working on. CVS will automagically release the lock when the
+# commit is performed.
+
+# Method:
+# An "rlog -h" is exec'ed to give info on all about to be
+# committed files. This (header) information is parsed to determine
+# if any locks are outstanding and what versions of the file are
+# locked. This filename, version number info is used to index an
+# associative array. All of the files to be committed are checked to
+# see if any locks are outstanding. If locks are outstanding, the
+# version number of the current file (taken from the CVS/Entries
+# subdirectory) is used in the key to determine if that version is
+# locked. If the file being checked in is locked by the person doing
+# the checkin, the commit is allowed, but if the lock is held on that
+# version of a file by another person, the commit is not allowed.
+
+$ext = ",v"; # The extension on your rcs files.
+
+$\="\n"; # I hate having to put \n's at the end of my print statements
+$,=' '; # Spaces should occur between arguments to print when printed
+
+# turn off setgid
+#
+$) = $(;
+
+#
+# parse command line arguments
+#
+require 'getopts.pl';
+
+&Getopts("vd"); # verbose or debugging
+
+# Verbose is useful when debugging
+$opt_v = $opt_d if defined $opt_d;
+
+# $files[0] is really the name of the subdirectory.
+# @files = split(/ /,$ARGV[0]);
+@files = @ARGV[0..$#ARGV];
+$cvsroot = $ENV{'CVSROOT'};
+
+#
+# get login name
+#
+$login = getlogin || (getpwuid($<))[0] || "nobody";
+
+#
+# save the current directory since we have to return here to parse the
+# CVS/Entries file if a lock is found.
+#
+$pwd = `/bin/pwd`;
+chop $pwd;
+
+print "Starting directory is $pwd" if defined $opt_d ;
+
+#
+# cd to the repository directory and check on the files.
+#
+print "Checking directory ", $files[0] if defined $opt_v ;
+
+if ( $files[0] =~ /^\// )
+{
+ print "Directory path is $files[0]" if defined $opt_d ;
+ chdir $files[0] || die "Can't change to repository directory $files[0]" ;
+}
+else
+{
+ print "Directory path is $cvsroot/$files[0]" if defined $opt_d ;
+ chdir ($cvsroot . "/" . $files[0]) ||
+ die "Can't change to repository directory $files[0] in $cvsroot" ;
+}
+
+
+# Open the rlog process and apss all of the file names to that one
+# process to cut down on exec overhead. This may backfire if there
+# are too many files for the system buffer to handle, but if there are
+# that many files, chances are that the cvs repository is not set up
+# cleanly.
+
+print "opening rlog -h @files[1..$#files] |" if defined $opt_d;
+
+open( RLOG, "rlog -h @files[1..$#files] |") || die "Can't run rlog command" ;
+
+# Create the locks associative array. The elements in the array are
+# of two types:
+#
+# The name of the RCS file with a value of the total number of locks found
+# for that file,
+# or
+#
+# The name of the rcs file concatenated with the version number of the lock.
+# The value of this element is the name of the locker.
+
+# The regular expressions used to split the rcs info may have to be changed.
+# The current ones work for rcs 5.6.
+
+$lock = 0;
+
+while (<RLOG>)
+{
+ chop;
+ next if /^$/; # ditch blank lines
+
+ if ( $_ =~ /^RCS file: (.*)$/ )
+ {
+ $curfile = $1;
+ next;
+ }
+
+ if ( $_ =~ /^locks: strict$/ )
+ {
+ $lock = 1 ;
+ next;
+ }
+
+ if ( $lock )
+ {
+ # access list: is the line immediately following the list of locks.
+ if ( /^access list:/ )
+ { # we are done getting lock info for this file.
+ $lock = 0;
+ }
+ else
+ { # We are accumulating lock info.
+
+ # increment the lock count
+ $locks{$curfile}++;
+ # save the info on the version that is locked. $2 is the
+ # version number $1 is the name of the locker.
+ $locks{"$curfile" . "$2"} = $1
+ if /[ ]*([a-zA-Z._]*): ([0-9.]*)$/;
+
+ print "lock by $1 found on $curfile version $2" if defined $opt_d;
+
+ }
+ }
+}
+
+# Lets go back to the starting directory and see if any locked files
+# are ones we are interested in.
+
+chdir $pwd;
+
+# fo all of the file names (remember $files[0] is the directory name
+foreach $i (@files[1..$#files])
+{
+ if ( defined $locks{$i . $ext} )
+ { # well the file has at least one lock outstanding
+
+ # find the base version number of our file
+ &parse_cvs_entry($i,*entry);
+
+ # is our version of this file locked?
+ if ( defined $locks{$i . $ext . $entry{"version"}} )
+ { # if so, it is by us?
+ if ( $login ne ($by = $locks{$i . $ext . $entry{"version"}}) )
+ {# crud somebody else has it locked.
+ $outstanding_lock++ ;
+ print "$by has file $i locked for version " , $entry{"version"};
+ }
+ else
+ { # yeah I have it locked.
+ print "You have a lock on file $i for version " , $entry{"version"}
+ if defined $opt_v;
+ }
+ }
+ }
+}
+
+exit $outstanding_lock;
+
+
+### End of main program
+
+sub parse_cvs_entry
+{ # a very simple minded hack at parsing an entries file.
+local ( $file, *entry ) = @_;
+local ( @pp );
+
+
+open(ENTRIES, "< CVS/Entries") || die "Can't open entries file";
+
+while (<ENTRIES>)
+ {
+ if ( $_ =~ /^\/$file\// )
+ {
+ @pp = split('/');
+
+ $entry{"name"} = $pp[1];
+ $entry{"version"} = $pp[2];
+ $entry{"dates"} = $pp[3];
+ $entry{"name"} = $pp[4];
+ $entry{"name"} = $pp[5];
+ $entry{"sticky"} = $pp[6];
+ return;
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/contrib/sccs2rcs b/gnu/usr.bin/cvs/contrib/sccs2rcs
new file mode 100644
index 000000000000..a208645aa68e
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/sccs2rcs
@@ -0,0 +1,277 @@
+#!/bin/csh -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.
+#
+# sccs2rcs,v 1.1 1992/04/10 03:04:26 berliner 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/Makefile b/gnu/usr.bin/cvs/cvs/Makefile
new file mode 100644
index 000000000000..5be0fbe3238c
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/Makefile
@@ -0,0 +1,18 @@
+PROG = cvs
+CFLAGS += -I${.CURDIR}/../lib \
+ -DDIRENT -DSTDC_HEADERS -DPOSIX -DBROKEN_SIGISMEMBER \
+ -DFTIME_MISSING -DHAVE_TIMEZONE -DUTIME_NULL_MISSING
+
+LDADD= -L${.CURDIR}/../lib/obj -lcvs
+
+SRCS = add.c admin.c checkin.c checkout.c classify.c commit.c \
+create_adm.c diff.c entries.c find_names.c history.c ignore.c \
+import.c lock.c log.c logmsg.c main.c rcs.c modules.c \
+no_diff.c parseinfo.c patch.c recurse.c release.c remove.c repos.c rtag.c \
+status.c tag.c update.c vers_ts.c version.c
+
+MAN1= cvs.0
+MAN5= cvs.0
+
+.include <bsd.prog.mk>
+.include "../../Makefile.inc"
diff --git a/gnu/usr.bin/cvs/cvs/add.c b/gnu/usr.bin/cvs/cvs/add.c
new file mode 100644
index 000000000000..b4925bfa1280
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/add.c
@@ -0,0 +1,447 @@
+/*
+ * 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.3 kit.
+ *
+ * Add
+ *
+ * Adds a file or directory to the RCS source repository. For a file,
+ * the entry is marked as "needing to be added" in the user's own CVS
+ * directory, and really added to the repository when it is committed.
+ * For a directory, it is added at the appropriate place in the source
+ * repository and a CVS directory is generated within the directory.
+ *
+ * The -m option is currently the only supported option. Some may wish to
+ * supply standard "rcs" options here, but I've found that this causes more
+ * trouble than anything else.
+ *
+ * The user files or directories must already exist. For a directory, it must
+ * not already have a CVS file in it.
+ *
+ * An "add" on a file that has been "remove"d but not committed will cause the
+ * file to be resurrected.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)add.c 1.46 92/04/03";
+#endif
+
+#if __STDC__
+static int add_directory (char *repository, char *dir);
+static int build_entry (char *repository, char *user, char *options,
+ char *message, List * entries);
+#else
+static int add_directory ();
+static int build_entry ();
+#endif /* __STDC__ */
+
+static char *add_usage[] =
+{
+ "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
+ "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
+ "\t-m\tUse \"message\" for the creation log.\n",
+ NULL
+};
+
+int
+add (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char message[MAXMESGLEN];
+ char *user;
+ int i;
+ char *repository;
+ int c;
+ int err = 0;
+ int added_files = 0;
+ char *options = NULL;
+ List *entries;
+ Vers_TS *vers;
+
+ if (argc == 1 || argc == -1)
+ usage (add_usage);
+
+ /* parse args */
+ message[0] = '\0';
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "k:m:")) != -1)
+ {
+ switch (c)
+ {
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+
+ case 'm':
+ if (strlen (optarg) >= sizeof (message))
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, sizeof (message));
+ message[sizeof (message) - 1] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case '?':
+ default:
+ usage (add_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0)
+ usage (add_usage);
+
+ /* find the repository associated with our current dir */
+ repository = Name_Repository ((char *) NULL, (char *) NULL);
+ entries = ParseEntries (0);
+
+ /* walk the arg list adding files/dirs */
+ for (i = 0; i < argc; i++)
+ {
+ int begin_err = err;
+
+ user = argv[i];
+ if (index (user, '/') != NULL)
+ {
+ error (0, 0,
+ "cannot add files with '/' in their name; %s not added", user);
+ err++;
+ continue;
+ }
+
+ vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
+ user, 0, 0, entries, (List *) NULL);
+ if (vers->vn_user == NULL)
+ {
+ /* No entry available, ts_rcs is invalid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* There is no RCS file either */
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file either */
+ error (0, 0, "nothing known about %s", user);
+ err++;
+ }
+ else if (!isdir (user))
+ {
+ /*
+ * See if a directory exists in the repository with
+ * the same name. If so, blow this request off.
+ */
+ char dname[PATH_MAX];
+ (void) sprintf (dname, "%s/%s", repository, user);
+ if (isdir (dname))
+ {
+ error (0, 0,
+ "cannot add file `%s' since the directory",
+ user);
+ error (0, 0, "`%s' already exists in the repository",
+ dname);
+ error (1, 0, "illegal filename overlap");
+ }
+
+ /* There is a user file, so build the entry for it */
+ if (build_entry (repository, user, vers->options,
+ message, entries) != 0)
+ err++;
+ else if (!quiet)
+ {
+ added_files++;
+ error (0, 0, "scheduling file `%s' for addition",
+ user);
+ }
+ }
+ }
+ else
+ {
+
+ /*
+ * There is an RCS file already, so somebody else must've
+ * added it
+ */
+ error (0, 0, "%s added independently by second party", user);
+ err++;
+ }
+ }
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ {
+
+ /*
+ * An entry for a new-born file, ts_rcs is dummy, but that is
+ * inappropriate here
+ */
+ error (0, 0, "%s has already been entered", user);
+ err++;
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ /* An entry for a removed file, ts_rcs is invalid */
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file (as it should be) */
+ if (vers->vn_rcs == NULL)
+ {
+
+ /*
+ * There is no RCS file, so somebody else must've removed
+ * it from under us
+ */
+ error (0, 0,
+ "cannot resurrect %s; RCS file removed by second party", user);
+ err++;
+ }
+ else
+ {
+
+ /*
+ * There is an RCS file, so remove the "-" from the
+ * version number and restore the file
+ */
+ char *tmp = xmalloc (strlen (user) + 50);
+
+ (void) strcpy (tmp, vers->vn_user + 1);
+ (void) strcpy (vers->vn_user, tmp);
+ (void) sprintf (tmp, "Resurrected %s", user);
+ Register (entries, user, vers->vn_user, tmp, vers->options,
+ vers->tag, vers->date);
+ free (tmp);
+
+ /* XXX - bugs here; this really resurrect the head */
+ if (update (2, argv + i - 1) == 0)
+ {
+ error (0, 0, "%s, version %s, resurrected", user,
+ vers->vn_user);
+ }
+ else
+ {
+ error (0, 0, "could not resurrect %s", user);
+ err++;
+ }
+ }
+ }
+ else
+ {
+ /* The user file shouldn't be there */
+ error (0, 0, "%s should be removed and is still there (or is back again)", user);
+ err++;
+ }
+ }
+ else
+ {
+ /* A normal entry, ts_rcs is valid, so it must already be there */
+ error (0, 0, "%s already exists, with version number %s", user,
+ vers->vn_user);
+ err++;
+ }
+ freevers_ts (&vers);
+
+ /* passed all the checks. Go ahead and add it if its a directory */
+ if (begin_err == err && isdir (user))
+ {
+ err += add_directory (repository, user);
+ continue;
+ }
+ }
+ if (added_files)
+ error (0, 0, "use 'cvs commit' to add %s permanently",
+ (added_files == 1) ? "this file" : "these files");
+ dellist (&entries);
+ return (err);
+}
+
+/*
+ * The specified user file is really a directory. So, let's make sure that
+ * it is created in the RCS source repository, and that the user's directory
+ * is updated to include a CVS directory.
+ *
+ * Returns 1 on failure, 0 on success.
+ */
+static int
+add_directory (repository, dir)
+ char *repository;
+ char *dir;
+{
+ char cwd[PATH_MAX], rcsdir[PATH_MAX];
+ char message[PATH_MAX + 100];
+ char *tag, *date;
+
+ if (index (dir, '/') != NULL)
+ {
+ error (0, 0,
+ "directory %s not added; must be a direct sub-directory", dir);
+ return (1);
+ }
+ if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0)
+ {
+ error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM);
+ return (1);
+ }
+
+ /* before we do anything else, see if we have any per-directory tags */
+ ParseTag (&tag, &date);
+
+ /* now, remember where we were, so we can get back */
+ if (getwd (cwd) == NULL)
+ {
+ error (0, 0, "cannot get working directory: %s", cwd);
+ return (1);
+ }
+ if (chdir (dir) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", dir);
+ return (1);
+ }
+ if (isfile (CVSADM) || isfile (OCVSADM))
+ {
+ error (0, 0,
+ "%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM);
+ goto out;
+ }
+
+ (void) sprintf (rcsdir, "%s/%s", repository, dir);
+ if (isfile (rcsdir) && !isdir (rcsdir))
+ {
+ error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
+ goto out;
+ }
+
+ /* setup the log message */
+ (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
+ if (tag)
+ {
+ (void) strcat (message, "--> Using per-directory sticky tag `");
+ (void) strcat (message, tag);
+ (void) strcat (message, "'\n");
+ }
+ if (date)
+ {
+ (void) strcat (message, "--> Using per-directory sticky date `");
+ (void) strcat (message, date);
+ (void) strcat (message, "'\n");
+ }
+
+ if (!isdir (rcsdir))
+ {
+ mode_t omask;
+ char line[MAXLINELEN];
+ Node *p;
+ List *ulist;
+
+ (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
+ rcsdir);
+ (void) fflush (stdout);
+ clearerr (stdin);
+ if (fgets (line, sizeof (line), stdin) == NULL ||
+ (line[0] != 'y' && line[0] != 'Y'))
+ {
+ error (0, 0, "directory %s not added", rcsdir);
+ goto out;
+ }
+ omask = umask (2);
+ if (mkdir (rcsdir, 0777) < 0)
+ {
+ error (0, errno, "cannot mkdir %s", rcsdir);
+ (void) umask ((int) omask);
+ goto out;
+ }
+ (void) umask ((int) omask);
+
+ /*
+ * Set up an update list with a single title node for Update_Logfile
+ */
+ ulist = getlist ();
+ p = getnode ();
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->key = xstrdup ("- New directory");
+ p->data = (char *) T_TITLE;
+ (void) addnode (ulist, p);
+ Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
+ dellist (&ulist);
+ }
+
+ Create_Admin (".", rcsdir, tag, date);
+ if (tag)
+ free (tag);
+ if (date)
+ free (date);
+
+ (void) printf ("%s", message);
+out:
+ if (chdir (cwd) < 0)
+ error (1, errno, "cannot chdir to %s", cwd);
+ return (0);
+}
+
+/*
+ * Builds an entry for a new file and sets up "CVS/file",[pt] by
+ * interrogating the user. Returns non-zero on error.
+ */
+static int
+build_entry (repository, user, options, message, entries)
+ char *repository;
+ char *user;
+ char *options;
+ char *message;
+ List *entries;
+{
+ char fname[PATH_MAX];
+ char line[MAXLINELEN];
+ FILE *fp;
+
+ /*
+ * There may be an old file with the same name in the Attic! This is,
+ * perhaps, an awkward place to check for this, but other places are
+ * equally awkward.
+ */
+ (void) sprintf (fname, "%s/%s/%s%s", repository, CVSATTIC, user, RCSEXT);
+ if (isreadable (fname))
+ {
+ error (0, 0, "there is an old file %s already in %s/%s", user,
+ repository, CVSATTIC);
+ return (1);
+ }
+
+ if (noexec)
+ return (0);
+
+ /*
+ * The options for the "add" command are store in the file CVS/user,p
+ */
+ (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
+ fp = open_file (fname, "w+");
+ if (fclose (fp) == EOF)
+ error(1, errno, "cannot close %s", fname);
+
+ /*
+ * And the requested log is read directly from the user and stored in the
+ * file user,t. If the "message" argument is set, use it as the
+ * initial creation log (which typically describes the file).
+ */
+ (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
+ fp = open_file (fname, "w+");
+ if (*message && fputs (message, fp) == EOF)
+ error (1, errno, "cannot write to %s", fname);
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", fname);
+
+ /*
+ * Create the entry now, since this allows the user to interrupt us above
+ * without needing to clean anything up (well, we could clean up the ,p
+ * and ,t files, but who cares).
+ */
+ (void) sprintf (line, "Initial %s", user);
+ Register (entries, user, "0", line, options, (char *) 0, (char *) 0);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/admin.c b/gnu/usr.bin/cvs/cvs/admin.c
new file mode 100644
index 000000000000..91d3929a83e2
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/admin.c
@@ -0,0 +1,124 @@
+/*
+ * 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.3 kit.
+ *
+ * Administration
+ *
+ * For now, this is basically a front end for rcs. All options are passed
+ * directly on.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)admin.c 1.17 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype admin_dirproc (char *dir, char *repos, char *update_dir);
+static int admin_fileproc (char *file, char *update_dir,
+ char *repository, List *entries,
+ List *srcfiles);
+#else
+static int admin_fileproc ();
+static Dtype admin_dirproc ();
+#endif /* __STDC__ */
+
+static char *admin_usage[] =
+{
+ "Usage: %s %s rcs-options files...\n",
+ NULL
+};
+
+static int ac;
+static char **av;
+
+int
+admin (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int err;
+
+ if (argc <= 1)
+ usage (admin_usage);
+
+ /* skip all optional arguments to see if we have any file names */
+ for (ac = 1; ac < argc; ac++)
+ if (argv[ac][0] != '-')
+ break;
+ argc -= ac;
+ av = argv + 1;
+ argv += ac;
+ ac--;
+ if (ac == 0 || argc == 0)
+ usage (admin_usage);
+
+ /* start the recursion processor */
+ err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc,
+ (int (*) ()) NULL, argc, argv, 0,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+ return (err);
+}
+
+/*
+ * Called to run "rcs" on a particular file.
+ */
+/* ARGSUSED */
+static int
+admin_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Vers_TS *vers;
+ char *version;
+ char **argv;
+ int argc;
+ int retcode = 0;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 0, 0, entries, srcfiles);
+
+ version = vers->vn_user;
+ if (version == NULL)
+ return (0);
+ else if (strcmp (version, "0") == 0)
+ {
+ error (0, 0, "cannot admin newly added file `%s'", file);
+ return (0);
+ }
+
+ run_setup ("%s%s", Rcsbin, RCS);
+ for (argc = ac, argv = av; argc; argc--, argv++)
+ run_arg (*argv);
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "%s failed for `%s'", RCS, file);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+admin_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Administrating %s", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/checkin.c b/gnu/usr.bin/cvs/cvs/checkin.c
new file mode 100644
index 000000000000..14f7c055e2ae
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/checkin.c
@@ -0,0 +1,135 @@
+/*
+ * 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.3 kit.
+ *
+ * Check In
+ *
+ * Does a very careful checkin of the file "user", and tries not to spoil its
+ * modification time (to avoid needless recompilations). When RCS ID keywords
+ * get expanded on checkout, however, the modification time is updated and
+ * there is no good way to get around this.
+ *
+ * Returns non-zero on error.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)checkin.c 1.40 92/03/31";
+#endif
+
+int
+Checkin (type, file, repository, rcs, rev, tag, message, entries)
+ int type;
+ char *file;
+ char *repository;
+ char *rcs;
+ char *rev;
+ char *tag;
+ char *message;
+ List *entries;
+{
+ char fname[PATH_MAX];
+ Vers_TS *vers;
+
+ (void) printf ("Checking in %s;\n", file);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
+
+ /*
+ * Move the user file to a backup file, so as to preserve its
+ * modification times, then place a copy back in the original file name
+ * for the checkin and checkout.
+ */
+ if (!noexec)
+ copy_file (file, fname);
+
+ run_setup ("%s%s -f %s%s", Rcsbin, RCS_CI,
+ rev ? "-r" : "", rev ? rev : "");
+ run_args ("-m%s", message);
+ run_arg (rcs);
+
+ switch (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL))
+ {
+ case 0: /* everything normal */
+
+ /*
+ * The checkin succeeded, so now check the new file back out and
+ * see if it matches exactly with the one we checked in. If it
+ * does, just move the original user file back, thus preserving
+ * the modes; otherwise, we have no recourse but to leave the
+ * newly checkout file as the user file and remove the old
+ * original user file.
+ */
+
+ /* XXX - make sure -k options are used on the co; and tag/date? */
+ run_setup ("%s%s -q %s%s", Rcsbin, RCS_CO,
+ rev ? "-r" : "", rev ? rev : "");
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ xchmod (file, 1);
+ if (xcmp (file, fname) == 0)
+ rename_file (fname, file);
+ else
+ (void) unlink_file (fname);
+
+ /*
+ * If we want read-only files, muck the permissions here, before
+ * getting the file time-stamp.
+ */
+ if (cvswrite == FALSE)
+ xchmod (file, 0);
+
+ /* for added files with symbolic tags, need to add the tag too */
+ if (type == 'A' && tag && !isdigit (*tag))
+ {
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, tag, rev);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+
+ /* re-register with the new data */
+ vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL,
+ file, 1, 1, entries, (List *) NULL);
+ if (strcmp (vers->options, "-V4") == 0)
+ vers->options[0] = '\0';
+ Register (entries, file, vers->vn_rcs, vers->ts_user, vers->options,
+ vers->tag, vers->date);
+ history_write (type, (char *) 0, vers->vn_rcs, file, repository);
+ freevers_ts (&vers);
+ break;
+
+ case -1: /* fork failed */
+ if (!noexec)
+ error (1, errno, "could not check in %s -- fork failed", file);
+ return (1);
+
+ default: /* ci failed */
+
+ /*
+ * The checkin failed, for some unknown reason, so we restore the
+ * original user file, print an error, and return an error
+ */
+ if (!noexec)
+ {
+ rename_file (fname, file);
+ error (0, 0, "could not check in %s", file);
+ }
+ return (1);
+ }
+
+ /*
+ * When checking in a specific revision, we may have locked the wrong
+ * branch, so to be sure, we do an extra unlock here before
+ * returning.
+ */
+ if (rev)
+ {
+ run_setup ("%s%s -q -u", Rcsbin, RCS);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL);
+ }
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/checkout.c b/gnu/usr.bin/cvs/cvs/checkout.c
new file mode 100644
index 000000000000..ad5a5b8115af
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/checkout.c
@@ -0,0 +1,718 @@
+/*
+ * 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.3 kit.
+ *
+ * Create Version
+ *
+ * "checkout" creates a "version" of an RCS repository. This version is owned
+ * totally by the user and is actually an independent copy, to be dealt with
+ * as seen fit. Once "checkout" has been called in a given directory, it
+ * never needs to be called again. The user can keep up-to-date by calling
+ * "update" when he feels like it; this will supply him with a merge of his
+ * own modifications and the changes made in the RCS original. See "update"
+ * for details.
+ *
+ * "checkout" can be given a list of directories or files to be updated and in
+ * the case of a directory, will recursivley create any sub-directories that
+ * exist in the repository.
+ *
+ * When the user is satisfied with his own modifications, the present version
+ * can be committed by "commit"; this keeps the present version in tact,
+ * usually.
+ *
+ * The call is cvs checkout [options] <module-name>...
+ *
+ * "checkout" creates a directory ./CVS, in which it keeps its administration,
+ * in two files, Repository and Entries. The first contains the name of the
+ * repository. The second contains one line for each registered file,
+ * consisting of the version number it derives from, its time stamp at
+ * derivation time and its name. Both files are normal files and can be
+ * edited by the user, if necessary (when the repository is moved, e.g.)
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)checkout.c 1.67 92/04/10";
+#endif
+
+#if __STDC__
+static char *findslash (char *start, char *p);
+static int build_dirs_and_chdir (char *dir, char *prepath, char *realdir,
+ int sticky);
+static int checkout_proc (int *pargc, char *argv[], char *where,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *omodule,
+ char *msg);
+#else
+static int checkout_proc ();
+static char *findslash ();
+static int build_dirs_and_chdir ();
+#endif /* __STDC__ */
+
+static char *checkout_usage[] =
+{
+ "Usage:\n %s %s [-ANPQcflnpqs] [-r rev | -D date] [-d dir] [-k kopt] modules...\n",
+ "\t-A\tReset any sticky tags/date/kopts.\n",
+ "\t-N\tDon't shorten module paths if -d specified.\n",
+ "\t-P\tPrune empty directories.\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-c\t\"cat\" the module database.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-n\tDo not run module program (if any).\n",
+ "\t-p\tCheck out files to standard output.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-s\tLike -c, but include module status.\n",
+ "\t-r rev\tCheck out revision or tag. (implies -P)\n",
+ "\t-D date\tCheck out revisions as of date. (implies -P)\n",
+ "\t-d dir\tCheck out into dir instead of module name.\n",
+ "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
+ "\t-j rev\tMerge in changes made between current revision and rev.\n",
+ NULL
+};
+
+static char *export_usage[] =
+{
+ "Usage: %s %s [-NPQflnq] [-r rev | -D date] [-d dir] module...\n",
+ "\t-N\tDon't shorten module paths if -d specified.\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-n\tDo not run module program (if any).\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-r rev\tCheck out revision or tag. (implies -P)\n",
+ "\t-D date\tCheck out revisions as of date. (implies -P)\n",
+ "\t-d dir\tCheck out into dir instead of module name.\n",
+ NULL
+};
+
+static int checkout_prune_dirs;
+static int force_tag_match = 1;
+static int pipeout;
+static int aflag;
+static char *options = NULL;
+static char *tag = NULL;
+static char *date = NULL;
+static char *join_rev1 = NULL;
+static char *join_rev2 = NULL;
+static char *preload_update_dir = NULL;
+
+int
+checkout (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ DBM *db;
+ int cat = 0, err = 0, status = 0;
+ int run_module_prog = 1;
+ int local = 0;
+ int shorten = -1;
+ char *where = NULL;
+ char *valid_options, **valid_usage;
+
+ /*
+ * A smaller subset of options are allowed for the export command, which
+ * is essentially like checkout, except that it hard-codes certain
+ * options to be on (like -kv) and takes care to remove the CVS directory
+ * when it has done its duty
+ */
+ if (strcmp (command_name, "export") == 0)
+ {
+ valid_options = "Nnd:flRQqr:D:";
+ valid_usage = export_usage;
+ }
+ else
+ {
+ valid_options = "ANnk:d:flRpQqcsr:D:j:P";
+ valid_usage = checkout_usage;
+ }
+
+ if (argc == -1)
+ usage (valid_usage);
+
+ ign_setup ();
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, valid_options)) != -1)
+ {
+ switch (c)
+ {
+ case 'A':
+ aflag = 1;
+ break;
+ case 'N':
+ shorten = 0;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'P':
+ checkout_prune_dirs = 1;
+ break;
+ case 'p':
+ pipeout = 1;
+ run_module_prog = 0; /* don't run module prog when piping */
+ noexec = 1; /* so no locks will be created */
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'd':
+ where = optarg;
+ if (shorten == -1)
+ shorten = 1;
+ break;
+ case 's':
+ status = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'r':
+ tag = optarg;
+ checkout_prune_dirs = 1;
+ break;
+ case 'D':
+ date = Make_Date (optarg);
+ checkout_prune_dirs = 1;
+ break;
+ case 'j':
+ if (join_rev2)
+ error (1, 0, "only two -j options can be specified");
+ if (join_rev1)
+ join_rev2 = optarg;
+ else
+ join_rev1 = optarg;
+ break;
+ case '?':
+ default:
+ usage (valid_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (shorten == -1)
+ shorten = 0;
+
+ if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)
+ || (tag && date))
+ usage (valid_usage);
+
+ if (where && pipeout)
+ error (1, 0, "-d and -p are mutually exclusive");
+
+ if (strcmp (command_name, "export") == 0)
+ {
+ if (!tag && !date)
+ {
+ error (0, 0, "must specify a tag or date");
+ usage (valid_usage);
+ }
+ if (tag && isdigit (tag[0]))
+ error (1, 0, "tag `%s' must be a symbolic tag", tag);
+ options = RCS_check_kflag ("v");/* -kv must be on */
+ }
+
+ if (cat || status)
+ {
+ cat_module (status);
+ return (0);
+ }
+ db = open_module ();
+
+ /*
+ * if we have more than one argument and where was specified, we make the
+ * where, cd into it, and try to shorten names as much as possible.
+ * Otherwise, we pass the where as a single argument to do_module.
+ */
+ if (argc > 1 && where != NULL)
+ {
+ char repository[PATH_MAX];
+
+ (void) mkdir (where, 0777);
+ if (chdir (where) < 0)
+ error (1, errno, "cannot chdir to %s", where);
+ preload_update_dir = xstrdup (where);
+ where = (char *) NULL;
+ if (!isfile (CVSADM) && !isfile (OCVSADM))
+ {
+ (void) sprintf (repository, "%s/%s", CVSroot, CVSNULLREPOS);
+ if (!isfile (repository))
+ (void) mkdir (repository, 0777);
+ Create_Admin (".", repository, (char *) NULL, (char *) NULL);
+ if (!noexec)
+ {
+ FILE *fp;
+
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ }
+ }
+
+ /*
+ * if where was specified (-d) and we have not taken care of it already
+ * with the multiple arg stuff, and it was not a simple directory name
+ * but rather a path, we strip off everything but the last component and
+ * attempt to cd to the indicated place. where then becomes simply the
+ * last component
+ */
+ if (where != NULL && index (where, '/') != NULL)
+ {
+ char *slash;
+
+ slash = rindex (where, '/');
+ *slash = '\0';
+
+ if (chdir (where) < 0)
+ error (1, errno, "cannot chdir to %s", where);
+
+ preload_update_dir = xstrdup (where);
+
+ where = slash + 1;
+ if (*where == '\0')
+ where = NULL;
+ }
+
+ for (i = 0; i < argc; i++)
+ err += do_module (db, argv[i], CHECKOUT, "Updating", checkout_proc,
+ where, shorten, local, run_module_prog,
+ (char *) NULL);
+ close_module (db);
+ return (err);
+}
+
+/*
+ * process_module calls us back here so we do the actual checkout stuff
+ */
+/* ARGSUSED */
+static int
+checkout_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 err = 0;
+ int which;
+ char *cp;
+ char *cp2;
+ char repository[PATH_MAX];
+ char xwhere[PATH_MAX];
+ char *oldupdate = NULL;
+ char *prepath;
+ char *realdirs;
+
+ /*
+ * OK, so we're doing the checkout! Our args are as follows:
+ * argc,argv contain either dir or dir followed by a list of files
+ * where contains where to put it (if supplied by checkout)
+ * mwhere contains the module name or -d from module file
+ * mfile says do only that part of the module
+ * shorten = TRUE says shorten as much as possible
+ * omodule is the original arg to do_module()
+ */
+
+ /* set up the repository (maybe) for the bottom directory */
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+
+ /* save the original value of preload_update_dir */
+ if (preload_update_dir != NULL)
+ oldupdate = xstrdup (preload_update_dir);
+
+ /* fix up argv[] for the case of partial modules */
+ if (mfile != NULL)
+ {
+ char file[PATH_MAX];
+
+ /* if mfile is really a path, straighten it out first */
+ if ((cp = rindex (mfile, '/')) != NULL)
+ {
+ *cp = 0;
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+
+ /*
+ * Now we need to fill in the where correctly. if !shorten, tack
+ * the rest of the path onto where if where is filled in
+ * otherwise tack the rest of the path onto mwhere and make that
+ * the where
+ *
+ * If shorten is enabled, we might use mwhere to set where if
+ * nobody set it yet, so we'll need to setup mwhere as the last
+ * component of the path we are tacking onto repository
+ */
+ if (!shorten)
+ {
+ if (where != NULL)
+ (void) sprintf (xwhere, "%s/%s", where, mfile);
+ else
+ (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
+ where = xwhere;
+ }
+ else
+ {
+ char *slash;
+
+ if ((slash = rindex (mfile, '/')) != NULL)
+ mwhere = slash + 1;
+ else
+ mwhere = mfile;
+ }
+ mfile = cp + 1;
+ }
+
+ (void) sprintf (file, "%s/%s", repository, mfile);
+ if (isdir (file))
+ {
+
+ /*
+ * The portion of a module was a directory, so kludge up where to
+ * be the subdir, and fix up repository
+ */
+ (void) strcpy (repository, file);
+
+ /*
+ * At this point, if shorten is not enabled, we make where either
+ * where with mfile concatenated, or if where hadn't been set we
+ * set it to mwhere with mfile concatenated.
+ *
+ * If shorten is enabled and where hasn't been set yet, then where
+ * becomes mfile
+ */
+ if (!shorten)
+ {
+ if (where != NULL)
+ (void) sprintf (xwhere, "%s/%s", where, mfile);
+ else
+ (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
+ where = xwhere;
+ }
+ else if (where == NULL)
+ where = mfile;
+ }
+ else
+ {
+ int i;
+
+ /*
+ * The portion of a module was a file, so kludge up argv to be
+ * correct
+ */
+ for (i = 1; i < *pargc; i++)/* free the old ones */
+ free (argv[i]);
+ argv[1] = xstrdup (mfile); /* set up the new one */
+ *pargc = 2;
+
+ /* where gets mwhere if where isn't set */
+ if (where == NULL)
+ where = mwhere;
+ }
+ }
+
+ /*
+ * if shorten is enabled and where isn't specified yet, we pluck the last
+ * directory component of argv[0] and make it the where
+ */
+ if (shorten && where == NULL)
+ {
+ if ((cp = rindex (argv[0], '/')) != NULL)
+ {
+ (void) strcpy (xwhere, cp + 1);
+ where = xwhere;
+ }
+ }
+
+ /* if where is still NULL, use mwhere if set or the argv[0] dir */
+ if (where == NULL)
+ {
+ if (mwhere)
+ where = mwhere;
+ else
+ {
+ (void) strcpy (xwhere, argv[0]);
+ where = xwhere;
+ }
+ }
+
+ if (preload_update_dir != NULL)
+ {
+ char tmp[PATH_MAX];
+
+ (void) sprintf (tmp, "%s/%s", preload_update_dir, where);
+ free (preload_update_dir);
+ preload_update_dir = xstrdup (tmp);
+ }
+ else
+ preload_update_dir = xstrdup (where);
+
+ /*
+ * At this point, where is the directory we want to build, repository is
+ * the repository for the lowest level of the path.
+ */
+
+ /*
+ * If we are sending everything to stdout, we can skip a whole bunch of
+ * work from here
+ */
+ if (!pipeout)
+ {
+
+ /*
+ * We need to tell build_dirs not only the path we want it to build,
+ * but also the repositories we want it to populate the path with. To
+ * accomplish this, we pass build_dirs a ``real path'' with valid
+ * repositories and a string to pre-pend based on how many path
+ * elements exist in where. Big Black Magic
+ */
+ prepath = xstrdup (repository);
+ cp = rindex (where, '/');
+ cp2 = rindex (prepath, '/');
+ while (cp != NULL)
+ {
+ cp = findslash (where, cp - 1);
+ cp2 = findslash (prepath, cp2 - 1);
+ }
+ *cp2 = '\0';
+ realdirs = cp2 + 1;
+
+ /*
+ * build dirs on the path if necessary and leave us in the bottom
+ * directory (where if where was specified) doesn't contain a CVS
+ * subdir yet, but all the others contain CVS and Entries.Static
+ * files
+ */
+ if (build_dirs_and_chdir (where, prepath, realdirs, *pargc <= 1) != 0)
+ {
+ error (0, 0, "ignoring module %s", omodule);
+ free (prepath);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+
+ /* clean up */
+ free (prepath);
+
+ /* set up the repository (or make sure the old one matches) */
+ if (!isfile (CVSADM) && !isfile (OCVSADM))
+ {
+ FILE *fp;
+
+ if (!noexec && *pargc > 1)
+ {
+ Create_Admin (".", repository, (char *) NULL, (char *) NULL);
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ else
+ Create_Admin (".", repository, tag, date);
+ }
+ else
+ {
+ char *repos;
+
+ /* get the contents of the previously existing repository */
+ repos = Name_Repository ((char *) NULL, preload_update_dir);
+ if (strcmp (repository, repos) != 0)
+ {
+ error (0, 0, "existing repository %s does not match %s",
+ repos, repository);
+ error (0, 0, "ignoring module %s", omodule);
+ free (repos);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+ free (repos);
+ }
+ }
+
+ /*
+ * If we are going to be updating to stdout, we need to cd to the
+ * repository directory so the recursion processor can use the current
+ * directory as the place to find repository information
+ */
+ if (pipeout)
+ {
+ if (chdir (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+ which = W_REPOS;
+ }
+ else
+ which = W_LOCAL | W_REPOS;
+
+ if (tag != NULL || date != NULL)
+ which |= W_ATTIC;
+
+ /*
+ * if we are going to be recursive (building dirs), go ahead and call the
+ * update recursion processor. We will be recursive unless either local
+ * only was specified, or we were passed arguments
+ */
+ if (!(local_specified || *pargc > 1))
+ {
+ if (strcmp (command_name, "export") != 0 && !pipeout)
+ history_write ('O', preload_update_dir, tag ? tag : date, where,
+ repository);
+ err += do_update (0, (char **) NULL, options, tag, date,
+ force_tag_match, 0 /* !local */ ,
+ 1 /* update -d */ , aflag, checkout_prune_dirs,
+ pipeout, which, join_rev1, join_rev2,
+ preload_update_dir);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (err);
+ }
+
+ if (!pipeout)
+ {
+ int i;
+ List *entries;
+
+ /* we are only doing files, so register them */
+ entries = ParseEntries (0);
+ for (i = 1; i < *pargc; i++)
+ {
+ char line[MAXLINELEN];
+ char *user;
+ Vers_TS *vers;
+
+ user = argv[i];
+ vers = Version_TS (repository, options, tag, date, user,
+ force_tag_match, 0, entries, (List *) NULL);
+ if (vers->ts_user == NULL)
+ {
+ (void) sprintf (line, "Initial %s", user);
+ Register (entries, user, vers->vn_rcs, line, vers->options,
+ vers->tag, vers->date);
+ }
+ freevers_ts (&vers);
+ }
+ dellist (&entries);
+ }
+
+ /* Don't log "export", just regular "checkouts" */
+ if (strcmp (command_name, "export") != 0 && !pipeout)
+ history_write ('O', preload_update_dir, (tag ? tag : date), where,
+ repository);
+
+ /* go ahead and call update now that everything is set */
+ err += do_update (*pargc - 1, argv + 1, options, tag, date,
+ force_tag_match, local_specified, 1 /* update -d */,
+ aflag, checkout_prune_dirs, pipeout, which, join_rev1,
+ join_rev2, preload_update_dir);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (err);
+}
+
+static char *
+findslash (start, p)
+ char *start;
+ char *p;
+{
+ while ((int) p >= (int) start && *p != '/')
+ p--;
+ if ((int) p < (int) start)
+ return (NULL);
+ else
+ return (p);
+}
+
+/*
+ * build all the dirs along the path to dir with CVS subdirs with appropriate
+ * repositories and Entries.Static files
+ */
+static int
+build_dirs_and_chdir (dir, prepath, realdir, sticky)
+ char *dir;
+ char *prepath;
+ char *realdir;
+ int sticky;
+{
+ FILE *fp;
+ char repository[PATH_MAX];
+ char path[PATH_MAX];
+ char path2[PATH_MAX];
+ char *slash;
+ char *slash2;
+ char *cp;
+ char *cp2;
+
+ (void) strcpy (path, dir);
+ (void) strcpy (path2, realdir);
+ for (cp = path, cp2 = path2;
+ (slash = index (cp, '/')) != NULL && (slash2 = index (cp2, '/')) != NULL;
+ cp = slash + 1, cp2 = slash2 + 1)
+ {
+ *slash = '\0';
+ *slash2 = '\0';
+ (void) mkdir (cp, 0777);
+ if (chdir (cp) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", cp);
+ return (1);
+ }
+ if (!isfile (CVSADM) && !isfile (OCVSADM) &&
+ strcmp (command_name, "export") != 0)
+ {
+ (void) sprintf (repository, "%s/%s", prepath, path2);
+ Create_Admin (".", repository, sticky ? (char *) NULL : tag,
+ sticky ? (char *) NULL : date);
+ if (!noexec)
+ {
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ }
+ *slash = '/';
+ *slash2 = '/';
+ }
+ (void) mkdir (cp, 0777);
+ if (chdir (cp) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", cp);
+ return (1);
+ }
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/classify.c b/gnu/usr.bin/cvs/cvs/classify.c
new file mode 100644
index 000000000000..318fab869e96
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/classify.c
@@ -0,0 +1,380 @@
+/*
+ * 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.3 kit.
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)classify.c 1.11 92/03/31";
+#endif
+
+#if __STDC__
+static void sticky_ck (char *file, int aflag, Vers_TS * vers, List * entries);
+#else
+static void sticky_ck ();
+#endif /* __STDC__ */
+
+/*
+ * Classify the state of a file
+ */
+Ctype
+Classify_File (file, tag, date, options, force_tag_match, aflag, repository,
+ entries, srcfiles, versp)
+ char *file;
+ char *tag;
+ char *date;
+ char *options;
+ int force_tag_match;
+ int aflag;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+ Vers_TS **versp;
+{
+ Vers_TS *vers;
+ Ctype ret;
+
+ /* get all kinds of good data about the file */
+ vers = Version_TS (repository, options, tag, date, file,
+ force_tag_match, 0, entries, srcfiles);
+
+ if (vers->vn_user == NULL)
+ {
+ /* No entry available, ts_rcs is invalid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* there is no RCS file either */
+ if (vers->ts_user == NULL)
+ {
+ /* there is no user file */
+ if (!force_tag_match || !(vers->tag || vers->date))
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", file);
+ ret = T_UNKNOWN;
+ }
+ else
+ {
+ /* there is a user file */
+ if (!force_tag_match || !(vers->tag || vers->date))
+ if (!really_quiet)
+ error (0, 0, "use `cvs add' to create an entry for %s",
+ file);
+ ret = T_UNKNOWN;
+ }
+ }
+ else
+ {
+ /* there is an rcs file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file; needs checkout */
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+ /*
+ * There is a user file; print a warning and add it to the
+ * conflict list, only if it is indeed different from what we
+ * plan to extract
+ */
+ if (No_Difference (file, vers, entries))
+ {
+ /* the files were different so it is a conflict */
+ if (!really_quiet)
+ error (0, 0, "move away %s; it is in the way", file);
+ ret = T_CONFLICT;
+ }
+ else
+ /* since there was no difference, still needs checkout */
+ ret = T_CHECKOUT;
+ }
+ }
+ }
+ else if (strcmp (vers->vn_user, "0") == 0)
+ {
+ /* An entry for a new-born file; ts_rcs is dummy */
+
+ if (vers->ts_user == NULL)
+ {
+ /*
+ * There is no user file, but there should be one; remove the
+ * entry
+ */
+ if (!really_quiet)
+ error (0, 0, "warning: new-born %s has disappeared", file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else
+ {
+ /* There is a user file */
+
+ if (vers->vn_rcs == NULL)
+ /* There is no RCS file, added file */
+ ret = T_ADDED;
+ else
+ {
+ /*
+ * There is an RCS file, so someone else must have checked
+ * one in behind our back; conflict
+ */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: %s created independently by second party",
+ file);
+ ret = T_CONFLICT;
+ }
+ }
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ /* An entry for a removed file, ts_rcs is invalid */
+
+ if (vers->ts_user == NULL)
+ {
+ char tmp[PATH_MAX];
+
+ /* There is no user file (as it should be) */
+
+ (void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : "");
+
+ if (vers->vn_rcs == NULL)
+ {
+
+ /*
+ * There is no RCS file; this is all-right, but it has been
+ * removed independently by a second party; remove the entry
+ */
+ ret = T_REMOVE_ENTRY;
+ }
+ else if (strcmp (tmp, vers->vn_user) == 0)
+
+ /*
+ * The RCS file is the same version as the user file was, and
+ * that's OK; remove it
+ */
+ ret = T_REMOVED;
+ else
+ {
+
+ /*
+ * The RCS file is a newer version than the removed user file
+ * and this is definitely not OK; make it a conflict.
+ */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: removed %s was modified by second party",
+ file);
+ ret = T_CONFLICT;
+ }
+ }
+ else
+ {
+ /* The user file shouldn't be there */
+ if (!really_quiet)
+ error (0, 0, "%s should be removed and is still there", file);
+ ret = T_REMOVED;
+ }
+ }
+ else
+ {
+ /* A normal entry, TS_Rcs is valid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* There is no RCS file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file, so just remove the entry */
+ if (!really_quiet)
+ error (0, 0, "warning: %s is not (any longer) pertinent",
+ file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so just remove it from
+ * the entry list
+ */
+ if (!really_quiet)
+ error (0, 0, "%s is no longer in the repository", file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else
+ {
+ /*
+ * The user file has been modified and since it is no longer
+ * in the repository, a conflict is raised
+ */
+ if (No_Difference (file, vers, entries))
+ {
+ /* they are different -> conflict */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: %s is modified but no longer in the repository",
+ file);
+ ret = T_CONFLICT;
+ }
+ else
+ {
+ /* they weren't really different */
+ if (!really_quiet)
+ error (0, 0,
+ "warning: %s is not (any longer) pertinent",
+ file);
+ ret = T_REMOVE_ENTRY;
+ }
+ }
+ }
+ else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
+ {
+ /* The RCS file is the same version as the user file */
+
+ if (vers->ts_user == NULL)
+ {
+
+ /*
+ * There is no user file, so note that it was lost and
+ * extract a new version
+ */
+ if (strcmp (command_name, "update") == 0)
+ if (!really_quiet)
+ error (0, 0, "warning: %s was lost", file);
+ ret = T_CHECKOUT;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so nothing special at
+ * all to do -- no lists updated, unless the sticky -k option
+ * has changed. If the sticky tag has changed, we just need
+ * to re-register the entry
+ */
+ if (vers->entdata->options &&
+ strcmp (vers->entdata->options, vers->options) != 0)
+ ret = T_CHECKOUT;
+ else
+ {
+ sticky_ck (file, aflag, vers, entries);
+ ret = T_UPTODATE;
+ }
+ }
+ else
+ {
+
+ /*
+ * The user file appears to have been modified, but we call
+ * No_Difference to verify that it really has been modified
+ */
+ if (No_Difference (file, vers, entries))
+ {
+
+ /*
+ * they really are different; modified if we aren't
+ * changing any sticky -k options, else needs merge
+ */
+#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
+ if (strcmp (vers->entdata->options ?
+ vers->entdata->options : "", vers->options) == 0)
+ ret = T_MODIFIED;
+ else
+ ret = T_NEEDS_MERGE;
+#else
+ ret = T_MODIFIED;
+ sticky_ck (file, aflag, vers, entries);
+#endif
+ }
+ else
+ {
+ /* file has not changed; check out if -k changed */
+ if (strcmp (vers->entdata->options ?
+ vers->entdata->options : "", vers->options) != 0)
+ {
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+
+ /*
+ * else -> note that No_Difference will Register the
+ * file already for us, using the new tag/date. This
+ * is the desired behaviour
+ */
+ ret = T_UPTODATE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* The RCS file is a newer version than the user file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file, so just get it */
+
+ if (strcmp (command_name, "update") == 0)
+ if (!really_quiet)
+ error (0, 0, "warning: %s was lost", file);
+ ret = T_CHECKOUT;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so just get it as well
+ */
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+ if (No_Difference (file, vers, entries))
+ /* really modified, needs to merge */
+ ret = T_NEEDS_MERGE;
+ else
+ /* not really modified, check it out */
+ ret = T_CHECKOUT;
+ }
+ }
+ }
+
+ /* free up the vers struct, or just return it */
+ if (versp != (Vers_TS **) NULL)
+ *versp = vers;
+ else
+ freevers_ts (&vers);
+
+ /* return the status of the file */
+ return (ret);
+}
+
+static void
+sticky_ck (file, aflag, vers, entries)
+ char *file;
+ int aflag;
+ Vers_TS *vers;
+ List *entries;
+{
+ if (aflag || vers->tag || vers->date)
+ {
+ char *enttag = vers->entdata->tag;
+ char *entdate = vers->entdata->date;
+
+ if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
+ ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
+ (entdate && vers->date && strcmp (entdate, vers->date)) ||
+ ((entdate && !vers->date) || (!entdate && vers->date)))
+ {
+ Register (entries, file, vers->vn_user, vers->ts_rcs,
+ vers->options, vers->tag, vers->date);
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/commit.c b/gnu/usr.bin/cvs/cvs/commit.c
new file mode 100644
index 000000000000..1b2f3be810a5
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/commit.c
@@ -0,0 +1,1229 @@
+/*
+ * 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.3 kit.
+ *
+ * Commit Files
+ *
+ * "commit" commits the present version to the RCS repository, AFTER
+ * having done a test on conflicts.
+ *
+ * The call is: cvs commit [options] files...
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)commit.c 1.84 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype check_direntproc (char *dir, char *repos, char *update_dir);
+static int check_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int check_filesdoneproc (int err, char *repos, char *update_dir);
+static int checkaddfile (char *file, char *repository, char *tag);
+static Dtype commit_direntproc (char *dir, char *repos, char *update_dir);
+static int commit_dirleaveproc (char *dir, int err, char *update_dir);
+static int commit_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int commit_filesdoneproc (int err, char *repository, char *update_dir);
+static int finaladd (char *file, char *revision, char *tag, char *repository,
+ List *entries);
+static int findmaxrev (Node * p);
+static int fsortcmp (Node * p, Node * q);
+static int lock_RCS (char *user, char *rcs, char *rev, char *repository);
+static int lock_filesdoneproc (int err, char *repository, char *update_dir);
+static int lockrcsfile (char *file, char *repository, char *rev);
+static int precommit_list_proc (Node * p);
+static int precommit_proc (char *repository, char *filter);
+static int remove_file (char *file, char *repository, char *tag,
+ List *entries);
+static void fix_rcs_modes (char *rcs, char *user);
+static void fixaddfile (char *file, char *repository);
+static void fixbranch (char *file, char *repository, char *branch);
+static void unlockrcs (char *file, char *repository);
+static void ci_delproc (Node *p);
+static void locate_rcs (char *file, char *repository, char *rcs);
+#else
+static int fsortcmp ();
+static int lock_filesdoneproc ();
+static int check_fileproc ();
+static Dtype check_direntproc ();
+static int precommit_list_proc ();
+static int precommit_proc ();
+static int check_filesdoneproc ();
+static int commit_fileproc ();
+static int commit_filesdoneproc ();
+static Dtype commit_direntproc ();
+static int commit_dirleaveproc ();
+static int findmaxrev ();
+static int remove_file ();
+static int finaladd ();
+static void unlockrcs ();
+static void fixaddfile ();
+static void fixbranch ();
+static int checkaddfile ();
+static int lockrcsfile ();
+static int lock_RCS ();
+static void fix_rcs_modes ();
+static void ci_delproc ();
+static void locate_rcs ();
+#endif /* __STDC__ */
+
+struct commit_info
+{
+ Ctype status; /* as returned from Classify_File() */
+ char *rev; /* a numeric rev, if we know it */
+ char *tag; /* any sticky tag, or -r option */
+};
+struct master_lists
+{
+ List *ulist; /* list for Update_Logfile */
+ List *cilist; /* list with commit_info structs */
+};
+
+static int got_message;
+static int run_module_prog = 1;
+static int aflag;
+static char *tag;
+static char *write_dirtag;
+static char *logfile;
+static List *mulist;
+static List *locklist;
+static char *message;
+
+static char *commit_usage[] =
+{
+ "Usage: %s %s [-nRl] [-m msg | -f logfile] [-r rev] files...\n",
+ "\t-n\tDo not run the module program (if any).\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-l\tLocal directory only (not recursive).\n",
+ "\t-f file\tRead the log message from file.\n",
+ "\t-m msg\tLog message.\n",
+ "\t-r rev\tCommit to this branch or trunk revision.\n",
+ NULL
+};
+
+int
+commit (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (commit_usage);
+
+#ifdef CVS_BADROOT
+ /*
+ * For log purposes, do not allow "root" to commit files. If you look
+ * like root, but are really logged in as a non-root user, it's OK.
+ */
+ if (geteuid () == (uid_t) 0)
+ {
+ struct passwd *pw;
+
+ if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL)
+ error (1, 0, "you are unknown to this system");
+ if (pw->pw_uid == (uid_t) 0)
+ error (1, 0, "cannot commit files as 'root'");
+ }
+#endif /* CVS_BADROOT */
+
+ message = xmalloc (MAXMESGLEN + 1);
+ message[0] = '\0'; /* Null message by default */
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "nlRm:f:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ if (strlen (optarg) >= (size_t) MAXMESGLEN)
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, MAXMESGLEN);
+ message[MAXMESGLEN] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case 'r':
+ if (tag)
+ free (tag);
+ tag = xstrdup (optarg);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'f':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ logfile = optarg;
+ break;
+ case '?':
+ default:
+ usage (commit_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* numeric specified revision means we ignore sticky tags... */
+ if (tag && isdigit (*tag))
+ {
+ aflag = 1;
+ /* strip trailing dots */
+ while (tag[strlen (tag) - 1] == '.')
+ tag[strlen (tag) - 1] = '\0';
+ }
+
+ /* some checks related to the "-f logfile" option */
+ if (logfile)
+ {
+ int n, logfd;
+
+ if (*message)
+ error (1, 0, "cannot specify both a message and a log file");
+
+ if ((logfd = open (logfile, O_RDONLY)) < 0 ||
+ (n = read (logfd, message, MAXMESGLEN)) < 0)
+ {
+ error (1, errno, "cannot read log message from %s", logfile);
+ }
+ (void) close (logfd);
+ message[n] = '\0';
+ }
+
+ /* XXX - this is not the perfect check for this */
+ if (argc <= 0)
+ write_dirtag = tag;
+
+ /*
+ * Run the recursion processor to find all the dirs to lock and lock all
+ * the dirs
+ */
+ locklist = getlist ();
+ err = start_recursion ((int (*) ()) NULL, lock_filesdoneproc,
+ (Dtype (*) ()) NULL, (int (*) ()) NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0);
+ sortlist (locklist, fsortcmp);
+ if (Writer_Lock (locklist) != 0)
+ error (1, 0, "lock failed - giving up");
+
+ /*
+ * Set up the master update list
+ */
+ mulist = getlist ();
+
+ /*
+ * Run the recursion processor to verify the files are all up-to-date
+ */
+ err = start_recursion (check_fileproc, check_filesdoneproc,
+ check_direntproc, (int (*) ()) NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1);
+ if (err)
+ {
+ Lock_Cleanup ();
+ error (1, 0, "correct above errors first!");
+ }
+
+ /*
+ * Run the recursion processor to commit the files
+ */
+ if (noexec == 0)
+ err = start_recursion (commit_fileproc, commit_filesdoneproc,
+ commit_direntproc, commit_dirleaveproc,
+ argc, argv, local, W_LOCAL, aflag, 0,
+ (char *) NULL, 1);
+
+ /*
+ * Unlock all the dirs and clean up
+ */
+ Lock_Cleanup ();
+ dellist (&mulist);
+ dellist (&locklist);
+ return (err);
+}
+
+/*
+ * compare two lock list nodes (for sort)
+ */
+static int
+fsortcmp (p, q)
+ Node *p, *q;
+{
+ return (strcmp (p->key, q->key));
+}
+
+/*
+ * Create a list of repositories to lock
+ */
+/* ARGSUSED */
+static int
+lock_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ Node *p;
+
+ p = getnode ();
+ p->type = LOCK;
+ p->key = xstrdup (repository);
+ if (p->key == NULL || addnode (locklist, p) != 0)
+ freenode (p);
+ return (err);
+}
+
+/*
+ * Check to see if a file is ok to commit and make sure all files are
+ * up-to-date
+ */
+/* ARGSUSED */
+static int
+check_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Ctype status;
+ char *xdir;
+ Node *p;
+ List *ulist, *cilist;
+ Vers_TS *vers;
+ struct commit_info *ci;
+ int save_noexec, save_quiet, save_really_quiet;
+
+ save_noexec = noexec;
+ save_quiet = quiet;
+ save_really_quiet = really_quiet;
+ noexec = quiet = really_quiet = 1;
+
+ /* handle specified numeric revision specially */
+ if (tag && isdigit (*tag))
+ {
+ /* If the tag is for the trunk, make sure we're at the head */
+ if (numdots (tag) < 2)
+ {
+ status = Classify_File (file, (char *) NULL, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_UPTODATE)
+ {
+ freevers_ts (&vers);
+ status = Classify_File (file, tag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ }
+ }
+ else
+ {
+ char *xtag, *cp;
+
+ /*
+ * The revision is off the main trunk; make sure we're
+ * up-to-date with the head of the specified branch.
+ */
+ xtag = xstrdup (tag);
+ if ((numdots (xtag) & 1) != 0)
+ {
+ cp = rindex (xtag, '.');
+ *cp = '\0';
+ }
+ status = Classify_File (file, xtag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
+ && (cp = rindex (xtag, '.')) != NULL)
+ {
+ /* pluck one more dot off the revision */
+ *cp = '\0';
+ freevers_ts (&vers);
+ status = Classify_File (file, xtag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ }
+ /* now, muck with vers to make the tag correct */
+ free (vers->tag);
+ vers->tag = xstrdup (tag);
+ free (xtag);
+ }
+ }
+ else
+ status = Classify_File (file, tag, (char *) NULL, (char *) NULL,
+ 1, 0, repository, entries, srcfiles, &vers);
+ noexec = save_noexec;
+ quiet = save_quiet;
+ really_quiet = save_really_quiet;
+
+ switch (status)
+ {
+ case T_CHECKOUT:
+ case T_NEEDS_MERGE:
+ case T_CONFLICT:
+ case T_REMOVE_ENTRY:
+ error (0, 0, "Up-to-date check failed for `%s'", file);
+ freevers_ts (&vers);
+ return (1);
+ case T_MODIFIED:
+ case T_ADDED:
+ case T_REMOVED:
+ /*
+ * some quick sanity checks; if no numeric -r option specified:
+ * - can't have a sticky date
+ * - can't have a sticky tag that is not a branch
+ * Also,
+ * - if status is T_REMOVED, can't have a numeric tag
+ * - if status is T_ADDED, rcs file must not exist
+ * - if status is T_ADDED, can't have a non-trunk numeric rev
+ */
+ if (!tag || !isdigit (*tag))
+ {
+ if (vers->date)
+ {
+ error (0, 0,
+ "cannot commit with sticky date for file `%s'",
+ file);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (status == T_MODIFIED && vers->tag &&
+ !RCS_isbranch (file, vers->tag, srcfiles))
+ {
+ error (0, 0,
+ "sticky tag `%s' for file `%s' is not a branch",
+ vers->tag, file);
+ freevers_ts (&vers);
+ return (1);
+ }
+ }
+ if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
+ {
+ error (0, 0,
+ "cannot remove file `%s' which has a numeric sticky tag of `%s'",
+ file, vers->tag);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (status == T_ADDED)
+ {
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (isreadable (rcs))
+ {
+ error (0, 0,
+ "cannot add file `%s' when RCS file `%s' already exists",
+ file, rcs);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (vers->tag && isdigit (*vers->tag) &&
+ numdots (vers->tag) > 1)
+ {
+ error (0, 0,
+ "cannot add file `%s' with revision `%s'; must be on trunk",
+ file, vers->tag);
+ freevers_ts (&vers);
+ return (1);
+ }
+ }
+
+ /* done with consistency checks; now, to get on with the commit */
+ if (update_dir[0] == '\0')
+ xdir = ".";
+ else
+ xdir = update_dir;
+ if ((p = findnode (mulist, xdir)) != NULL)
+ {
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+ }
+ else
+ {
+ struct master_lists *ml;
+
+ ulist = getlist ();
+ cilist = getlist ();
+ p = getnode ();
+ p->key = xstrdup (xdir);
+ p->type = UPDATE;
+ ml = (struct master_lists *)
+ xmalloc (sizeof (struct master_lists));
+ ml->ulist = ulist;
+ ml->cilist = cilist;
+ p->data = (char *) ml;
+ (void) addnode (mulist, p);
+ }
+
+ /* first do ulist, then cilist */
+ p = getnode ();
+ p->key = xstrdup (file);
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->data = (char *) status;
+ (void) addnode (ulist, p);
+
+ p = getnode ();
+ p->key = xstrdup (file);
+ p->type = UPDATE;
+ p->delproc = ci_delproc;
+ ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
+ ci->status = status;
+ if (vers->tag)
+ if (isdigit (*vers->tag))
+ ci->rev = xstrdup (vers->tag);
+ else
+ ci->rev = RCS_whatbranch (file, vers->tag, srcfiles);
+ else
+ ci->rev = (char *) NULL;
+ ci->tag = xstrdup (vers->tag);
+ p->data = (char *) ci;
+ (void) addnode (cilist, p);
+ break;
+ case T_UNKNOWN:
+ error (0, 0, "nothing known about `%s'", file);
+ freevers_ts (&vers);
+ return (1);
+ case T_UPTODATE:
+ break;
+ default:
+ error (0, 0, "Unknown status 0x%x for `%s'", status, file);
+ break;
+ }
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Print warm fuzzies while examining the dirs
+ */
+/* ARGSUSED */
+static Dtype
+check_direntproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+
+ return (R_PROCESS);
+}
+
+/*
+ * Walklist proc to run pre-commit checks
+ */
+static int
+precommit_list_proc (p)
+ Node *p;
+{
+ if (p->data == (char *) T_ADDED || p->data == (char *) T_MODIFIED)
+ run_arg (p->key);
+ return (0);
+}
+
+/*
+ * Callback proc for pre-commit checking
+ */
+static List *ulist;
+static int
+precommit_proc (repository, filter)
+ char *repository;
+ char *filter;
+{
+ /* see if the filter is there, only if it's a full path */
+ if (filter[0] == '/' && !isfile (filter))
+ {
+ error (0, errno, "cannot find pre-commit filter `%s'", filter);
+ return (1); /* so it fails! */
+ }
+
+ run_setup ("%s %s", filter, repository);
+ (void) walklist (ulist, precommit_list_proc);
+ return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
+}
+
+/*
+ * Run the pre-commit checks for the dir
+ */
+/* ARGSUSED */
+static int
+check_filesdoneproc (err, repos, update_dir)
+ int err;
+ char *repos;
+ char *update_dir;
+{
+ int n;
+ Node *p;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ ulist = (List *) NULL;
+
+ /* skip the checks if there's nothing to do */
+ if (ulist == NULL || ulist->list->next == ulist->list)
+ return (err);
+
+ /* run any pre-commit checks */
+ if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0)
+ {
+ error (0, 0, "Pre-commit check failed");
+ err += n;
+ }
+
+ return (err);
+}
+
+/*
+ * Do the work of committing a file
+ */
+static int maxrev;
+static char sbranch[PATH_MAX];
+
+/* ARGSUSED */
+static int
+commit_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Node *p;
+ int err = 0;
+ List *ulist, *cilist;
+ struct commit_info *ci;
+ char rcs[PATH_MAX];
+
+ if (update_dir[0] == '\0')
+ p = findnode (mulist, ".");
+ else
+ p = findnode (mulist, update_dir);
+
+ /*
+ * if p is null, there were file type command line args which were
+ * all up-to-date so nothing really needs to be done
+ */
+ if (p == NULL)
+ return (0);
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+
+ /*
+ * At this point, we should have the commit message unless we were called
+ * with files as args from the command line. In that latter case, we
+ * need to get the commit message ourselves
+ */
+ if (use_editor && !got_message)
+ {
+ got_message = 1;
+ do_editor (update_dir, message, repository, ulist);
+ }
+
+ p = findnode (cilist, file);
+ if (p == NULL)
+ return (0);
+
+ ci = (struct commit_info *) p->data;
+ if (ci->status == T_MODIFIED)
+ {
+ if (lockrcsfile (file, repository, ci->rev) != 0)
+ {
+ unlockrcs (file, repository);
+ return (1);
+ }
+ }
+ else if (ci->status == T_ADDED)
+ {
+ if (checkaddfile (file, repository, ci->tag) != 0)
+ {
+ fixaddfile (file, repository);
+ return (1);
+ }
+ }
+
+ /*
+ * Add the file for real
+ */
+ if (ci->status == T_ADDED)
+ {
+ char *xrev = (char *) NULL;
+
+ if (ci->rev == NULL)
+ {
+ /* find the max major rev number in this directory */
+ maxrev = 0;
+ (void) walklist (entries, findmaxrev);
+ if (maxrev == 0)
+ maxrev = 1;
+ xrev = xmalloc (20);
+ (void) sprintf (xrev, "%d", maxrev);
+ }
+
+ /* XXX - an added file with symbolic -r should add tag as well */
+ err = finaladd (file, ci->rev ? ci->rev : xrev, ci->tag,
+ repository, entries);
+ if (xrev)
+ free (xrev);
+ return (err);
+ }
+
+ if (ci->status == T_MODIFIED)
+ {
+ locate_rcs (file, repository, rcs);
+ err = Checkin ('M', file, repository, rcs, ci->rev, ci->tag,
+ message, entries);
+ if (err != 0)
+ {
+ unlockrcs (file, repository);
+ fixbranch (file, repository, sbranch);
+ }
+ }
+
+ if (ci->status == T_REMOVED)
+ err = remove_file (file, repository, ci->tag, entries);
+
+ return (err);
+}
+
+/*
+ * Log the commit and clean up the update list
+ */
+/* ARGSUSED */
+static int
+commit_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ List *ulist, *cilist;
+ char *xtag = (char *) NULL;
+ Node *p;
+
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ {
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+ }
+ else
+ return (err);
+
+ got_message = 0;
+
+ /* see if we need to specify a per-directory or -r option tag */
+ if (tag == NULL)
+ ParseTag (&xtag, (char **) NULL);
+
+ Update_Logfile (repository, message, tag ? tag : xtag, (FILE *) 0, ulist);
+ dellist (&ulist);
+ dellist (&cilist);
+ if (xtag)
+ free (xtag);
+
+ if (err == 0 && run_module_prog)
+ {
+ char *cp;
+ FILE *fp;
+ char line[MAXLINELEN];
+ char *repository;
+
+ /* It is not an error if Checkin.prog does not exist. */
+ if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL)
+ {
+ if (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0';
+ repository = Name_Repository ((char *) NULL, update_dir);
+ run_setup ("%s %s", line, repository);
+ (void) printf ("%s %s: Executing '", program_name,
+ command_name);
+ run_print (stdout);
+ (void) printf ("'\n");
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ free (repository);
+ }
+ (void) fclose (fp);
+ }
+ }
+
+ return (err);
+}
+
+/*
+ * Get the log message for a dir and print a warm fuzzy
+ */
+/* ARGSUSED */
+static Dtype
+commit_direntproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ Node *p;
+ List *ulist;
+ char *real_repos;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ ulist = (List *) NULL;
+
+ /* skip the files as an optimization */
+ if (ulist == NULL || ulist->list->next == ulist->list)
+ return (R_SKIP_FILES);
+
+ /* print the warm fuzzy */
+ if (!quiet)
+ error (0, 0, "Committing %s", update_dir);
+
+ /* get commit message */
+ if (use_editor)
+ {
+ got_message = 1;
+ real_repos = Name_Repository (dir, update_dir);
+ do_editor (update_dir, message, real_repos, ulist);
+ free (real_repos);
+ }
+ return (R_PROCESS);
+}
+
+/*
+ * Process the post-commit proc if necessary
+ */
+/* ARGSUSED */
+static int
+commit_dirleaveproc (dir, err, update_dir)
+ char *dir;
+ int err;
+ char *update_dir;
+{
+ /* update the per-directory tag info */
+ if (err == 0 && write_dirtag != NULL)
+ WriteTag ((char *) NULL, write_dirtag, (char *) NULL);
+
+ return (err);
+}
+
+/*
+ * find the maximum major rev number in an entries file
+ */
+static int
+findmaxrev (p)
+ Node *p;
+{
+ char *cp;
+ int thisrev;
+ Entnode *entdata;
+
+ entdata = (Entnode *) p->data;
+ cp = index (entdata->version, '.');
+ if (cp != NULL)
+ *cp = '\0';
+ thisrev = atoi (entdata->version);
+ if (cp != NULL)
+ *cp = '.';
+ if (thisrev > maxrev)
+ maxrev = thisrev;
+ return (0);
+}
+
+/*
+ * Actually remove a file by moving it to the attic
+ * XXX - if removing a ,v file that is a relative symbolic link to
+ * another ,v file, we probably should add a ".." component to the
+ * link to keep it relative after we move it into the attic.
+ */
+static int
+remove_file (file, repository, tag, entries)
+ char *file;
+ char *repository;
+ char *tag;
+ List *entries;
+{
+ int omask;
+ int retcode;
+ char rcs[PATH_MAX];
+ char tmp[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (tag)
+ {
+ /* a symbolic tag is specified; just remove the tag from the file */
+ run_setup ("%s%s -q -N%s", Rcsbin, RCS, tag);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", tag, rcs);
+ return (1);
+ }
+ return (0);
+ }
+ else
+ {
+ /* no symbolic tag specified; really move it into the Attic */
+ (void) sprintf (tmp, "%s/%s", repository, CVSATTIC);
+ omask = umask (2);
+ (void) mkdir (tmp, 0777);
+ (void) umask (omask);
+ (void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+
+ if ((strcmp (rcs, tmp) == 0 || rename (rcs, tmp) != -1) ||
+ (!isreadable (rcs) && isreadable (tmp)))
+ {
+ Scratch_Entry (entries, file);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Do the actual checkin for added files
+ */
+static int
+finaladd (file, rev, tag, repository, entries)
+ char *file;
+ char *rev;
+ char *tag;
+ char *repository;
+ List *entries;
+{
+ int ret;
+ char tmp[PATH_MAX];
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ ret = Checkin ('A', file, repository, rcs, rev, tag,
+ message, entries);
+ if (ret == 0)
+ {
+ (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
+ (void) unlink_file (tmp);
+ (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ (void) unlink_file (tmp);
+ }
+ else
+ fixaddfile (file, repository);
+ return (ret);
+}
+
+/*
+ * Unlock an rcs file
+ */
+static void
+unlockrcs (file, repository)
+ char *file;
+ char *repository;
+{
+ char rcs[PATH_MAX];
+ int retcode = 0;
+
+ locate_rcs (file, repository, rcs);
+ run_setup ("%s%s -q -u", Rcsbin, RCS);
+ run_arg (rcs);
+
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not unlock %s", rcs);
+}
+
+/*
+ * remove a partially added file. if we can parse it, leave it alone.
+ */
+static void
+fixaddfile (file, repository)
+ char *file;
+ char *repository;
+{
+ RCSNode *rcsfile;
+ char rcs[PATH_MAX];
+ int save_really_quiet;
+
+ locate_rcs (file, repository, rcs);
+ save_really_quiet = really_quiet;
+ really_quiet = 1;
+ if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
+ (void) unlink_file (rcs);
+ else
+ freercsnode (&rcsfile);
+ really_quiet = save_really_quiet;
+}
+
+/*
+ * put the branch back on an rcs file
+ */
+static void
+fixbranch (file, repository, branch)
+ char *file;
+ char *repository;
+ char *branch;
+{
+ char rcs[PATH_MAX];
+ int retcode = 0;
+
+ if (branch != NULL && branch[0] != '\0')
+ {
+ locate_rcs (file, repository, rcs);
+ run_setup ("%s%s -q -b%s", Rcsbin, RCS, branch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "cannot restore branch to %s for %s", branch, rcs);
+ }
+}
+
+/*
+ * do the initial part of a file add for the named file. if adding
+ * with a tag, put the file in the Attic and point the symbolic tag
+ * at the committed revision.
+ */
+static int
+checkaddfile (file, repository, tag)
+ char *file;
+ char *repository;
+ char *tag;
+{
+ FILE *fp;
+ char *cp;
+ char rcs[PATH_MAX];
+ char fname[PATH_MAX];
+ int omask;
+ int retcode = 0;
+
+ if (tag)
+ {
+ (void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
+ omask = umask (2);
+ (void) mkdir (rcs, 0777);
+ (void) umask (omask);
+ (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+ }
+ else
+ locate_rcs (file, repository, rcs);
+
+ run_setup ("%s%s -i", Rcsbin, RCS);
+ run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
+ fp = open_file (fname, "r");
+ while (fgets (fname, sizeof (fname), fp) != NULL)
+ {
+ if ((cp = rindex (fname, '\n')) != NULL)
+ *cp = '\0';
+ if (*fname)
+ run_arg (fname);
+ }
+ (void) fclose (fp);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not create %s", rcs);
+ return (1);
+ }
+ fix_rcs_modes (rcs, file);
+ return (0);
+}
+
+/*
+ * Lock the rcs file ``file''
+ */
+static int
+lockrcsfile (file, repository, rev)
+ char *file;
+ char *repository;
+ char *rev;
+{
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (lock_RCS (file, rcs, rev, repository) != 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
+ * couldn't. If the RCS file currently has a branch as the head, we must
+ * move the head back to the trunk before locking the file, and be sure to
+ * put the branch back as the head if there are any errors.
+ */
+static int
+lock_RCS (user, rcs, rev, repository)
+ char *user;
+ char *rcs;
+ char *rev;
+ char *repository;
+{
+ RCSNode *rcsfile;
+ char *branch = NULL;
+ int err = 0;
+
+ /*
+ * For a specified, numeric revision of the form "1" or "1.1", (or when
+ * no revision is specified ""), definitely move the branch to the trunk
+ * before locking the RCS file.
+ *
+ * The assumption is that if there is more than one revision on the trunk,
+ * the head points to the trunk, not a branch... and as such, it's not
+ * necessary to move the head in this case.
+ */
+ if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
+ {
+ if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
+ {
+ /* invalid rcs file? */
+ err = 1;
+ }
+ else
+ {
+ /* rcsfile is valid */
+ branch = xstrdup (rcsfile->branch);
+ freercsnode (&rcsfile);
+ if (branch != NULL)
+ {
+ run_setup ("%s%s -q -b", Rcsbin, RCS);
+ run_arg (rcs);
+ if (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+ {
+ error (0, 0, "cannot change branch to default for %s",
+ rcs);
+ if (branch)
+ free (branch);
+ return (1);
+ }
+ }
+ run_setup ("%s%s -q -l", Rcsbin, RCS);
+ run_arg (rcs);
+ err = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ }
+ else
+ {
+ run_setup ("%s%s -q -l%s", Rcsbin, RCS, rev ? rev : "");
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL);
+ }
+
+ if (err == 0)
+ {
+ if (branch)
+ {
+ (void) strcpy (sbranch, branch);
+ free (branch);
+ }
+ else
+ sbranch[0] = '\0';
+ return (0);
+ }
+
+ /* try to restore the branch if we can on error */
+ if (branch != NULL)
+ fixbranch (user, repository, branch);
+
+ if (branch)
+ free (branch);
+ return (1);
+}
+
+/*
+ * Called when "add"ing files to the RCS respository, as it is necessary to
+ * preserve the file modes in the same fashion that RCS does. This would be
+ * automatic except that we are placing the RCS ,v file very far away from
+ * the user file, and I can't seem to convince RCS of the location of the
+ * user file. So we munge it here, after the ,v file has been successfully
+ * initialized with "rcs -i".
+ */
+static void
+fix_rcs_modes (rcs, user)
+ char *rcs;
+ char *user;
+{
+ struct stat sb;
+
+ if (stat (user, &sb) != -1)
+ (void) chmod (rcs, (int) sb.st_mode & ~0222);
+}
+
+/*
+ * free an UPDATE node's data (really nothing to do)
+ */
+void
+update_delproc (p)
+ Node *p;
+{
+ p->data = (char *) NULL;
+}
+
+/*
+ * Free the commit_info structure in p.
+ */
+static void
+ci_delproc (p)
+ Node *p;
+{
+ struct commit_info *ci;
+
+ ci = (struct commit_info *) p->data;
+ if (ci->rev)
+ free (ci->rev);
+ if (ci->tag)
+ free (ci->tag);
+ free (ci);
+}
+
+/*
+ * Find an RCS file in the repository.
+ */
+static void
+locate_rcs (file, repository, rcs)
+ char *file;
+ char *repository;
+ char *rcs;
+{
+ (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+ if (!isreadable (rcs))
+ {
+ (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+ if (!isreadable (rcs))
+ (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/config.h b/gnu/usr.bin/cvs/cvs/config.h
new file mode 100644
index 000000000000..b3bee5f97b09
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/config.h
@@ -0,0 +1,217 @@
+/* @(#)config.h 1.19 92/03/31 */
+
+/*
+ * 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.3 kit.
+ *
+ * This file holds (most of) the configuration tweaks that can be made to
+ * customize CVS for your site. CVS comes configured for a typical SunOS 4.x
+ * environment. The comments for each configurable item are intended to be
+ * self-explanatory. All #defines are tested first to see if an over-riding
+ * option was specified on the "make" command line.
+ *
+ * If special libraries are needed, you will have to edit the Makefile.in file
+ * or the configure script directly. Sorry.
+ */
+
+/*
+ * CVS provides the most features when used in conjunction with the Version-5
+ * release of RCS. Thus, it is the default. This also assumes that GNU diff
+ * Version-1.15 is being used as well -- you will have to configure your RCS
+ * V5 release separately to make this the case. If you do not have RCS V5 and
+ * GNU diff V1.15, comment out this define. You should not try mixing and
+ * matching other combinations of these tools.
+ */
+#ifndef HAVE_RCS5
+#define HAVE_RCS5
+#endif
+
+/*
+ * If, before installing this version of CVS, you were running RCS V4 AND you
+ * are installing this CVS and RCS V5 and GNU diff 1.15 all at the same time,
+ * you should turn on the following define. It only exists to try to do
+ * reasonable things with your existing checked out files when you upgrade to
+ * RCS V5, since the keyword expansion formats have changed with RCS V5.
+ *
+ * If you already have been running with RCS5, or haven't been running with CVS
+ * yet at all, or are sticking with RCS V4 for now, leave the commented out.
+ */
+#ifndef HAD_RCS4
+/* #define HAD_RCS4 */
+#endif
+
+/*
+ * For portability and heterogeneity reasons, CVS is shipped by default using
+ * my own text-file version of the ndbm database library in the src/myndbm.c
+ * file. If you want better performance and are not concerned about
+ * heterogeneous hosts accessing your modules file, turn this option off.
+ */
+#ifndef MY_NDBM
+#define MY_NDBM
+#endif
+
+/*
+ * The "diff" program to execute when creating patch output. This "diff"
+ * must support the "-c" option for context diffing. Specify a full pathname
+ * if your site wants to use a particular diff. If you are using the GNU
+ * version of diff (version 1.15 or later), this should be "diff -a".
+ *
+ * NOTE: this program is only used for the ``patch'' sub-command. The other
+ * commands use rcsdiff which will use whatever version of diff was specified
+ * when rcsdiff was built on your system.
+ */
+#ifndef DIFF
+#define DIFF "diff"
+#endif
+
+/*
+ * The "grep" program to execute when checking to see if a merged file had
+ * any conflicts. This "grep" must support the "-s" option and a standard
+ * regular expression as an argument. Specify a full pathname if your site
+ * wants to use a particular grep.
+ */
+#ifndef GREP
+#define GREP "grep"
+#endif
+
+/*
+ * The "rm" program to execute when pruning directories that are not part of
+ * a release. This "rm" must support the "-fr" options. Specify a full
+ * pathname if your site wants to use a particular rm.
+ */
+#ifndef RM
+#define RM "rm"
+#endif
+
+/*
+ * The "sort" program to execute when displaying the module database. Specify
+ * a full pathname if your site wants to use a particular sort.
+ */
+#ifndef SORT
+#define SORT "sort"
+#endif
+
+/*
+ * By default, RCS programs are executed with the shell or through execlp(),
+ * so the user's PATH environment variable is searched. If you'd like to
+ * bind all RCS programs to a certain directory (perhaps one not in most
+ * people's PATH) then set the default in RCSBIN_DFLT. Note that setting
+ * this here will cause all RCS programs to be executed from this directory,
+ * unless the user overrides the default with the RCSBIN environment variable
+ * or the "-b" option to CVS.
+ *
+ * This define should be either the empty string ("") or a full pathname to the
+ * directory containing all the installed programs from the RCS distribution.
+ */
+#ifndef RCSBIN_DFLT
+#define RCSBIN_DFLT ""
+#endif
+
+/*
+ * The default editor to use, if one does not specify the "-e" option to cvs,
+ * or does not have an EDITOR environment variable. I set this to just "vi",
+ * and use the shell to find where "vi" actually is. This allows sites with
+ * /usr/bin/vi or /usr/ucb/vi to work equally well (assuming that your PATH
+ * is reasonable).
+ */
+#ifndef EDITOR_DFLT
+#define EDITOR_DFLT "vi"
+#endif
+
+/*
+ * The Repository file holds the path to the directory within the source
+ * repository that contains the RCS ,v files for each CVS working directory.
+ * This path is either a full-path or a path relative to CVSROOT.
+ *
+ * The only advantage that I can see to having a relative path is that One can
+ * change the physical location of the master source repository, change one's
+ * CVSROOT environment variable, and CVS will work without problems. I
+ * recommend using full-paths.
+ */
+#ifndef RELATIVE_REPOS
+/* #define RELATIVE_REPOS */
+#endif
+
+/*
+ * When committing or importing files, you must enter a log message.
+ * Normally, you can do this either via the -m flag on the command line or an
+ * editor will be started for you. If you like to use logging templates (the
+ * rcsinfo file within the $CVSROOT/CVSROOT directory), you might want to
+ * force people to use the editor even if they specify a message with -m.
+ * Enabling FORCE_USE_EDITOR will cause the -m message to be appended to the
+ * temp file when the editor is started.
+ */
+#ifndef FORCE_USE_EDITOR
+/* #define FORCE_USE_EDITOR */
+#endif
+
+/*
+ * When locking the repository, some sites like to remove locks and assume
+ * the program that created them went away if the lock has existed for a long
+ * time. This used to be the default for previous versions of CVS. CVS now
+ * attempts to be much more robust, so lock files should not be left around
+ * by mistake. The new behaviour will never remove old locks (they must now
+ * be removed by hand). Enabling CVS_FUDGELOCKS will cause CVS to remove
+ * locks that are older than CVSLCKAGE seconds.
+ * Use of this option is NOT recommended.
+ */
+#ifndef CVS_FUDGELOCKS
+/* #define CVS_FUDGELOCKS */
+#endif
+
+/*
+ * When committing a permanent change, CVS and RCS make a log entry of
+ * who committed the change. If you are committing the change logged in
+ * as "root" (not under "su" or other root-priv giving program), CVS/RCS
+ * cannot determine who is actually making the change.
+ *
+ * As such, by default, CVS disallows changes to be committed by users
+ * logged in as "root". You can disable this option by commenting
+ * out the lines below.
+ */
+#ifndef CVS_BADROOT
+#define CVS_BADROOT
+#endif
+
+/*
+ * The "cvs diff" command accepts all the single-character options that GNU
+ * diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put
+ * cpp-style #define's around the output differences. CVS, by default, uses
+ * -D to specify a free-form date (like "cvs diff -D '1 week ago'"). If
+ * you would prefer that the -D option of "cvs diff" work like the GNU diff
+ * option, then comment out this define.
+ */
+#ifndef CVS_DIFFDATE
+#define CVS_DIFFDATE
+#endif
+
+/* End of CVS configuration section */
+
+/*
+ * Externs that are included in libc, but are used frequently enough to
+ * warrant defining here.
+ */
+#ifndef STDC_HEADERS
+extern void exit ();
+#endif
+
+#ifndef getwd
+extern char *getwd ();
+#endif
+
+/*
+ * Some UNIX distributions don't include these in their stat.h Defined here
+ * because "config.h" is always included last.
+ */
+#ifndef S_IWRITE
+#define S_IWRITE 0000200 /* write permission, owner */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 0000020 /* write permission, grougroup */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0000002 /* write permission, other */
+#endif
diff --git a/gnu/usr.bin/cvs/cvs/create_adm.c b/gnu/usr.bin/cvs/cvs/create_adm.c
new file mode 100644
index 000000000000..911258ed9940
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/create_adm.c
@@ -0,0 +1,100 @@
+/*
+ * 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.3 kit.
+ *
+ * Create Administration.
+ *
+ * Creates a CVS administration directory based on the argument repository; the
+ * "Entries" file is prefilled from the "initrecord" argument.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)create_adm.c 1.24 92/03/31";
+#endif
+
+void
+Create_Admin (dir, repository, tag, date)
+ char *dir;
+ char *repository;
+ char *tag;
+ char *date;
+{
+ FILE *fout;
+ char *cp;
+ char tmp[PATH_MAX];
+
+ if (noexec)
+ return;
+
+ if (!isdir (repository))
+ error (1, 0, "there is no repository %s", repository);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM);
+ else
+ (void) strcpy (tmp, CVSADM);
+
+ if (isfile (tmp))
+ error (1, 0, "there is a version here already");
+ else
+ {
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, OCVSADM);
+ else
+ (void) strcpy (tmp, OCVSADM);
+
+ if (isfile (tmp))
+ error (1, 0, "there is a version here already");
+ }
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM);
+ else
+ (void) strcpy (tmp, CVSADM);
+ make_directory (tmp);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
+ else
+ (void) strcpy (tmp, CVSADM_REP);
+ fout = open_file (tmp, "w+");
+ cp = repository;
+ strip_path (cp);
+
+#ifdef RELATIVE_REPOS
+ /*
+ * If the Repository file is to hold a relative path, try to strip off
+ * the leading CVSroot argument.
+ */
+ if (CVSroot != NULL)
+ {
+ char path[PATH_MAX];
+
+ (void) sprintf (path, "%s/", CVSroot);
+ if (strncmp (repository, path, strlen (path)) == 0)
+ cp = repository + strlen (path);
+ }
+#endif
+
+ if (fprintf (fout, "%s\n", cp) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+
+ /* now, do the Entries file */
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT);
+ else
+ (void) strcpy (tmp, CVSADM_ENT);
+ fout = open_file (tmp, "w+");
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+
+ /* Create a new CVS/Tag file */
+ WriteTag (dir, tag, date);
+}
diff --git a/gnu/usr.bin/cvs/cvs/cvs.1 b/gnu/usr.bin/cvs/cvs/cvs.1
new file mode 100644
index 000000000000..f0c648fdb836
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.1
@@ -0,0 +1,1991 @@
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id cvs.1,v 1.12 1992/04/10 03:05:16 berliner Exp
+.TH CVS 1 "\*(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"
+cvs \- Concurrent Versions System
+.SH "SYNOPSIS"
+.TP
+\fBcvs\fP [ \fIcvs_options\fP ]
+.I cvs_command
+[
+.I command_options
+] [
+.I command_args
+]
+.SH "DESCRIPTION"
+.IX "revision control system" "\fLcvs\fR"
+.IX cvs "" "\fLcvs\fP \- concurrent versions system"
+.IX "concurrent versions system \- \fLcvs\fP"
+.IX "release control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system"
+.IX "source control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system"
+.IX revisions "cvs command" "" "\fLcvs\fP \- source control"
+.B cvs
+is a front end to the
+.BR rcs ( 1 )
+revision control system which extends
+the notion of revision control from a collection of files in a single
+directory to a hierarchical collection of directories consisting of
+revision controlled files.
+These directories and files can be combined together to form a software
+release.
+.B cvs
+provides the functions necessary to manage these software releases and to
+control the concurrent editing of source files among multiple software
+developers.
+.SP
+.B cvs
+keeps a single copy of the master sources.
+This copy is called the source ``repository''; it contains all the
+information to permit extracting previous software releases at any
+time based on either a symbolic revision tag, or a date in the past.
+.SH "ESSENTIAL COMMANDS"
+.B cvs
+provides a rich variety of commands (\fIcvs_command\fP in the
+Synopsis), each of which often has a wealth of options, to satisfy the
+many needs of source management in distributed environments. However,
+you don't have to master every detail to do useful work with
+.BR cvs ;
+in fact, five commands are sufficient to use (and contribute to)
+the source repository.
+.TP
+\fBcvs checkout\fP \fImodules\fP\|.\|.\|.
+A necessary preliminary for most \fBcvs\fP work: creates your private
+copy of the source for \fImodules\fP (named collections of source; you
+can also use a path relative to the source repository here). You can
+work with this copy without interfering with others' work. At least
+one subdirectory level is always created.
+.TP
+.B cvs update
+Execute this command from \fIwithin\fP your private source
+directory when you wish to update your copies of source files from
+changes that other developers have made to the source in the
+repository.
+.TP
+\fBcvs add\fP \fIfile\fP\|.\|.\|.
+Use this command to enroll new files in \fBcvs\fP records of your
+working directory. The files will be added to the repository the next
+time you run
+.` "cvs commit".
+Note:
+You should use the
+.` "cvs import"
+command to bootstrap new sources into the source repository.
+.` "cvs add"
+is only used for new files to an already checked-out module.
+.TP
+\fBcvs remove\fP \fIfile\fP\|.\|.\|.
+Use this command (after erasing any files listed) to declare that you
+wish to eliminate files from the repository. The removal does not
+affect others until you run
+.` "cvs commit".
+.TP
+\fBcvs commit\fP \fIfile\fP\|.\|.\|.
+Use this command when you wish to ``publish'' your changes to other
+developers, by incorporating them in the source repository.
+.SH "OPTIONS"
+The
+.B cvs
+command line can include
+.IR cvs_options ,
+which apply to the overall
+.B cvs
+program; a
+.IR cvs_command ,
+which specifies a particular action on the source repository; and
+.I command_options
+and
+.I command_arguments
+to fully specify what the
+.I cvs_command
+will do.
+.SP
+.I Warning:
+you must be careful of precisely where you place options relative to the
+.IR cvs_command .
+The same option can mean different things depending on whether it
+is in the
+.I cvs_options
+position (to the left of a
+.B cvs
+command) or in the
+.I command_options
+position (to the right of a
+.B cvs
+command).
+.SP
+There are only two situations where you may omit
+.IR cvs_command :
+.` "cvs \-H"
+elicits a list of available commands, and
+.` "cvs \-v "
+displays version information on \fBcvs\fP itself.
+.SP
+.SH "CVS OPTIONS"
+Use these options to control the overall
+.B cvs
+program:
+.TP
+.B \-H
+Display usage information about the specified
+.I cvs_command
+(but do not actually execute the command). If you don't specify a
+command name,
+.` "cvs \-H"
+displays a summary of all the commands available.
+.TP
+.B \-Q
+Causes the command to be
+.I really
+quiet; the command will generate output only for serious problems.
+.TP
+.B \-q
+Causes the command to be somewhat quiet; informational messages, such
+as reports of recursion through subdirectories, are suppressed.
+.TP
+\fB\-b\fP \fIbindir\fP
+Use
+.I bindir
+as the directory where
+.SM RCS
+programs are located.
+Overrides the setting of the
+.SM RCSBIN
+environment variable.
+This value should be specified as an absolute pathname.
+.TP
+\fB\-d\fP \fICVS_root_directory\fP
+Use
+.I CVS_root_directory
+as the root directory pathname of the master
+.SM RCS
+source repository.
+Overrides the setting of the
+.SM CVSROOT
+environment variable.
+This value should be specified as an absolute pathname.
+.TP
+\fB\-e\fP \fIeditor\fP
+Use
+.I editor
+to enter revision log information.
+Overrides the setting of the
+.SM EDITOR
+environment variable.
+.TP
+.B \-l
+Do not log the
+.I cvs_command
+in the command history (but execute it anyway). See the description
+of the
+.B history
+command for information on command history.
+.TP
+.B \-n
+Do not change any files. Attempt to execute the
+.IR cvs_command ,
+but only to issue reports; do not remove, update, or merge any
+existing files, or create any new files.
+.TP
+.B \-t
+Trace program execution; display messages showing the steps of
+.B cvs
+activity. Particularly useful with
+.B \-n
+to explore the potential impact of an unfamiliar command.
+.TP
+.B \-r
+Makes new working files files read-only.
+Same effect as if the
+.SM CVSREAD
+environment variable is set.
+.TP
+.B \-v
+Displays version and copyright information for
+.BR cvs .
+.TP
+.B \-w
+Makes new working files read-write (default).
+Overrides the setting of the
+.SM CVSREAD
+environment variable.
+.SH "USAGE"
+Except when requesting general help with
+.` "cvs \-H",
+you must specify a
+.I cvs_command
+to
+.B cvs
+to select a specific release control function to perform.
+Each
+.B cvs
+command accepts its own collection of options and arguments.
+However, many options are available across several commands.
+You can display a usage summary for each command by specifying the
+.B \-H
+option with the command.
+.SH "CVS COMMAND SUMMARY"
+Here are brief descriptions of all the
+.B cvs
+commands:
+.TP
+.B add
+Add a new file or directory to the repository, pending a
+.` "cvs commit"
+on the same file.
+Can only be done from within sources created by a previous
+.` "cvs checkout"
+invocation.
+Use
+.` "cvs import"
+to place whole new hierarchies of sources under
+.B cvs
+control.
+(Does not directly affect repository; changes
+working directory.)
+.TP
+.B admin
+Execute
+.SM RCS
+control functions on the source repository. (Changes
+repository directly; uses working directory without changing it.)
+.TP
+.B checkout
+Make a working directory of source files for editing. (Creates or changes
+working directory.)
+.TP
+.B commit
+Apply to the source repository changes, additions, and deletions from your
+working directory. (Changes repository.)
+.TP
+.B diff
+Show differences between files in working directory and source
+repository, or between two revisions in source repository.
+(Does not change either repository or working directory.)
+.TP
+.B export
+Prepare copies of a set of source files for shipment off site.
+Differs from
+.` "cvs checkout"
+in that no
+.B cvs
+administrative directories are created (and therefore
+.` "cvs commit"
+cannot be executed from a directory prepared with
+.` "cvs export"),
+and a symbolic tag must be specified.
+(Does not change repository; creates directory similar to working
+directories).
+.TP
+.B history
+Show reports on
+.B cvs
+commands that you or others have executed on a particular file or
+directory in the source repository. (Does not change repository or
+working directory.) History logs are kept only if enabled by creation
+of the
+.` "$CVSROOT/CVSROOT/history"
+file; see
+.BR cvs ( 5 ).
+.TP
+.B import
+Incorporate a set of updates from off-site into the source repository,
+as a ``vendor branch''. (Changes repository.)
+.TP
+.B log
+Display
+.SM RCS
+log information.
+(Does not change repository or working directory.)
+.TP
+.B rdiff
+Prepare a collection of diffs as a patch file between two releases in
+the repository. (Does not change repository or working directory.)
+.TP
+.B release
+Cancel a
+.` "cvs checkout",
+abandoning any changes.
+(Can delete working directory; no effect on repository.)
+.TP
+.B remove
+Remove files from the source repository, pending a
+.` "cvs commit"
+on the same files. (Does not directly affect repository;
+changes working directory.)
+.TP
+.B rtag
+Explicitly specify a symbolic tag for particular revisions of files in the
+source repository. See also
+.` "cvs tag".
+(Changes repository directly; does not require or affect
+working directory.)
+.TP
+.B status
+Show current status of files: latest version, version in working
+directory, whether working version has been edited and, optionally,
+symbolic tags in the
+.SM RCS
+file. (Does not change
+repository or working directory.)
+.TP
+.B tag
+Specify a symbolic tag for files in the repository. Tags the revisions
+that were last synchronized with your working directory. (Changes
+repository directly; uses working directory without changing it.)
+.TP
+.B update
+Bring your working directory up to date with changes from the
+repository. Merges are performed automatically when possible; a
+warning is issued if manual resolution is required for conflicting
+changes. (Changes working directory; does not change repository.)
+.SH "COMMON COMMAND OPTIONS"
+This section describes the
+.I command_options
+that are available across several
+.B cvs
+commands. Not all commands support all of these options; each option
+is only supported for commands where it makes sense. However, when
+a command has one of these options you can count on the same meaning
+for the option as in other commands. (Other command
+options, which are listed with the individual commands, may have
+different meanings from one
+.B cvs
+command to another.)
+.I "Warning:"
+the
+.B history
+command is an exception;
+it supports many options that conflict
+even with these standard options.
+.TP
+\fB\-D\fP \fIdate_spec\fP
+Use the most recent revision no later than \fIdate_spec\fP (a single
+argument, date description specifying a date in the
+past). A wide variety of date formats are supported by the underlying
+.SM RCS
+facilities, similar to those described in
+.BR co ( 1 ),
+but not exactly the same.
+The \fIdate_spec\fP is interpreted as being in the local timezone, unless a
+specific timezone is specified.
+The specification is ``sticky'' when you use it to make a
+private copy of a source file; that is, when you get a working file
+using \fB\-D\fP, \fBcvs\fP records the date you
+specified, so that further updates in the same directory will use the
+same date (unless you explicitly override it; see the description of
+the \fBupdate\fP command).
+.B \-D
+is available with the
+.BR checkout ", " diff, ", " history ", " export ", "
+.BR rdiff ", " rtag ", and "
+.B update
+commands.
+Examples of valid date specifications include:
+.in +1i
+.ft B
+.nf
+1 month ago
+2 hours ago
+400000 seconds ago
+last year
+last Monday
+yesterday
+a fortnight ago
+3/31/92 10:00:07 PST
+January 23, 1987 10:05pm
+22:00 GMT
+.fi
+.ft P
+.in -1i
+.TP
+.B \-f
+When you specify a particular date or tag to \fBcvs\fP commands, they
+normally ignore files that do not contain the tag (or did not exist on
+the date) that you specified. Use the \fB\-f\fP option if you want
+files retrieved even when there is no match for the tag or date. (The
+most recent version is used in this situation.)
+.B \-f
+is available with these commands:
+.BR checkout ", " export ", "
+.BR rdiff ", " rtag ", and " update .
+.TP
+.B \-H
+Help; describe the options available for this command. This is the
+only option supported for
+.I all
+.B cvs
+commands.
+.TP
+\fB\-k\fP \fIkflag\fP
+Alter the default
+.SM RCS
+processing of keywords; all the
+.B \-k
+options described in
+.BR rcs ( 1 )
+are available. The \fB\-k\fP option is available with the
+.BR add ", " checkout ", " diff ", "
+.RB rdiff ", and " update
+commands. Your \fIkflag\fP specification is ``sticky'' when you use
+it to create a private copy of a source file; that is, when you use
+this option with the \fBcheckout\fP or \fBupdate\fP commands,
+\fBcvs\fP associates your selected \fIkflag\fP with the file, and
+continues to use it with future \fBupdate\fP commands on the same file
+until you specify otherwise.
+.TP
+.B \-l
+Local; run only in current working directory, rather than recurring through
+subdirectories. Available with the following commands:
+.BR checkout ", " commit ", " diff ", "
+.BR export ", " remove ", " rdiff ", " rtag ", "
+.BR status ", " tag ", and " update .
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-l"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.TP
+.B \-n
+Do
+.I not
+run any
+.BR checkout / commit / tag / update
+program. (A program can be specified to run on each of these
+activities, in the modules database; this option bypasses it.)
+Available with the
+.BR checkout ", " commit ", " export ", and "
+.B rtag
+commands.
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-n"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.TP
+.B \-P
+Prune (remove) directories that are empty after being updated, on
+.BR checkout ", or " update .
+Normally, an empty directory (one that is void of revision-controlled
+files) is left alone.
+Specifying
+.B \-P
+will cause these directories to be silently removed from your checked-out
+sources.
+This does not remove the directory from the repository, only from your
+checked out copy.
+Note that this option is implied by the
+.B \-r
+or
+.B \-D
+options of
+.BR checkout " and " export .
+.TP
+.B \-p
+Pipe the files retrieved from the repository to standard output,
+rather than writing them in the current directory. Available with the
+.BR checkout " and " update
+commands.
+.TP
+.B \-Q
+Causes the command to be
+.I really
+quiet; the command will generate output only for serious problems.
+Available with the following commands:
+.BR checkout ", " import ", " export ", "
+.BR rdiff ", " rtag ", "
+.BR tag ", and " update .
+.TP
+.B \-q
+Causes the command to be somewhat quiet; informational messages, such
+as reports of recursion through subdirectories, are suppressed.
+Available with the following commands:
+.BR checkout ", " import ", " export ", "
+.BR rtag ", "
+.BR tag ", and " update .
+.TP
+\fB\-r\fP \fItag\fP
+Use the revision specified by the
+.I tag
+argument instead of the default ``head'' revision. As well as
+arbitrary tags defined with the \fBtag\fP or \fBrtag\fP command, two
+special tags are always available:
+.` "HEAD"
+refers to the most
+recent version available in the repository, and
+.` "BASE"
+refers to the revision you last checked out into the current working
+directory.
+.SP
+The \fItag\fP specification is ``sticky'' when you use
+this option with
+.` "cvs checkout"
+or
+.` "cvs update"
+to
+make your own copy of a file: \fBcvs\fP remembers the \fItag\fP and
+continues to use it on future \fBupdate\fP commands, until you specify
+otherwise.
+.I tag
+can be either a symbolic or numeric tag, in
+.SM RCS
+fashion.
+Specifying the
+.B \-q
+option along with the
+.B \-r
+option is often useful, to suppress the warning messages when the
+.SM RCS
+file does not contain the specified tag.
+.B \-r
+is available with the
+.BR checkout ", " commit ", " diff ", "
+.BR history ", " export ", "
+.BR rdiff ", " rtag ", and " update
+commands.
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-r"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.SH "CVS COMMANDS"
+Here (finally) are details on all the
+.B cvs
+commands and the options each accepts. The summary lines at the top
+of each command's description highlight three kinds of things:
+.TP 1i
+\ \ \ \ Command Options and Arguments
+Special options are described in detail below; common command options
+may appear only in the summary line.
+.TP 1i
+\ \ \ \ Working Directory, or Repository?
+Some \fBcvs\fP commands require a working directory to operate; some
+require a repository. Also, some commands \fIchange\fP the
+repository, some change the working directory, and some change
+nothing.
+.TP 1i
+\ \ \ \ Synonyms
+Many commands have synonyms, which you may find easier to
+remember (or type) than the principal name.
+.PP
+.TP
+\fBadd\fP [\fB\-k\fP \fIkflag\fP] [\fB\-m '\fP\fImessage\fP\fB'\fP] \fIfiles.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+working directory.
+.br
+.I Synonym:
+.B new
+.br
+Use the
+.B add
+command to create a new file or directory in the
+.SM RCS
+source repository.
+The files or directories specified with
+.B add
+must already exist in the current directory (which must have been created
+with the
+.B checkout
+command).
+To add a whole new directory hierarchy to the source repository
+(for example, files received from a third-party vendor), use the
+.` "cvs import"
+command instead.
+.SP
+If the argument to
+.` "cvs add"
+refers to an immediate sub-directory, the directory is
+created at the correct place in the
+.SM RCS
+source repository, and the necessary
+.B cvs
+administration files are created in your working directory.
+If the directory already exists in the source repository,
+.` "cvs add"
+still creates the administration files in your version of the directory.
+This allows you to use
+.` "cvs add"
+to add a particular directory to your private sources even if
+someone else created that directory after your
+.B checkout
+of the sources. You can do the following:
+.SP
+.in +1i
+.ft B
+.nf
+example% mkdir new_directory
+example% cvs add new_directory
+example% cvs update new_directory
+.fi
+.ft P
+.in -1i
+.SP
+An alternate approach using
+.` "cvs update"
+might be:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs update -d new_directory
+.fi
+.ft P
+.in -1i
+.SP
+(To add \fIany available\fP new directories to your working directory, it's
+probably simpler to use
+.` "cvs checkout"
+or
+.` "cvs update -d".)
+.SP
+The added files are not placed in the
+.SM RCS
+source repository until you use
+.` "cvs commit"
+to make the change permanent.
+Doing a
+.` "cvs add"
+on a file that was removed with the
+.` "cvs remove"
+command will resurrect the file, if no
+.` "cvs commit"
+command intervened.
+.SP
+You will have the opportunity to specify a logging message, as usual,
+when you use
+.` "cvs commit"
+to make the new file permanent. If you'd like to have another
+logging message associated with just
+.I creation
+of the file (for example, to describe the file's purpose), you can
+specify it with the
+.` "\-m \fImessage\fP"
+option to the
+.B add
+command.
+.SP
+The
+.` "-k kflag"
+option specifies the default way that this
+file will be checked out.
+The
+.` "kflag"
+argument is stored in the
+.SM RCS
+file and can be changed with
+.` "cvs admin".
+Specifying
+.` "-ko"
+is useful for checking in binaries that
+shouldn't have the
+.SM RCS
+id strings expanded.
+.TP
+\fBadmin\fP [\fIrcs-options\fP] \fIfiles.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B rcs
+.br
+This is the
+.B cvs
+interface to assorted administrative
+.SM RCS
+facilities, documented in
+.BR rcs ( 1 ).
+.` "cvs admin"
+simply passes all its options and arguments to the
+.B rcs
+command; it does no filtering or other processing.
+This command does work recursively, however, so extreme care should be
+used.
+.TP
+\fBcheckout\fP [\fBoptions\fP] \fImodules\fP.\|.\|.
+.I Requires:
+repository.
+.br
+.I Changes:
+working directory.
+.br
+.I Synonyms:
+.BR co ", " get
+.br
+Make a working directory containing copies of the source files specified by
+.IR modules .
+You must execute
+.` "cvs checkout"
+before using most of the other
+.B cvs
+commands, since most of them operate on your working directory.
+.SP
+\fImodules\fP are either symbolic names (themselves defined as the
+module
+.` "modules"
+in the source repository; see
+.BR cvs ( 5 ))
+for some collection of source directories and files, or paths to
+directories or files in the repository.
+.SP
+Depending on the
+.I modules
+you specify,
+.B checkout
+may recursively create directories and populate them with the appropriate
+source files.
+You can then edit these source files at any time (regardless of whether
+other software developers are editing their own copies of the sources);
+update them to include new changes applied by others to the source
+repository; or commit your work as a permanent change to the
+.SM RCS
+repository.
+.SP
+Note that
+.B checkout
+is used to create directories.
+The top-level directory created is always added to the directory
+where
+.B checkout
+is invoked, and usually has the same name as the specified
+.IR module .
+In the case of a
+.I module
+alias, the created sub-directory may have a different name, but you can be
+sure that it will be a sub-directory, and that
+.B checkout
+will show the relative path leading to each file as it is extracted into
+your private work area (unless you specify the
+.B \-Q
+option).
+.SP
+Running
+.` "cvs checkout"
+on a directory that was already built by a prior
+.B checkout
+is also permitted, and
+has the same effect as specifying the
+.B \-d
+option to the
+.B update
+command described below.
+.SP
+The
+.I options
+permitted with
+.` "cvs checkout"
+include the standard command options
+.BR \-P ", " \-Q ", " \-f ", "
+.BI \-k " kflag"
+\&,
+.BR \-l ", " \-n ", " \-p ", "
+.BR \-q ", " \-r
+.IR tag ", and"
+.BI \-D " date"\c
+\&.
+.SP
+In addition to those, you can use these special command options
+with
+.BR checkout :
+.SP
+Use the
+.B \-A
+option to reset any sticky tags, dates, or
+.B \-k
+options. (If you get a working file using one of the
+\fB\-r\fP, \fB\-D\fP, or \fB\-k\fP options, \fBcvs\fP remembers the
+corresponding tag, date, or \fIkflag\fP and continues using it on
+future updates; use the \fB\-A\fP option to make \fBcvs\fP forget these
+specifications, and retrieve the ``head'' version of the file).
+.SP
+The
+.BI \-j " branch"
+option merges the changes made between the
+resulting revision and the revision that it is based on (e.g., if
+the tag refers to a branch,
+.B cvs
+will merge all changes made in that branch into your working file).
+.SP
+With two \fB-j\fP options,
+.B cvs
+will merge in the changes between the two respective revisions.
+This can be used to ``remove'' a certain delta from your working file.
+.SP
+In addition, each \fB-j\fP 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.
+An example might be what
+.` "cvs import"
+tells you to do when you have
+just imported sources that have conflicts with local changes:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs checkout -jTAG:yesterday -jTAG module
+.fi
+.ft P
+.in -1i
+.SP
+Use the
+.B \-N
+option with
+.` "\-d \fIdir\fP"
+to avoid shortening module paths in your working directory. (Normally, \fBcvs\fP shortens paths as much as possible when you specify an explicit target directory.)
+.SP
+Use the
+.B \-c
+option to copy the module file, sorted, to the standard output,
+instead of creating or modifying any files or directories in your
+working directory.
+.SP
+Use the
+.BI \-d " dir"
+option to create a directory called
+.I dir
+for the working files, instead of using the module name. Unless you
+also use \fB\-N\fP, the paths created under \fIdir\fP will be as short
+as possible.
+.SP
+Use the
+.B \-s
+option to display per-module status information stored with
+the
+.B \-s
+option within the modules file.
+.TP
+\fBcommit\fP [\fB\-lnR\fP] [\fB\-m\fP '\fIlog_message\fP' | \fB\-f\fP \fIfile\fP] [\fB\-r\fP \fIrevision\fP] [\fIfiles.\|.\|.\fP]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B ci
+.br
+Use
+.` "cvs commit"
+when you want to incorporate changes from your working source
+files into the general source repository.
+.SP
+If you don't specify particular \fIfiles\fP to commit, all
+of the files in your working current directory are examined.
+.B commit
+is careful to change in the repository only those files that you have
+really changed. By default (or if you explicitly specify the
+.B \-R
+option), files
+in subdirectories are also examined and committed if they have
+changed; you can use the
+.B \-l
+option to limit
+.B commit
+to the current directory only.
+.SP
+.B commit
+verifies that the selected files are up to date with the current revisions
+in the source repository; it will notify you, and exit without
+committing, if any of the specified files must be made current first
+with
+.` "cvs update".
+.B commit
+does not call the
+.B update
+command for you, but rather leaves that for you to do when
+the time is right.
+.SP
+When all is well, an editor is invoked to allow you to enter a log
+message that will be written to one or more logging programs and placed in the
+.SM RCS
+source repository file.
+You can instead specify the log message on the command line with the
+.B \-m
+option, thus suppressing the editor invocation, or use the
+.B \-f
+option to specify that the argument \fIfile\fP contains the log message.
+.SP
+The
+.B \-r
+option can be used to commit to a particular symbolic or numeric revision
+within the
+.SM RCS
+file.
+For example, to bring all your files up to the
+.SM RCS
+revision ``3.0'' (including those that haven't changed), you might do:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs commit -r3.0
+.fi
+.ft P
+.in -1i
+.SP
+.B cvs
+will only allow you to commit to a revision that is on the main trunk (a
+revision with a single dot).
+However, you can also commit to a branch revision (one that has an even
+number of dots) with the
+.B \-r
+option.
+To create a branch revision, one typically use the
+.B \-b option of the
+.BR rtag " or " tag
+commands.
+Then, either
+.BR checkout " or " update
+can be used to base your sources on the newly created branch.
+From that point on, all
+.B commit
+changes made within these working sources will be automatically added
+to a branch revision, thereby not perturbing main-line development in any
+way.
+For example, if you had to create a patch to the 1.2 version of the
+product, even though the 2.0 version is already under development, you
+might do:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs rtag -b -rFCS1_2 FCS1_2_Patch product_module
+example% cvs checkout -rFCS1_2_Patch product_module
+example% cd product_module
+[[ hack away ]]
+example% cvs commit
+.fi
+.ft P
+.in -1i
+.SP
+Say you have been working on some extremely experimental software, based on
+whatever revision you happened to checkout last week.
+If others in your group would like to work on this software with you, but
+without disturbing main-line development, you could commit your change to a
+new branch.
+Others can then checkout your experimental stuff and utilize the full
+benefit of
+.B cvs
+conflict resolution.
+The scenario might look like:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs tag -b EXPR1
+example% cvs update -rEXPR1
+[[ hack away ]]
+example% cvs commit
+.fi
+.ft P
+.in -1i
+.SP
+Others would simply do
+.` "cvs checkout -rEXPR1 whatever_module"
+to work with you on the experimental change.
+.TP
+\fBdiff\fP [\fB\-kl\fP] [\fIrcsdiff_options\fP] [[\fB\-r\fP \fIrev1\fP | \fB\-D\fP \fIdate1\fP] [\fB\-r\fP \fIrev2\fP | \fB\-D\fP \fIdate2\fP]] [\fIfiles.\|.\|.\fP]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+nothing.
+.br
+You can compare your working files with revisions in the source
+repository, with the
+.` "cvs diff"
+command. If you don't specify a particular revision, your files
+are compared with the revisions they were based on. You can also use
+the standard
+.B cvs
+command option
+.B \-r
+to specify a particular revision to compare your files with. Finally,
+if you use
+.B \-r
+twice, you can see differences between two revisions in the
+repository.
+You can also specify
+.B \-D
+options to diff against a revision in the past.
+The
+.B \-r
+and
+.B \-D
+options can be mixed together with at most two options ever specified.
+.SP
+See
+.BR rcsdiff ( 1 )
+for a list of other accepted options.
+.SP
+If you don't specify any files,
+.B diff
+will display differences for all those files in the current directory
+(and its subdirectories, unless you use the standard option
+.BR \-l )
+that
+differ from the corresponding revision in the source repository
+(i.e. files that
+.I you
+have changed), or that differ from the revision specified.
+.TP
+\fBexport\fP [\-\fBf\|lNnQq\fP] \fB\-r\fP \fIrev\fP\||\|\fB\-D\fP \fIdate\fP [\fB\-d\fP \fIdir\fP] \fImodule\fP.\|.\|.
+.I Requires:
+repository.
+.br
+.I Changes:
+current directory.
+.br
+This command is a variant of
+.` "cvs checkout";
+use it when you want a copy of the source for \fImodule\fP
+without the \fBcvs\fP administrative directories. For example, you
+might use
+.` "cvs export"
+to prepare source for shipment
+off-site. This command \fIrequires\fP that you specify a date or tag
+(with \fB\-D\fP or \fB\-r\fP), so that you can count on reproducing
+the source you ship to others.
+.SP
+The only non-standard options are
+.` "\-d \fIdir\fP"
+(write the
+source into directory \fIdir\fP) and
+.` "\-N"
+(don't shorten
+module paths).
+These have the same meanings as the same options in
+.` "cvs checkout".
+.SP
+The
+.B \-kv
+option is always set when
+.B export
+is used.
+This causes any
+.SM RCS
+keywords to be expanded such that an
+.B import
+done at some other site will not lose the keyword revision information.
+There is no way to override this.
+.TP
+\fBhistory\fP [\fB\-\fP\fIreport\fP] [\fB\-\fP\fIflags\fP] [\fB\-\fP\fIoptions args\fP] [\fIfiles\fP.\|.\|.]
+.I Requires:
+the file
+.` "$CVSROOT/CVSROOT/history"
+.br
+.I Changes:
+nothing.
+.br
+\fBcvs\fP keeps a history file that tracks each use of the
+\fBcheckout\fP, \fBcommit\fP, \fBrtag\fP, \fBupdate\fP, and \fBrelease\fP
+commands. You can use
+.` "cvs history"
+to display this
+information in various formats.
+.SP
+.I Warning:
+.` "cvs history"
+uses
+.` "\-f",
+.` "\-l",
+.` "\-n",
+and
+.` "\-p"
+in ways that conflict with the
+descriptions in
+.SM
+COMMON COMMAND OPTIONS\c
+\&.
+.SP
+Several options (shown above as \fB\-\fP\fIreport\fP) control what
+kind of report is generated:
+.TP 1i
+.B \ \ \ \ \ \ \-c
+Report on each time \fBcommit\fP was used (i.e., each time the
+repository was modified).
+.TP 1i
+\fB\ \ \ \ \ \ \-m\fP \fImodule\fP
+Report on a particular \fImodule\fP. (You can meaningfully use
+\fB\-m\fP more than once on the command line.)
+.TP 1i
+.B \ \ \ \ \ \ \-o
+Report on checked-out modules.
+.TP 1i
+.B \ \ \ \ \ \ \-T
+Report on all tags.
+.TP 1i
+\fB\ \ \ \ \ \ \-x\fP \fItyp\fP
+Extract a particular set of record types \fIX\fP from the \fBcvs\fP
+history. The types are indicated by single letters, which you may
+specify in combination.
+Certain commands have a single record type: \fBcheckout\fP (type `O'),
+\fBrelease\fP (type `F'), and \fBrtag\fP (type `T'). One of four
+record types may result from an \fBupdate\fP: `W', when the working copy
+of a file is deleted during update (because it was gone from the
+repository); `U', when a working file was copied from the
+repository; `G', when a merge was necessary and it succeeded; and 'C',
+when a merge was necessary but collisions were detected (requiring
+manual merging). Finally, one of three record types results from
+\fBcommit\fP: `M', when a file was modified; `A', when a file is first
+added; and `R', when a file is removed.
+.TP 1i
+.B \ \ \ \ \ \ \-e
+Everything (all record types); equivalent to specifying
+.` "\-xMACFROGWUT".
+.PP
+.RS .5i
+The options shown as \fB\-\fP\fIflags\fP constrain the report without
+requiring option arguments:
+.RE
+.TP 1i
+.B \ \ \ \ \ \ \-a
+Show data for all users (the default is to show data only for the user
+executing
+.` "cvs history").
+.TP 1i
+.B \ \ \ \ \ \ \-l
+Show last modification only.
+.TP 1i
+.B \ \ \ \ \ \ \-w
+Show only the records for modifications done from the same working
+directory where
+.` "cvs history"
+is executing.
+.PP
+.RS .5i
+The options shown as \fB\-\fP\fIoptions args\fP constrain the report
+based on an argument:
+.RE
+.TP 1i
+\fB\ \ \ \ \ \ \-b\fP \fIstr\fP
+Show data back to a record containing the string \fIstr\fP in either
+the module name, the file name, or the repository path.
+.TP 1i
+\fB\ \ \ \ \ \ \-D\fP \fIdate\fP
+Show data since \fIdate\fP.
+.TP 1i
+\fB\ \ \ \ \ \ \-p\fP \fIrepository\fP
+Show data for a particular source repository (you can specify several
+\fB\-p\fP options on the same command line).
+.TP 1i
+\fB\ \ \ \ \ \ \-r\fP \fIrev\fP
+Show records referring to revisions since the revision or tag
+named \fIrev\fP appears in individual RCS files.
+Each
+.SM RCS
+file is searched for the revision or tag.
+.TP 1i
+\fB\ \ \ \ \ \ \-t\fP \fItag\fP
+Show records since tag \fItag\fP was last added to the the history file.
+This differs from the \fB-r\fP flag above in that it reads
+only the history file, not the
+.SM RCS
+files, and is much faster.
+.TP 1i
+\fB\ \ \ \ \ \ \-u\fP \fIname\fP
+Show records for user \fIname\fP.
+.PP
+.TP
+\fBimport\fP [\fB\-\fP\fIoptions\fP] \fIrepository vendortag releasetag\fP.\|.\|.
+.I Requires:
+Repository, source distribution directory.
+.br
+.I Changes:
+repository.
+.br
+Use
+.` "cvs import"
+to incorporate an entire source
+distribution from an outside source (e.g., a source vendor) into your
+source repository directory. You can use this command both for
+initial creation of a repository, and for wholesale updates to the
+module form the outside source.
+.SP
+The \fIrepository\fP argument gives a directory name (or a path to a
+directory) under the CVS root directory for repositories; if the
+directory did not exist, \fBimport\fP creates it.
+.SP
+When you use \fBimport\fP for updates to source that has been modified in your
+source repository (since a prior \fBimport\fP), it
+will notify you of any files that conflict in the two branches of
+development; use
+.` "cvs checkout -j"
+to reconcile the differences, as \fBimport\fP instructs you to do.
+.SP
+By default, certain file names are ignored during
+.` "cvs import":
+names associated with
+.SM CVS
+administration, or with other common source control systems; common
+names for patch files, object files, archive files, and editor backup
+files; and other names that are usually artifacts of assorted utilities.
+Currently, the default list of ignored files includes files matching
+these names:
+.SP
+.in +1i
+.ft B
+.nf
+RCSLOG RCS SCCS
+CVS* cvslog.*
+tags TAGS
+\&.make.state .nse_depinfo
+*~ #* .#* ,*
+*.old *.bak *.orig *.rej .del\-*
+*.a *.o *.Z *.elc *.ln core
+.fi
+.ft P
+.in -1i
+.SP
+The outside source is saved in a first-level
+.SM RCS
+branch, by default
+.` "1.1.1".
+Updates are leaves of this
+branch; for example, files from the first imported collection of
+source will be revision
+.` "1.1.1.1",
+then files from the first
+imported update will be revision
+.` "1.1.1.2",
+and so on.
+.SP
+At least three arguments are required. \fIrepository\fP is needed to
+identify the collection of source. \fIvendortag\fP is a tag for the
+entire branch (e.g., for
+.` "1.1.1").
+You must also specify at
+least one \fIreleasetag\fP to identify the files at the leaves created
+each time you execute
+.` "cvs import".
+.SP
+Three of the standard
+.B cvs
+command options are available: \fB\-Q\fP, \fB\-q\fP, and \fB\-m\fP
+\fImessage\fP. If you do not specify a logging message with
+\fB\-m\fP, your editor is invoked (as with \fBcommit\fP) to allow you
+to enter one.
+.SP
+There are two additional special options.
+.SP
+Use
+.` "\-b \fIbranch\fP"
+to specify a first-level branch other
+than
+.` "1.1.1".
+.SP
+Use
+.` "\-I \fIname\fP"
+to specify file names that should be
+ignored during \fBimport\fP. You can use this option repeatedly.
+To avoid ignoring any files at all (even those ignored by default),
+specify
+.` "\-I !".
+.TP
+\fBlog\fP [\fB\-l\fP] \fIrlog-options [files\fP\|.\|.\|.]
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+nothing.
+.br
+.I Synonym:
+.B rlog
+.br
+Display log information for \fIfiles\fP.
+.` "cvs log"
+calls
+the
+.SM RCS
+utility \fBrlog\fP; all the options described in
+.BR rlog ( 1 )
+are available. Among the more useful \fBrlog\fP options are \fB\-h\fP
+to display only the header (including tag definitions, but omitting
+most of the full log); \fB\-r\fP to select logs on particular
+revisions or ranges of revisions; and \fB\-d\fP to select particular
+dates or date ranges. See
+.BR rlog ( 1 )
+for full explanations.
+This command is recursive by default, unless the
+.B \-l
+option is specified.
+.TP
+\fBrdiff\fP [\fB\-\fP\fIflags\fP] [\fB\-V\fP \fIvn\fP] [\fB\-r\fP \fIt\fP|\fB\-D\fP \fId\fP [\fB\-r\fP \fIt2\fP|\fB\-D\fP \fId2\fP]] \fImodules\|.\|.\|.\fP
+.I Requires:
+repository.
+.br
+.I Changes:
+nothing.
+.br
+.I Synonym:
+.B patch
+.br
+Builds a Larry Wall format
+.BR patch ( 1 )
+file between two releases, that can be fed directly into the
+.B patch
+program to bring an old release up-to-date with the new release.
+(This is one of the few \fBcvs\fP commands that operates directly from
+the repository, and doesn't require a prior
+.BR checkout .)
+The diff output is sent to the standard output device.
+You can specify (using the standard \fB\-r\fP and \fB\-D\fP options)
+any combination of one or two revisions or dates.
+If only one revision or date is specified, the
+patch file reflects differences between that revision or date and the
+current ``head'' revisions in the
+.SM RCS
+file.
+.SP
+Note that if the software release affected
+is contained in more than one directory, then it may be necessary to
+specify the
+.B \-p
+option to the
+.B patch
+command when patching the old sources, so that
+.B patch
+is able to find the files that are located in other directories.
+.SP
+If you use the option \fB\-V\fP \fIvn\fP,
+.SM RCS
+keywords are expanded according to the rules current in
+.SM RCS
+version \fIvn\fP (the expansion format changed with
+.SM RCS
+version 5).
+.SP
+The standard option \fIflags\fP \fB\-f\fP, \fB\-l\fP, \fB\-Q\fP, and
+\fB\-q\fP are available with this command. There are also several
+special options flags:
+.SP
+If you use the
+.B \-s
+option, no patch output is produced.
+Instead, a summary of the changed or added files between the two
+releases is sent to the standard output device.
+This is useful for finding out, for example, which files have changed
+between two dates or revisions.
+.SP
+If you use the
+.B \-t
+option, a diff of the top two revisions is sent to the standard output device.
+This is most useful for seeing what the last change to a file was.
+.SP
+If you use the
+.B \-u
+option, the patch output uses the newer ``unidiff'' format for context
+diffs.
+.SP
+You can use
+.B \-c
+to explicitly specify the
+.` "diff \-c"
+form of context diffs
+(which is the default), if you like.
+.TP
+\fBrelease\fP [\fB\-dQq\fP] \fImodules\fP\|.\|.\|.
+.I Requires:
+Working directory.
+.br
+.I Changes:
+Working directory, history log.
+.br
+This command is meant to safely cancel the effect of
+.` "cvs checkout'.
+Since
+.B cvs
+doesn't lock files, it isn't strictly necessary to use this command.
+You can always simply delete your working directory, if you
+like; but you risk losing changes you may have forgotten, and you
+leave no trace in the
+.B cvs
+history file that you've abandoned your checkout.
+.SP
+Use
+.` "cvs release"
+to avoid these problems. This command
+checks that no un-committed changes are present; that you are
+executing it from immediately above, or inside, a \fBcvs\fP working
+directory; and that the repository recorded for your files is the same
+as the repository defined in the module database.
+.SP
+If all these conditions are true,
+.` "cvs release"
+leaves a
+record of its execution (attesting to your intentionally abandoning
+your checkout) in the
+.B cvs
+history log.
+.SP
+You can use the \fB\-d\fP flag to request that your working copies of
+the source files be deleted if the \fBrelease\fP succeeds.
+.TP
+\fBremove\fP [\fB\-lR\fP] [\fIfiles\|.\|.\|.\fP]
+.I Requires:
+Working directory.
+.br
+.I Changes:
+Working directory.
+.br
+.I Synonyms:
+.BR rm ", " delete
+.br
+Use this command to declare that you wish to remove \fIfiles\fP from
+the source repository. Like most
+.B cvs
+commands,
+.` "cvs remove"
+works on files in your working
+directory, not directly on the repository. As a safeguard, it also
+requires that you first erase the specified files from your working
+directory.
+.SP
+The files are not actually removed until you apply your changes to the
+repository with
+.BR commit ;
+at that point, the corresponding
+.SM RCS
+files in the source repository are
+.I moved
+into the
+.` "Attic"
+directory (also within the source repository).
+.SP
+This command is recursive by default, scheduing all physically removed
+files that it finds for removal by the next
+.BR commit .
+Use the
+.B \-l
+option to avoid this recursion, or just specify that actual files that you
+wish remove to consider.
+.TP
+\fBrtag\fP [\fB\-f\|alnRQq\fP] [\fB\-b\fP] [\fB\-d\fP] [\fB\-r\fP \fItag\fP | \fB\-D\fP \fIdate\fP] \fIsymbolic_tag\fP \fImodules\|.\|.\|.\fP
+.I Requires:
+repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B rfreeze
+.br
+You can use this command to assign symbolic tags to particular,
+explicitly specified source versions in the repository.
+.` "cvs rtag"
+works directly on the repository contents (and requires no
+prior
+.BR checkout ).
+Use
+.` "cvs tag"
+instead, to base the selection of
+versions to tag on the contents of your working directory.
+.SP
+In general, tags (often the symbolic names of software distributions)
+should not be removed, but the
+.B \-d
+option is available as a means to remove completely obsolete symbolic names
+if necessary (as might be the case for an Alpha release, say).
+.SP
+The \fB-b\fP option makes the tag a ``branch'' tag, allowing
+concurrent, isolated development.
+This is most useful for creating a patch to a previously released software
+distribution.
+.SP
+You can use the standard \fB\-r\fP and \fB\-D\fP options to tag only those
+files that already contain a certain tag. This method would be used
+to rename a tag: tag only the files identified by the old tag, then delete the
+old tag, leaving the new tag on exactly the same files as the old tag.
+.SP
+.B rtag
+executes recursively by default, tagging all subdirectories of
+\fImodules\fP you specify in the argument. You can restrict its
+operation to top-level directories with the standard \fB\-l\fP option;
+or you can explicitly request recursion with \fB\-R\fP.
+.SP
+The modules database can specify a program to execute whenever a tag
+is specified; a typical use is to send electronic mail to a group of
+interested parties. If you want to bypass that program, use the
+standard \fB\-n\fP option.
+.SP
+The standard options \fB\-Q\fP and \fB\-q\fP are also available with
+this command.
+.SP
+Use the
+.B \-a
+option to have
+.B rtag
+look in the
+.` "Attic"
+for removed files that contin the specified tag.
+The tag is removed from these files, which makes it convenient to re-use a
+symbolic tag as development continues (and files get removed from the
+up-coming distribution).
+.TP
+\fBstatus\fP [\fB\-lR\fP] [\fB\-v\fP] [\fIfiles\fP\|.\|.\|.]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+nothing.
+.br
+Display a brief report on the current status of \fIfiles\fP with
+respect to the source repository, including any ``sticky'' tags,
+dates, or \fB\-k\fP options. (``Sticky'' options will restrict how
+.` "cvs update"
+operates until you reset them; see the
+description of
+.` "cvs update \-A\|.\|.\|.".)
+.SP
+You can also use this command to anticipate the potential impact of a
+.` "cvs update"
+on your working source directory. If you do
+not specify any \fIfiles\fP explicitly, reports are shown for all
+files that \fBcvs\fP has placed in your working directory. You can
+limit the scope of this search to the current directory itself (not
+its subdirectories) with the standard \fB\-l\fP option flag; or you
+can explicitly request recursive status reports with the \fB\-R\fP
+option.
+.SP
+The
+.B \-v
+option causes the symbolic tags for the
+.SM RCS
+file to be displayed as well.
+.TP
+\fBtag\fP [\fB\-lQqR\fP] [\fB\-b\fP] [\fB\-d\fP] \fIsymbolic_tag\fP [\fIfiles\fP\|.\|.\|.\|]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B freeze
+.br
+Use this command to assign symbolic tags to the nearest repository
+versions to your working sources. The tags are applied immediately to
+the repository, as with \fBrtag\fP, but the versions are supplied
+implicitly by the \fBcvs\fP records of your working files' history
+rather than applied explicitly.
+.SP
+One use for tags is to record a ``snapshot'' of the current sources
+when the software freeze date of a project arrives. As bugs are fixed
+after the freeze date, only those changed sources that are to be part
+of the release need be re-tagged.
+.SP
+The symbolic tags are meant to permanently record which revisions of which
+files were used in creating a software distribution.
+The
+.B checkout
+and
+.B update
+commands allow you to extract an exact copy of a tagged release at any time in
+the future, regardless of whether files have been changed, added, or removed
+since the release was tagged.
+.SP
+If you use
+.` "cvs tag \-d \fIsymbolic_tag\fP\|.\|.\|.",
+the
+symbolic tag you specify is
+.I deleted
+instead of being added. \fIWarning\fP: Be very certain of your ground
+before you delete a tag; doing this effectively discards some
+historical information, which may later turn out to have been valuable.
+.SP
+The \fB-b\fP option makes the tag a ``branch'' tag, allowing
+concurrent, isolated development.
+This is most useful for creating a patch to a previously released software
+distribution.
+.SP
+Normally,
+.B tag
+executes recursively through subdirectories; you can prevent this by
+using the standard \fB\-l\fP option, or specify the recursion
+explicitly by using \fB\-R\fP.
+.TP
+\fBupdate\fP [\fB\-Adf\|lPpQqR\fP] [\fB\-d\fP] [\fB\-r\fP \fItag\fP|\fB\-D\fP \fIdate\fP] \fIfiles\|.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+working directory.
+.br
+After you've run
+.B checkout
+to create your private copy of source from the common repository,
+other developers will continue changing the central source. From time
+to time, when it is convenient in your development process, you can
+use the
+.B update
+command
+from within your working directory to reconcile your work with any
+revisions applied to the source repository since your last
+.B checkout
+or
+.BR update .
+.SP
+.B update
+keeps you informed of its progress by printing a line for each file,
+prefaced with one of the characters
+.` "U A R M C ?"
+to indicate the status of the file:
+.TP 1i
+\fBU\fP \fIfile\fP
+The file was brought \fIup to date\fP with respect to the repository.
+This is done for any file that exists in the repository but not in
+your source, and for files that you haven't changed but are not the most
+recent versions available in the repository.
+.TP 1i
+\fBA\fP \fIfile\fP
+The file has been \fIadded\fP to your private copy of the sources, and
+will be added to the
+.SM RCS
+source repository when you run
+.` "cvs commit"
+on the file.
+This is a reminder to you that the file needs to be committed.
+.TP 1i
+\fBR\fP \fIfile\fP
+The file has been \fIremoved\fP from your private copy of the sources, and
+will be removed from the
+.SM RCS
+source repository when you run
+.` "cvs commit"
+on the file.
+This is a reminder to you that the file needs to be committed.
+.TP 1i
+\fBM\fP \fIfile\fP
+The file is \fImodified\fP in your working directory.
+.` "M"
+can indicate one of two states for a file you're working on: either
+there were no modifications to the same file in the repository, so
+that your file remains as you last saw it; or there were modifications
+in the repository as well as in your copy, but they were
+\fImerged\fP successfully, without conflict, in your working
+directory.
+.TP 1i
+\fBC\fP \fIfile\fP
+A \fIconflict\fP was detected while trying to merge your changes to
+\fIfile\fP with changes from the source repository. \fIfile\fP (the
+copy in your working directory) is now the output of the
+.BR rcsmerge ( 1 )
+command on the two versions; an unmodified copy of your file is also
+in your working directory, with the name `\fB.#\fP\fIfile\fP\fB.\fP\fIversion\fP',
+where
+.I version
+is the
+.SM RCS
+revision that your modified file started from.
+(Note that some systems automatically purge files that begin with
+\&
+.` ".#"
+if they have not been accessed for a few days.
+If you intend to keep a copy of your original file, it is a very good
+idea to rename it.)
+.TP 1i
+\fB?\fP \fIfile\fP
+\fIfile\fP is in your working directory, but does not correspond to
+anything in the source repository, and is not in the list of files
+for \fBcvs\fP to ignore (see the description of the \fB\-I\fP option).
+.PP
+.RS .5i
+.SP
+Use the
+.B \-A
+option to reset any sticky tags, dates, or
+.B \-k
+options. (If you get a working copy of a file by using one of the
+\fB\-r\fP, \fB\-D\fP, or \fB\-k\fP options, \fBcvs\fP remembers the
+corresponding tag, date, or \fIkflag\fP and continues using it on
+future updates; use the \fB\-A\fP option to make \fBcvs\fP forget these
+specifications, and retrieve the ``head'' version of the file).
+.SP
+The \fB\-j\fP\fIbranch\fP option
+merges the changes made between the
+resulting revision and the revision that it is based on (e.g., if
+the tag refers to a branch,
+.B cvs
+will merge all changes made in
+that branch into your working file).
+.SP
+With two \fB-j\fP options,
+.B 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:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs update -j1.5 -j1.3 foo.c # note the order...
+.fi
+.ft P
+.in -1i
+.SP
+In addition, each \fB-j\fP 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.
+.SP
+.in +1i
+.ft B
+.nf
+-jSymbolic_Tag:Date_Specifier
+.fi
+.ft P
+.in -1i
+.SP
+Use the
+.B \-d
+option to create any directories that exist in the repository if they're
+missing from the working directory. (Normally, update acts only on
+directories and files that were already enrolled in your
+working directory.) This is useful for updating directories
+that were created in the repository since the initial
+\fBcheckout\fP; but it has an unfortunate side effect. If you
+deliberately avoided certain directories in the repository when you
+created your working directory (either through use of a module name or by
+listing explicitly the files and directories you wanted on the
+command line), then updating with
+.B \-d
+will create those directories, which may not be what you want.
+.SP
+Use \fB\-I\fP \fIname\fP to ignore files whose names match \fIname\fP
+(in your working directory) during the update. You can specify
+\fB\-I\fP more than once on the command line to specify several files
+to ignore. By default,
+\fBupdate\fP ignores files whose names match any of the following:
+.SP
+.in +1i
+.ft B
+.nf
+RCSLOG RCS SCCS
+CVS* cvslog.*
+tags TAGS
+\&.make.state .nse_depinfo
+*~ #* .#* ,*
+*.old *.bak *.orig *.rej .del\-*
+*.a *.o *.Z *.elc *.ln core
+.fi
+.ft P
+.in -1i
+.SP
+Use
+.` "\-I !"
+to avoid ignoring any files at all.
+.SP
+The standard \fBcvs\fP command options \fB\-f\fP, \fB\-k\fP,
+\fB\-l\fP, \fB\-P\fP, \fB\-p\fP, \fB\-Q\fP, \fB\-q\fP, and \fB\-r\fP
+are also available with \fBupdate\fP.
+.RE
+.SH "FILES"
+For more detailed information on
+.B cvs
+supporting files, see
+.BR cvs ( 5 ).
+.LP
+.I
+Files in working directories:
+.TP
+CVS
+A directory of \fBcvs\fP administrative files.
+.I
+Do not delete.
+.TP
+CVS/Entries
+List and status of files in your working directory.
+.TP
+CVS/Entries.Backup
+A backup of
+.` "CVS/Entries".
+.TP
+CVS/Entries.Static
+Flag: do not add more entries on
+.` "cvs update".
+.TP
+CVS/Repository
+Pathname to the corresponding directory in the source repository.
+.TP
+CVS/Tag
+Contains the per-directory ``sticky'' tag or date information.
+This file is created/updated when you specify
+.B \-r
+or
+.B \-D
+to the
+.B checkout
+or
+.B update
+commands, and no files are specified.
+.TP
+CVS/Checkin.prog
+Name of program to run on
+.` "cvs commit".
+.TP
+CVS/Update.prog
+Name of program to run on
+.` "cvs update".
+.LP
+.I
+Files in source repositories:
+.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 dueing a
+.` "cvs commit"
+operation.
+.TP
+CVSROOT/editinfo,v
+Records programs for editing/validating
+.` "cvs commit"
+log entries.
+.TP
+Attic
+Directory for removed source files.
+.TP
+#cvs.lock
+A lock directory created by
+.B cvs
+when doing sensitive changes to the
+.SM RCS
+source repository.
+.TP
+#cvs.tfl.\fIpid\fP
+Temporary lock file for repository.
+.TP
+#cvs.rfl.\fIpid\fP
+A read lock.
+.TP
+#cvs.wfl.\fIpid\fP
+A write lock.
+.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.
+.TP
+.SM CVSREAD
+If this is set,
+.B checkout
+and
+.B update
+will try hard to make the files in your working directory read-only.
+When this is not set, the default behavior is to permit modification
+of your working files.
+.TP
+.SM RCSBIN
+Specifies the full pathname where to find
+.SM RCS
+programs, such as
+.BR co ( 1 )
+and
+.BR ci ( 1 ).
+If not set, a compiled-in value is used; see the display from
+.` "cvs \-v".
+.TP
+.SM EDITOR
+Specifies the program to use for recording log messages during
+.BR commit .
+If not set, the default is
+.BR /usr/ucb/vi .
+.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/cvs/cvs.5 b/gnu/usr.bin/cvs/cvs/cvs.5
new file mode 100644
index 000000000000..49ca56268332
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.5
@@ -0,0 +1,326 @@
+.TH cvs 5 "12 February 1992"
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.SH NAME
+cvs \- Concurrent Versions System support files
+.SH SYNOPSIS
+.hy 0
+.na
+.TP
+.B $CVSROOT/CVSROOT/modules,v
+.TP
+.B $CVSROOT/CVSROOT/commitinfo,v
+.TP
+.B $CVSROOT/CVSROOT/loginfo,v
+.TP
+.B $CVSROOT/CVSROOT/rcsinfo,v
+.TP
+.B $CVSROOT/CVSROOT/editinfo,v
+.TP
+.B $CVSROOT/CVSROOT/cvsignore,v
+.TP
+.B $CVSROOT/CVSROOT/history
+.ad b
+.hy 1
+.SH DESCRIPTION
+.B cvs
+is a system for providing source control to hierarchical collections
+of source directories. Commands and procedures for using \fBcvs\fP
+are described in
+.BR cvs ( 1 ).
+.SP
+.B cvs
+manages \fIsource repositories\fP, the directories containing master
+copies of the revision-controlled files, by copying particular
+revisions of the files to (and modifications back from) developers'
+private \fIworking directories\fP. In terms of file structure, each
+individual source repository is an immediate subdirectory of
+\fB$CVSROOT\fP.
+.SP
+The files described here are supporting files; they do not have to
+exist for \fBcvs\fP to operate, but they allow you to make \fBcvs\fP
+operation more flexible.
+.SP
+The
+.BR cvsinit ( 1 )
+shell script included at the top-level of the
+.B cvs
+distribution can be used to setup an initial
+.B $CVSROOT/CVSROOT
+area, if you don't have one already.
+.SP
+You can use the `\|modules\|' file to define symbolic names for
+collections of source maintained with \fBcvs\fP. If there is no
+`\|modules\|' file, developers must specify complete path names
+(absolute, or relative to \fB$CVSROOT\fP) for the files they wish to
+manage with \fBcvs\fP commands.
+.SP
+You can use the `\|commitinfo\|' file to define programs to execute
+whenever `\|\fBcvs commit\fP\|' is about to execute.
+These programs are used for ``pre-commit'' checking to verify that the
+modified, added, and removed files are really ready to be committed.
+Some uses for this check might be to turn off a portion (or all) of the
+source repository from a particular person or group.
+Or, perhaps, to verify that the changed files conform to the site's
+standards for coding practice.
+.SP
+You can use the `\|loginfo\|' file to define programs to execute after
+any
+.BR commit ,
+which writes a log entry for changes in the repository.
+These logging programs might be used to append the log message to a file.
+Or send the log message through electronic mail to a group of developers.
+Or, perhaps, post the log message to a particular newsgroup.
+.SP
+You can use the `\|rcsinfo\|' file to define forms for log messages.
+.SP
+You can use the `\|editinfo\|' file to define a program to execute for
+editing/validating `\|\fBcvs commit\fP\|' log entries.
+This is most useful when used with a `\|rcsinfo\|' forms specification, as
+it can verify that the proper fields of the form have been filled in by the
+user committing the change.
+.SP
+You can use the `\|cvsignore\|' file to specify the default list of
+files to ignore during \fBupdate\fP.
+.SP
+You can use the `\|history\|' file to record the \fBcvs\fP commands
+that affect the repository.
+The creation of this file enables history logging.
+.SH FILES
+.TP
+.B modules
+The `\|modules\|' file records your definitions of names for
+collections of source code. \fBcvs\fP will use these definitions if
+you create a file with the right format in
+`\|\fB$CVSROOT/CVSROOT/modules,v\fP\|'.
+The
+.BR mkmodules ( 1 )
+command should be run whenever the modules file changes, so that the
+appropriate files can be generated (depending on how you have configured
+.B cvs
+operation).
+.SP
+To allow convenient editing of the `\|modules\|' file itself, the file should
+include an entry like the following (where \fIlocalbin\fP represents the
+directory where your site installs programs like
+.BR mkmodules ( 1 )):
+.SP
+.nf
+\&\fBmodules \-i /\fP\fIlocalbin\fP\fB/mkmodules CVSROOT modules\fP
+.fi
+.SP
+This defines the name `\|\fBmodules\fP\|' as the module name for the
+file itself, so that you can use
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs checkout modules
+.fi
+.ft P
+.in -1i
+.SP
+to get an editable copy of the file. You should define similar module
+entries for the other configuration files described here (except
+\&`\|history\|').
+The
+.BR cvsinit ( 1 )
+script will setup a smilar `\|modules\|' file for you automatically.
+.SP
+The `\|modules\|' file may contain blank lines and comments (lines
+beginning with `\|\fB#\fP\|') as well as module definitions.
+Long lines can be continued on the next line by specifying a backslash
+(``\e'') as the last character on the line.
+.SP
+A \fImodule definition\fP is a single line of the `\|modules\|' file,
+in either of two formats. In both cases, \fImname\fP represents the
+symbolic module name, and the remainder of the line is its definition.
+.SP
+\fImname\fP \fB\-a\fP \fIaliases\fP\|.\|.\|.
+.br
+This represents the simplest way of defining a module \fImname\fP.
+The `\|\fB\-a\fP\|' flags the definition as a simple alias: \fBcvs\fP
+will treat any use of \fImname\fP (as a command argument) as if the list
+of names \fIaliases\fP had been specified instead. \fIaliases\fP may
+contain either other module names or paths. When you use paths in
+\fIaliases\fP, `\|\fBcvs checkout\fP\|' creates all intermediate
+directories in the working directory, just as if the path had been
+specified explicitly in the \fBcvs\fP arguments.
+.SP
+.nf
+\fImname\fP [ \fIoptions\fP ] \fIdir\fP [ \fIfiles\fP\|.\|.\|. ] [ \fB&\fP\fImodule\fP\|.\|.\|. ]
+.fi
+.SP
+In the simplest case, this form of module definition reduces to
+`\|\fImname dir\fP\|'. This defines all the files in directory
+\fIdir\fP as module \fImname\fP. \fIdir\fP is a relative path (from
+\fB$CVSROOT\fP) to a directory of source in one of the source
+repositories. In this case, on \fBcheckout\fP, a single directory
+called \fImname\fP is created as a working directory; no intermediate
+directory levels are used by default, even if \fIdir\fP was a path
+involving several directory levels.
+.SP
+By explicitly specifying \fIfiles\fP in the module definition after
+\fIdir\fP, you can select particular files from directory
+\fIdir\fP. The sample definition for \fBmodules\fP is an example of
+a module defined with a single file from a particular directory. Here
+is another example:
+.SP
+.nf
+.ft B
+m4test unsupported/gnu/m4 foreach.m4 forloop.m4
+.ft P
+.fi
+.SP
+With this definition, executing `\|\fBcvs checkout m4test\fP\|'
+will create a single working directory `\|m4test\|' containing the two
+files listed, which both come from a common directory several levels
+deep in the \fBcvs\fP source repository.
+.SP
+A module definition can refer to other modules by including
+`\|\fB&\fP\fImodule\fP\|' in its definition. \fBcheckout\fP creates
+a subdirectory for each such \fImodule\fP, in your working directory.
+.br
+.I
+New in \fBcvs\fP 1.3;
+avoid this feature if sharing module definitions with older versions
+of \fBcvs\fP.
+.SP
+Finally, you can use one or more of the following \fIoptions\fP in
+module definitions:
+.SP
+\&`\|\fB\-d\fP \fIname\fP\|', to name the working directory something
+other than the module name.
+.br
+.I
+New in \fBcvs\fP 1.3;
+avoid this feature if sharing module definitions with older versions
+of \fBcvs\fP.
+.SP
+\&`\|\fB\-i\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are committed. \fIprog\fP runs with a
+single argument, the full pathname of the affected directory in a
+source repository. The `\|commitinfo\|', `\|loginfo\|', and
+`\|editinfo\|' files provide other ways to call a program on \fBcommit\fP.
+.SP
+`\|\fB\-o\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are checked out. \fIprog\fP runs
+with a single argument, the module name.
+.SP
+`\|\fB\-t\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are tagged. \fIprog\fP runs with two
+arguments: the module name and the symbolic tag specified to \fBrtag\fP.
+.SP
+`\|\fB\-u\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever `\|\fBcvs update\fP\|' is executed from the top-level
+directory of the checked-out module. \fIprog\fP runs with a
+single argument, the full path to the source repository for this module.
+.TP
+\&\fBcommitinfo\fP, \fBloginfo\fP, \fBrcsinfo\fP, \fBeditinfo\fP
+These files all specify programs to call at different points in the
+`\|\fBcvs commit\fP\|' process. They have a common structure.
+Each line is a pair of fields: a regular expression, separated by
+whitespace from a filename or command-line template.
+Whenever one of the regular expression matches a directory name in the
+repository, the rest of the line is used.
+If the line begins with a \fB#\fP character, the entire line is considered
+a comment and is ignored.
+Whitespace between the fields is also ignored.
+.SP
+For `\|loginfo\|', the rest of the
+line is a command-line template to execute.
+The templates can include not only
+a program name, but whatever list of arguments you wish. If you write
+`\|\fB%s\fP\|' somewhere on the argument list, \fBcvs\fP supplies, at
+that point, the list of files affected by the \fBcommit\fP.
+The first entry in the list is the relative path within the source
+repository where the change is being made.
+The remaining arguments list the files that are being modified, added, or
+removed by this \fBcommit\fP invocation.
+.SP
+For `\|commitinfo\|', the rest of the line is a command-line template to
+execute.
+The template can include can include not only a program name, but whatever
+list of arguments you wish.
+The full path to the current source repository is appended to the template,
+followed by the file names of any files involved in the commit (added,
+removed, and modified files).
+.SP
+For `\|rcsinfo\|', the rest of the line is the full path to a file that
+should be loaded into the log message template.
+.SP
+For `\|editinfo\|', the rest of the line is a command-line template to
+execute.
+The template can include can include not only a program name, but whatever
+list of arguments you wish.
+The full path to the current log message template file is appended to the
+template.
+.SP
+You can use one of two special strings instead of a regular
+expression: `\|\fBALL\fP\|' specifies a command line template that
+must always be executed, and `\|\fBDEFAULT\fP\|' specifies a command
+line template to use if no regular expression is a match.
+.SP
+The `\|commitinfo\|' file contains commands to execute \fIbefore\fP any
+other \fBcommit\fP activity, to allow you to check any conditions that
+must be satisfied before \fBcommit\fP can proceed. The rest of the
+\fBcommit\fP will execute only if all selected commands from this file
+exit with exit status \fB0\fP.
+.SP
+The `\|rcsinfo\|' file allows you to specify \fIlog templates\fP for
+the \fBcommit\fP logging session; you can use this to provide a form
+to edit when filling out the \fBcommit\fP log. The field after the
+regular expression, in this file, contains filenames (of files
+containing the logging forms) rather than command templates.
+.SP
+The `\|editinfo\|' file allows you to execute a script \fIbefore the
+commit starts\fP, but after the log information is recorded. These
+"edit" scripts can verify information recorded in the log file. If
+the edit script exits wth a non-zero exit status, the commit is aborted.
+.SP
+The `\|loginfo\|' file contains commands to execute \fIat the end\fP
+of a commit. The text specified as a commit log message is piped
+through the command; typical uses include sending mail, filing an
+article in a newsgroup, or appending to a central file.
+.TP
+\&\fBcvsignore\fP, \fB.cvsignore\fP
+The default list of files (or
+.BR sh ( 1 )
+file name patterns) to ignore during `\|\fBcvs update\fP\|'.
+At startup time, \fBcvs\fP loads the compiled in default list of file name
+patterns (see
+.BR cvs ( 1 )).
+Then the per-repository list included in \fB$CVSROOT/CVSROOT/cvsignore\fP
+is loaded, if it exists.
+Then the per-user list is loaded from `\|$HOME/.cvsignore\|'.
+Finally, as \fBcvs\fP traverses through your directories, it will load any
+per-directory `\|.cvsignore\|' files whenever it finds one.
+These per-directory files are only valid for exactly the directory that
+contains them, not for any sub-directories.
+.TP
+.B history
+Create this file in \fB$CVSROOT/CVSROOT\fP to enable history logging
+(see the description of `\|\fBcvs history\fP\|').
+.SH "SEE ALSO"
+.BR cvs ( 1 ),
+.BR mkmodules ( 1 ).
+.SH COPYING
+Copyright \(co 1992 Cygnus Support, Brian Berliner, and Jeff Polk
+.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/cvs/cvs.h b/gnu/usr.bin/cvs/cvs/cvs.h
new file mode 100644
index 000000000000..ec47581143de
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.h
@@ -0,0 +1,438 @@
+/* @(#)cvs.h 1.72 92/03/31 */
+
+#include "system.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <signal.h>
+#include "hash.h"
+#include "rcs.h"
+#include "regex.h"
+#include "fnmatch.h"
+#include "getopt.h"
+#include "wait.h"
+#include "config.h"
+#ifdef MY_NDBM
+#include "myndbm.h"
+#else
+#include <ndbm.h>
+#endif /* !MY_NDBM */
+
+/* XXX - for now this is static */
+#undef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN+2
+#else
+#define PATH_MAX 1024+2
+#endif
+
+/* just in case this implementation does not define this */
+#ifndef L_tmpnam
+#define L_tmpnam 50
+#endif
+
+#if __STDC__
+#define CONST const
+#define PTR void *
+#else
+#define CONST
+#define PTR char *
+#endif
+
+/*
+ * 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.3 kit.
+ *
+ * Definitions for the CVS Administrative directory and the files it contains.
+ * Here as #define's to make changing the names a simple task.
+ */
+#define CVSADM "CVS"
+#define CVSADM_ENT "CVS/Entries"
+#define CVSADM_ENTBAK "CVS/Entries.Backup"
+#define CVSADM_ENTSTAT "CVS/Entries.Static"
+#define CVSADM_REP "CVS/Repository"
+#define CVSADM_CIPROG "CVS/Checkin.prog"
+#define CVSADM_UPROG "CVS/Update.prog"
+#define CVSADM_TAG "CVS/Tag"
+
+/*
+ * The following are obsolete and are maintained here only so that they can be
+ * cleaned up during the transition
+ */
+#define OCVSADM "CVS.adm" /* for CVS 1.2 and earlier */
+#define CVSADM_FILE "CVS/Files"
+#define CVSADM_MOD "CVS/Mod"
+
+/*
+ * Definitions for the CVSROOT Administrative directory and the files it
+ * contains. This directory is created as a sub-directory of the $CVSROOT
+ * environment variable, and holds global administration information for the
+ * entire source repository beginning at $CVSROOT.
+ */
+#define CVSROOTADM "CVSROOT"
+#define CVSROOTADM_MODULES "modules"
+#define CVSROOTADM_LOGINFO "loginfo"
+#define CVSROOTADM_RCSINFO "rcsinfo"
+#define CVSROOTADM_COMMITINFO "commitinfo"
+#define CVSROOTADM_EDITINFO "editinfo"
+#define CVSROOTADM_HISTORY "history"
+#define CVSROOTADM_IGNORE "cvsignore"
+#define CVSNULLREPOS "Emptydir" /* an empty directory */
+
+/* support for the modules file (CVSROOTADM_MODULES) */
+#define CVSMODULE_OPTS "ad:i:lo:s:t:u:"/* options in modules file */
+#define CVSMODULE_SPEC '&' /* special delimiter */
+
+/*
+ * The following are obsolete and are maintained here only so that they can be
+ * cleaned up during the transition
+ */
+#define OCVSROOTADM "CVSROOT.adm" /* for CVS 1.2 and earlier */
+
+/* Other CVS file names */
+#define CVSATTIC "Attic"
+#define CVSLCK "#cvs.lock"
+#define CVSTFL "#cvs.tfl"
+#define CVSRFL "#cvs.rfl"
+#define CVSWFL "#cvs.wfl"
+#define CVSEXT_OPT ",p"
+#define CVSEXT_LOG ",t"
+#define CVSPREFIX ",,"
+#define CVSDOTIGNORE ".cvsignore"
+
+/* miscellaneous CVS defines */
+#define CVSEDITPREFIX "CVS: "
+#define CVSLCKAGE (60*60) /* 1-hour old lock files cleaned up */
+#define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */
+#define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */
+#define BAKPREFIX ".#" /* when rcsmerge'ing */
+#define DEVNULL "/dev/null"
+
+#define FALSE 0
+#define TRUE 1
+
+/*
+ * Special tags. -rHEAD refers to the head of an RCS file, regardless of any
+ * sticky tags. -rBASE refers to the current revision the user has checked
+ * out This mimics the behaviour of RCS.
+ */
+#define TAG_HEAD "HEAD"
+#define TAG_BASE "BASE"
+
+/* Environment variable used by CVS */
+#define CVSREAD_ENV "CVSREAD" /* make files read-only */
+#define CVSREAD_DFLT FALSE /* writable files by default */
+
+#define RCSBIN_ENV "RCSBIN" /* RCS binary directory */
+/* #define RCSBIN_DFLT Set by config.h */
+
+#define EDITOR_ENV "EDITOR" /* which editor to use */
+/* #define EDITOR_DFLT Set by config.h */
+
+#define CVSROOT_ENV "CVSROOT" /* source directory root */
+#define CVSROOT_DFLT NULL /* No dflt; must set for checkout */
+
+#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */
+
+/*
+ * If the beginning of the Repository matches the following string, strip it
+ * so that the output to the logfile does not contain a full pathname.
+ *
+ * If the CVSROOT environment variable is set, it overrides this define.
+ */
+#define REPOS_STRIP "/master/"
+
+/*
+ * The maximum number of files per each CVS directory. This is mainly for
+ * sizing arrays statically rather than dynamically. 3000 seems plenty for
+ * now.
+ */
+#define MAXFILEPERDIR 3000
+#define MAXLINELEN 5000 /* max input line from a file */
+#define MAXPROGLEN 30000 /* max program length to system() */
+#define MAXLISTLEN 40000 /* For [A-Z]list holders */
+#define MAXMESGLEN 10000 /* max RCS log message size */
+#define MAXDATELEN 50 /* max length for a date */
+
+/* The type of request that is being done in do_module() */
+enum mtype
+{
+ CHECKOUT, TAG, PATCH
+};
+
+/*
+ * defines for Classify_File() to determine the current state of a file.
+ * These are also used as types in the data field for the list we make for
+ * Update_Logfile in commit, import, and add.
+ */
+enum classify_type
+{
+ T_UNKNOWN = 1, /* no old-style analog existed */
+ T_CONFLICT, /* C (conflict) list */
+ T_NEEDS_MERGE, /* G (needs merging) list */
+ T_MODIFIED, /* M (needs checked in) list */
+ T_CHECKOUT, /* O (needs checkout) list */
+ T_ADDED, /* A (added file) list */
+ T_REMOVED, /* R (removed file) list */
+ T_REMOVE_ENTRY, /* W (removed entry) list */
+ T_UPTODATE, /* File is up-to-date */
+ T_TITLE /* title for node type */
+};
+typedef enum classify_type Ctype;
+
+/*
+ * a struct vers_ts contains all the information about a file including the
+ * user and rcs file names, and the version checked out and the head.
+ *
+ * this is usually obtained from a call to Version_TS which takes a tag argument
+ * for the RCS file if desired
+ */
+struct vers_ts
+{
+ char *vn_user; /* rcs version user file derives from
+ * it can have the following special
+ * values:
+ * empty = no user file
+ * 0 = user file is new
+ * -vers = user file to be removed */
+ char *vn_rcs; /* the verion for the rcs file
+ * (tag version?) */
+ char *ts_user; /* the timestamp for the user file */
+ char *ts_rcs; /* the user timestamp from entries */
+ char *options; /* opts from Entries file
+ * (keyword expansion) */
+ char *tag; /* tag stored in the Entries file */
+ char *date; /* date stored in the Entries file */
+ Entnode *entdata; /* pointer to entries file node */
+ RCSNode *srcfile; /* pointer to parsed src file info */
+};
+typedef struct vers_ts Vers_TS;
+
+/*
+ * structure used for list-private storage by ParseEntries() and
+ * Version_TS().
+ */
+struct stickydirtag
+{
+ int aflag;
+ char *tag;
+ char *date;
+ char *options;
+};
+
+/* flags for run_exec(), the fast system() for CVS */
+#define RUN_NORMAL 0x0000 /* no special behaviour */
+#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */
+#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */
+#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */
+#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */
+#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */
+#define RUN_TTY (char *)0 /* for the benefit of lint */
+
+/* Flags for find_{names,dirs} routines */
+#define W_LOCAL 0x01 /* look for files locally */
+#define W_REPOS 0x02 /* look for files in the repository */
+#define W_ATTIC 0x04 /* look for files in the attic */
+
+/* Flags for return values of direnter procs for the recursion processor */
+enum direnter_type
+{
+ R_PROCESS = 1, /* process files and maybe dirs */
+ R_SKIP_FILES, /* don't process files in this dir */
+ R_SKIP_DIRS, /* don't process sub-dirs */
+ R_SKIP_ALL /* don't process files or dirs */
+};
+typedef enum direnter_type Dtype;
+
+extern char *program_name, *command_name;
+extern char *Rcsbin, *Editor, *CVSroot;
+extern char *CurDir;
+extern int really_quiet, quiet;
+extern int use_editor;
+extern int cvswrite;
+
+extern int trace; /* Show all commands */
+extern int noexec; /* Don't modify disk anywhere */
+extern int logoff; /* Don't write history entry */
+
+/* Externs that are included directly in the CVS sources */
+#if __STDC__
+int Reader_Lock (char *xrepository);
+DBM *open_module (void);
+FILE *Fopen (char *name, char *mode);
+FILE *open_file (char *name, char *mode);
+List *Find_Dirs (char *repository, int which);
+List *ParseEntries (int aflag);
+char *Make_Date (char *rawdate);
+char *Name_Repository (char *dir, char *update_dir);
+char *Short_Repository (char *repository);
+char *getcaller (void);
+char *time_stamp (char *file);
+char *xmalloc (int bytes);
+char *xrealloc (char *ptr, int bytes);
+char *xstrdup (char *str);
+int No_Difference (char *file, Vers_TS * vers, List * entries);
+int Parse_Info (char *infofile, char *repository, int (*callproc) (), int all);
+int Reader_Lock (char *xrepository);
+int SIG_register (int sig, SIGTYPE (*fn) ());
+int Writer_Lock (List * list);
+int gethostname (char *name, int namelen);
+int ign_name (char *name);
+int isdir (char *file);
+int isfile (char *file);
+int islink (char *file);
+int isreadable (char *file);
+int iswritable (char *file);
+int link_file (char *from, char *to);
+int numdots (char *s);
+int run_exec (char *stin, char *stout, char *sterr, int flags);
+int unlink_file (char *f);
+int update (int argc, char *argv[]);
+int xcmp (char *file1, char *file2);
+int yesno (void);
+time_t get_date (char *date, struct timeb *now);
+void Create_Admin (char *dir, char *repository, char *tag, char *date);
+void Lock_Cleanup (void);
+void ParseTag (char **tagp, char **datep);
+void Scratch_Entry (List * list, char *fname);
+void WriteTag (char *dir, char *tag, char *date);
+void cat_module (int status);
+void check_entries (char *dir);
+void close_module (DBM * db);
+void copy_file (char *from, char *to);
+void error (int status, int errnum, char *message,...);
+void fperror (FILE * fp, int status, int errnum, char *message,...);
+void free_names (int *pargc, char *argv[]);
+void freevers_ts (Vers_TS ** versp);
+void ign_add (char *ign, int hold);
+void ign_add_file (char *file, int hold);
+void ign_setup (void);
+void line2argv (int *pargc, char *argv[], char *line);
+void make_directories (char *name);
+void make_directory (char *name);
+void rename_file (char *from, char *to);
+void run_arg (char *s);
+void run_args (char *fmt,...);
+void run_print (FILE * fp);
+void run_setup (char *fmt,...);
+void strip_path (char *path);
+void update_delproc (Node * p);
+void usage (char **cpp);
+void xchmod (char *fname, int writable);
+int Checkin (int type, char *file, char *repository, char *rcs, char *rev,
+ char *tag, char *message, List * entries);
+Ctype Classify_File (char *file, char *tag, char *date, char *options,
+ int force_tag_match, int aflag, char *repository,
+ List *entries, List *srcfiles, Vers_TS **versp);
+List *Find_Names (char *repository, int which, int aflag,
+ List ** optentries);
+void Register (List * list, char *fname, char *vn, char *ts,
+ char *options, char *tag, char *date);
+void Update_Logfile (char *repository, char *xmessage, char *xrevision,
+ FILE * xlogfp, List * xchanges);
+Vers_TS *Version_TS (char *repository, char *options, char *tag,
+ char *date, char *user, int force_tag_match,
+ int set_time, List * entries, List * xfiles);
+void do_editor (char *dir, char *message, char *repository,
+ List * changes);
+int do_module (DBM * db, char *mname, enum mtype m_type, char *msg,
+ int (*callback_proc) (), char *where, int shorten,
+ int local_specified, int run_module_prog, char *extra_arg);
+int do_recursion (int (*xfileproc) (), int (*xfilesdoneproc) (),
+ Dtype (*xdirentproc) (), int (*xdirleaveproc) (),
+ Dtype xflags, int xwhich, int xaflag, int xreadlock,
+ int xdosrcs);
+int do_update (int argc, char *argv[], char *xoptions, char *xtag,
+ char *xdate, int xforce, int local, int xbuild,
+ int xaflag, int xprune, int xpipeout, int which,
+ char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir);
+void history_write (int type, char *update_dir, char *revs, char *name,
+ char *repository);
+int start_recursion (int (*fileproc) (), int (*filesdoneproc) (),
+ Dtype (*direntproc) (), int (*dirleaveproc) (),
+ int argc, char *argv[], int local, int which,
+ int aflag, int readlock, char *update_preload,
+ int dosrcs);
+void SIG_beginCrSect ();
+void SIG_endCrSect ();
+#else /* !__STDC__ */
+DBM *open_module ();
+FILE *Fopen ();
+FILE *open_file ();
+List *Find_Dirs ();
+List *Find_Names ();
+List *ParseEntries ();
+Vers_TS *Version_TS ();
+char *Make_Date ();
+char *Name_Repository ();
+char *Short_Repository ();
+char *getcaller ();
+char *time_stamp ();
+char *xmalloc ();
+char *xrealloc ();
+char *xstrdup ();
+int Checkin ();
+Ctype Classify_File ();
+int No_Difference ();
+int Parse_Info ();
+int Reader_Lock ();
+int SIG_register ();
+int Writer_Lock ();
+int do_module ();
+int do_recursion ();
+int do_update ();
+int gethostname ();
+int ign_name ();
+int isdir ();
+int isfile ();
+int islink ();
+int isreadable ();
+int iswritable ();
+int link_file ();
+int numdots ();
+int run_exec ();
+int start_recursion ();
+int unlink_file ();
+int update ();
+int xcmp ();
+int yesno ();
+time_t get_date ();
+void Create_Admin ();
+void Lock_Cleanup ();
+void ParseTag ();
+void ParseTag ();
+void Register ();
+void Scratch_Entry ();
+void Update_Logfile ();
+void WriteTag ();
+void cat_module ();
+void check_entries ();
+void close_module ();
+void copy_file ();
+void do_editor ();
+void error ();
+void fperror ();
+void free_names ();
+void freevers_ts ();
+void history_write ();
+void ign_add ();
+void ign_add_file ();
+void ign_setup ();
+void line2argv ();
+void make_directories ();
+void make_directory ();
+void rename_file ();
+void run_arg ();
+void run_args ();
+void run_print ();
+void run_setup ();
+void strip_path ();
+void update_delproc ();
+void usage ();
+void xchmod ();
+void SIG_beginCrSect ();
+void SIG_endCrSect ();
+#endif /* __STDC__ */
diff --git a/gnu/usr.bin/cvs/cvs/diff.c b/gnu/usr.bin/cvs/cvs/diff.c
new file mode 100644
index 000000000000..db8b4b746e8d
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/diff.c
@@ -0,0 +1,407 @@
+/*
+ * 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.3 kit.
+ *
+ * Difference
+ *
+ * Run diff against versions in the repository. Options that are specified are
+ * passed on directly to "rcsdiff".
+ *
+ * Without any file arguments, runs diff against all the currently modified
+ * files.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)diff.c 1.52 92/04/10";
+#endif
+
+#if __STDC__
+static Dtype diff_dirproc (char *dir, char *pos_repos, char *update_dir);
+static int diff_dirleaveproc (char *dir, int err, char *update_dir);
+static int diff_file_nodiff (char *file, char *repository, List *entries,
+ List *srcfiles, Vers_TS *vers);
+static int diff_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static void diff_mark_errors (int err);
+#else
+static int diff_fileproc ();
+static Dtype diff_dirproc ();
+static int diff_dirleaveproc ();
+static int diff_file_nodiff ();
+static void diff_mark_errors ();
+#endif /* __STDC__ */
+
+static char *diff_rev1, *diff_rev2;
+static char *diff_date1, *diff_date2;
+static char *use_rev1, *use_rev2;
+static char *options;
+static char opts[PATH_MAX];
+static int diff_errors;
+
+static char *diff_usage[] =
+{
+ "Usage: %s %s [-l] [rcsdiff-options]\n",
+#ifdef CVS_DIFFDATE
+ " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
+#else
+ " [-r rev1 [-r rev2]] [files...] \n",
+#endif
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-D d1\tDiff revision for date against working file.\n",
+ "\t-D d2\tDiff rev1/date1 against date2.\n",
+ "\t-r rev1\tDiff revision for rev1 against working file.\n",
+ "\t-r rev2\tDiff rev1/date1 against rev2.\n",
+ NULL
+};
+
+int
+diff (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char tmp[50];
+ int c, err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (diff_usage);
+
+ /*
+ * Note that we catch all the valid arguments here, so that we can
+ * intercept the -r arguments for doing revision diffs; and -l/-R for a
+ * non-recursive/recursive diff.
+ */
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv,
+ "abcdefhilnpqtuw0123456789BHQRTC:D:F:I:L:V:k:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'h': case 'i': case 'n': case 'p': case 't': case 'u':
+ case 'w': case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': case 'B':
+ case 'H': case 'T': case 'Q':
+ (void) sprintf (tmp, " -%c", (char) c);
+ (void) strcat (opts, tmp);
+ if (c == 'Q')
+ {
+ quiet = 1;
+ really_quiet = 1;
+ c = 'q';
+ }
+ break;
+ case 'C': case 'F': case 'I': case 'L': case 'V':
+#ifndef CVS_DIFFDATE
+ case 'D':
+#endif
+ (void) sprintf (tmp, " -%c%s", (char) c, optarg);
+ (void) strcat (opts, tmp);
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'r':
+ if (diff_rev2 != NULL || diff_date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (diff_rev1 != NULL || diff_date1 != NULL)
+ diff_rev2 = optarg;
+ else
+ diff_rev1 = optarg;
+ break;
+#ifdef CVS_DIFFDATE
+ case 'D':
+ if (diff_rev2 != NULL || diff_date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (diff_rev1 != NULL || diff_date1 != NULL)
+ diff_date2 = Make_Date (optarg);
+ else
+ diff_date1 = Make_Date (optarg);
+ break;
+#endif
+ case '?':
+ default:
+ usage (diff_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* make sure options is non-null */
+ if (!options)
+ options = xstrdup ("");
+
+ /* start the recursion processor */
+ err = start_recursion (diff_fileproc, (int (*) ()) NULL, diff_dirproc,
+ diff_dirleaveproc, argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+
+ /* clean up */
+ free (options);
+ return (err);
+}
+
+/*
+ * Do a file diff
+ */
+/* ARGSUSED */
+static int
+diff_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ int status, err = 2; /* 2 == trouble, like rcsdiff */
+ Vers_TS *vers;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 1, 0, entries, srcfiles);
+
+ if (vers->vn_user == NULL)
+ {
+ error (0, 0, "I know nothing about %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ {
+ error (0, 0, "%s is a new entry, no comparison available", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ error (0, 0, "%s was removed, no comparison available", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else
+ {
+ if (vers->vn_rcs == NULL && vers->srcfile == NULL)
+ {
+ error (0, 0, "cannot find revision control file for %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else
+ {
+ if (vers->ts_user == NULL)
+ {
+ error (0, 0, "cannot find %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ }
+ }
+
+ if (diff_file_nodiff (file, repository, entries, srcfiles, vers))
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ (void) fflush (stdout);
+ if (use_rev2)
+ {
+ run_setup ("%s%s %s %s -r%s -r%s", Rcsbin, RCS_DIFF,
+ opts, *options ? options : vers->options,
+ use_rev1, use_rev2);
+ }
+ else
+ {
+ run_setup ("%s%s %s %s -r%s", Rcsbin, RCS_DIFF, opts,
+ *options ? options : vers->options, use_rev1);
+ }
+ run_arg (vers->srcfile->path);
+
+ switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ RUN_REALLY|RUN_COMBINED)))
+ {
+ case -1: /* fork failed */
+ error (1, errno, "fork failed during rcsdiff of %s",
+ vers->srcfile->path);
+ case 0: /* everything ok */
+ err = 0;
+ break;
+ default: /* other error */
+ err = status;
+ break;
+ }
+
+ (void) fflush (stdout);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+}
+
+/*
+ * Remember the exit status for each file.
+ */
+static void
+diff_mark_errors (err)
+ int err;
+{
+ if (err > diff_errors)
+ diff_errors = err;
+}
+
+/*
+ * Print a warm fuzzy message when we enter a dir
+ */
+/* ARGSUSED */
+static Dtype
+diff_dirproc (dir, pos_repos, update_dir)
+ char *dir;
+ char *pos_repos;
+ char *update_dir;
+{
+ /* XXX - check for dirs we don't want to process??? */
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return (R_PROCESS);
+}
+
+/*
+ * Concoct the proper exit status.
+ */
+/* ARGSUSED */
+static int
+diff_dirleaveproc (dir, err, update_dir)
+ char *dir;
+ int err;
+ char *update_dir;
+{
+ return (diff_errors);
+}
+
+/*
+ * verify that a file is different 0=same 1=different
+ */
+static int
+diff_file_nodiff (file, repository, entries, srcfiles, vers)
+ char *file;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+ Vers_TS *vers;
+{
+ Vers_TS *xvers;
+ char tmp[L_tmpnam+1];
+
+ /* free up any old use_rev* variables and reset 'em */
+ if (use_rev1)
+ free (use_rev1);
+ if (use_rev2)
+ free (use_rev2);
+ use_rev1 = use_rev2 = (char *) NULL;
+
+ if (diff_rev1 || diff_date1)
+ {
+ /* special handling for TAG_HEAD */
+ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
+ use_rev1 = xstrdup (vers->vn_rcs);
+ else
+ {
+ xvers = Version_TS (repository, (char *) NULL, diff_rev1,
+ diff_date1, file, 1, 0, entries, srcfiles);
+ if (xvers->vn_rcs == NULL)
+ {
+ if (diff_rev1)
+ error (0, 0, "tag %s is not in file %s", diff_rev1, file);
+ else
+ error (0, 0, "no revision for date %s in file %s",
+ diff_date1, file);
+ return (1);
+ }
+ use_rev1 = xstrdup (xvers->vn_rcs);
+ freevers_ts (&xvers);
+ }
+ }
+ if (diff_rev2 || diff_date2)
+ {
+ /* special handling for TAG_HEAD */
+ if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
+ use_rev2 = xstrdup (vers->vn_rcs);
+ else
+ {
+ xvers = Version_TS (repository, (char *) NULL, diff_rev2,
+ diff_date2, file, 1, 0, entries, srcfiles);
+ if (xvers->vn_rcs == NULL)
+ {
+ if (diff_rev1)
+ error (0, 0, "tag %s is not in file %s", diff_rev2, file);
+ else
+ error (0, 0, "no revision for date %s in file %s",
+ diff_date2, file);
+ return (1);
+ }
+ use_rev2 = xstrdup (xvers->vn_rcs);
+ freevers_ts (&xvers);
+ }
+
+ /* now, see if we really need to do the diff */
+ return (strcmp (use_rev1, use_rev2) == 0);
+ }
+ if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0)
+ {
+ if (strcmp (vers->ts_rcs, vers->ts_user) == 0 &&
+ (!(*options) || strcmp (options, vers->options) == 0))
+ {
+ return (1);
+ }
+ if (use_rev1 == NULL)
+ use_rev1 = xstrdup (vers->vn_user);
+ }
+
+ /*
+ * with 0 or 1 -r option specified, run a quick diff to see if we
+ * should bother with it at all.
+ */
+ run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO,
+ *options ? options : vers->options, use_rev1);
+ run_arg (vers->srcfile->path);
+ switch (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY))
+ {
+ case 0: /* everything ok */
+ if (xcmp (file, tmp) == 0)
+ {
+ (void) unlink (tmp);
+ return (1);
+ }
+ break;
+ case -1: /* fork failed */
+ (void) unlink (tmp);
+ error (1, errno, "fork failed during checkout of %s",
+ vers->srcfile->path);
+ default:
+ break;
+ }
+ (void) unlink (tmp);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/entries.c b/gnu/usr.bin/cvs/cvs/entries.c
new file mode 100644
index 000000000000..6453e4f5dc0c
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/entries.c
@@ -0,0 +1,488 @@
+/*
+ * 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.3 kit.
+ *
+ * Entries file to Files file
+ *
+ * Creates the file Files containing the names that comprise the project, from
+ * the Entries file.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)entries.c 1.37 92/03/31";
+#endif
+
+#if __STDC__
+static Node *AddEntryNode (List * list, char *name, char *version,
+ char *timestamp, char *options, char *tag,
+ char *date);
+#else
+static Node *AddEntryNode ();
+#endif /* __STDC__ */
+
+static FILE *entfile;
+static char *entfilename; /* for error messages */
+
+/*
+ * Write out the line associated with a node of an entries file
+ */
+static int
+write_ent_proc (node)
+ Node *node;
+{
+ Entnode *p;
+
+ p = (Entnode *) node->data;
+ if (fprintf (entfile, "/%s/%s/%s/%s/", node->key, p->version,
+ p->timestamp, p->options) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ if (p->tag)
+ {
+ if (fprintf (entfile, "T%s\n", p->tag) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ }
+ else if (p->date)
+ {
+ if (fprintf (entfile, "D%s\n", p->date) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ }
+ else if (fprintf (entfile, "\n") == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ return (0);
+}
+
+/*
+ * write out the current entries file given a list, making a backup copy
+ * first of course
+ */
+static void
+write_entries (list)
+ List *list;
+{
+ /* open the new one and walk the list writing entries */
+ entfilename = CVSADM_ENTBAK;
+ entfile = open_file (entfilename, "w+");
+ (void) walklist (list, write_ent_proc);
+ if (fclose (entfile) == EOF)
+ error (1, errno, "error closing %s", entfilename);
+
+ /* now, atomically (on systems that support it) rename it */
+ rename_file (entfilename, CVSADM_ENT);
+}
+
+/*
+ * Removes the argument file from the Entries file if necessary.
+ */
+void
+Scratch_Entry (list, fname)
+ List *list;
+ char *fname;
+{
+ Node *node;
+
+ if (trace)
+ (void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
+
+ /* hashlookup to see if it is there */
+ if ((node = findnode (list, fname)) != NULL)
+ {
+ delnode (node); /* delete the node */
+ if (!noexec)
+ write_entries (list); /* re-write the file */
+ }
+}
+
+/*
+ * Enters the given file name/version/time-stamp into the Entries file,
+ * removing the old entry first, if necessary.
+ */
+void
+Register (list, fname, vn, ts, options, tag, date)
+ List *list;
+ char *fname;
+ char *vn;
+ char *ts;
+ char *options;
+ char *tag;
+ char *date;
+{
+ Node *node;
+
+ if (trace)
+ (void) fprintf (stderr, "-> Register(%s, %s, %s, %s, %s %s)\n",
+ fname, vn, ts, options, tag ? tag : "",
+ date ? date : "");
+ /* was it already there? */
+ if ((node = findnode (list, fname)) != NULL)
+ {
+ /* take it out */
+ delnode (node);
+
+ /* add the new one and re-write the file */
+ (void) AddEntryNode (list, fname, vn, ts, options, tag, date);
+ if (!noexec)
+ write_entries (list);
+ }
+ else
+ {
+ /* add the new one */
+ node = AddEntryNode (list, fname, vn, ts, options, tag, date);
+
+ if (!noexec)
+ {
+ /* append it to the end */
+ entfilename = CVSADM_ENT;
+ entfile = open_file (entfilename, "a");
+ (void) write_ent_proc (node);
+ if (fclose (entfile) == EOF)
+ error (1, errno, "error closing %s", entfilename);
+ }
+ }
+}
+
+/*
+ * Node delete procedure for list-private sticky dir tag/date info
+ */
+static void
+freesdt (p)
+ Node *p;
+{
+ struct stickydirtag *sdtp;
+
+ sdtp = (struct stickydirtag *) p->data;
+ if (sdtp->tag)
+ free (sdtp->tag);
+ if (sdtp->date)
+ free (sdtp->date);
+ if (sdtp->options)
+ free (sdtp->options);
+ free ((char *) sdtp);
+}
+
+/*
+ * Read the entries file into a list, hashing on the file name.
+ */
+List *
+ParseEntries (aflag)
+ int aflag;
+{
+ List *entries;
+ char line[MAXLINELEN];
+ char *cp, *user, *vn, *ts, *options;
+ char *tag_or_date, *tag, *date;
+ char *dirtag, *dirdate;
+ int lineno = 0;
+ FILE *fpin;
+
+ /* get a fresh list... */
+ entries = getlist ();
+
+ /*
+ * Parse the CVS/Tag file, to get any default tag/date settings. Use
+ * list-private storage to tuck them away for Version_TS().
+ */
+ ParseTag (&dirtag, &dirdate);
+ if (aflag || dirtag || dirdate)
+ {
+ struct stickydirtag *sdtp;
+
+ sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
+ bzero ((char *) sdtp, sizeof (*sdtp));
+ sdtp->aflag = aflag;
+ sdtp->tag = xstrdup (dirtag);
+ sdtp->date = xstrdup (dirdate);
+
+ /* feed it into the list-private area */
+ entries->list->data = (char *) sdtp;
+ entries->list->delproc = freesdt;
+ }
+
+ again:
+ fpin = fopen (CVSADM_ENT, "r");
+ if (fpin == NULL)
+ error (0, errno, "cannot open %s for reading", CVSADM_ENT);
+ else
+ {
+ while (fgets (line, sizeof (line), fpin) != NULL)
+ {
+ lineno++;
+ if (line[0] == '/')
+ {
+ user = line + 1;
+ if ((cp = index (user, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ vn = cp;
+ if ((cp = index (vn, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ ts = cp;
+ if ((cp = index (ts, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ options = cp;
+ if ((cp = index (options, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ tag_or_date = cp;
+ if ((cp = index (tag_or_date, '\n')) == NULL)
+ continue;
+ *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;
+ (void) AddEntryNode (entries, user, vn, ts, options, tag, date);
+ }
+ else
+ {
+ /* try conversion only on first line */
+ if (lineno == 1)
+ {
+ (void) fclose (fpin);
+ check_entries ((char *) NULL);
+ goto again;
+ }
+ }
+ }
+ }
+
+ /* clean up and return */
+ if (fpin)
+ (void) fclose (fpin);
+ if (dirtag)
+ free (dirtag);
+ if (dirdate)
+ free (dirdate);
+ return (entries);
+}
+
+/*
+ * Look at the entries file to determine if it is in the old entries format.
+ * If so, convert it to the new format.
+ */
+void
+check_entries (dir)
+ char *dir;
+{
+ FILE *fpin, *fpout;
+ char tmp[MAXLINELEN];
+ char line[MAXLINELEN];
+ char entname[MAXLINELEN];
+ char entbak[MAXLINELEN];
+ char *cp, *user, *rev, *ts, *opt;
+
+ if (dir != NULL)
+ {
+ (void) sprintf (entname, "%s/%s", dir, CVSADM_ENT);
+ (void) sprintf (entbak, "%s/%s", dir, CVSADM_ENTBAK);
+ }
+ else
+ {
+ (void) strcpy (entname, CVSADM_ENT);
+ (void) strcpy (entbak, CVSADM_ENTBAK);
+ }
+
+ fpin = open_file (entname, "r");
+ if (fgets (line, sizeof (line), fpin) == NULL)
+ {
+ (void) fclose (fpin);
+ return;
+ }
+ (void) fclose (fpin);
+ if (line[0] != '/')
+ {
+ rename_file (entname, entbak);
+ fpin = open_file (entbak, "r");
+ fpout = open_file (entname, "w+");
+ while (fgets (line, sizeof (line), fpin) != NULL)
+ {
+ if (line[0] == '/')
+ {
+ if (fputs (line, fpout) == EOF)
+ error (1, errno, "cannot write %s", CVSADM_ENT);
+ continue;
+ }
+ rev = line;
+ if ((ts = index (line, '|')) == NULL)
+ continue;
+ *ts++ = '\0';
+ if ((user = rindex (ts, ' ')) == NULL)
+ continue;
+ *user++ = '\0';
+ if ((cp = index (user, '|')) == NULL)
+ continue;
+ *cp = '\0';
+ opt = "";
+#ifdef HAVE_RCS5
+#ifdef HAD_RCS4
+ opt = "-V4";
+#endif
+#endif
+ if (fprintf (fpout, "/%s/%s/%s/%s/\n", user, rev, ts, opt) == EOF)
+ error (1, errno, "cannot write %s", CVSADM_ENT);
+ }
+ (void) fclose (fpin);
+ if (fclose (fpout) == EOF)
+ error (1, errno, "cannot close %s", entname);
+
+ /* clean up any old Files or Mod files */
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_FILE);
+ else
+ (void) strcpy (tmp, CVSADM_FILE);
+ if (isfile (tmp))
+ (void) unlink (tmp);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_MOD);
+ else
+ (void) strcpy (tmp, CVSADM_MOD);
+ if (isfile (tmp))
+ (void) unlink (tmp);
+ }
+}
+
+/*
+ * Free up the memory associated with the data section of an ENTRIES type
+ * node
+ */
+static void
+Entries_delproc (node)
+ Node *node;
+{
+ Entnode *p;
+
+ p = (Entnode *) node->data;
+ free (p->version);
+ free (p->timestamp);
+ free (p->options);
+ if (p->tag)
+ free (p->tag);
+ if (p->date)
+ free (p->date);
+ free ((char *) p);
+}
+
+/*
+ * Get an Entries file list node, initialize it, and add it to the specified
+ * list
+ */
+static Node *
+AddEntryNode (list, name, version, timestamp, options, tag, date)
+ List *list;
+ char *name;
+ char *version;
+ char *timestamp;
+ char *options;
+ char *tag;
+ char *date;
+{
+ Node *p;
+ Entnode *entdata;
+
+ /* get a node and fill in the regular stuff */
+ p = getnode ();
+ p->type = ENTRIES;
+ p->delproc = Entries_delproc;
+
+ /* this one gets a key of the name for hashing */
+ p->key = xstrdup (name);
+
+ /* malloc the data parts and fill them in */
+ p->data = xmalloc (sizeof (Entnode));
+ entdata = (Entnode *) p->data;
+ entdata->version = xstrdup (version);
+ entdata->timestamp = xstrdup (timestamp);
+ entdata->options = xstrdup (options);
+ if (entdata->options == NULL)
+ entdata->options = xstrdup ("");/* must be non-NULL */
+ entdata->tag = xstrdup (tag);
+ entdata->date = xstrdup (date);
+
+ /* put the node into the list */
+ if (addnode (list, p) != 0)
+ error (0, 0, "Duplicate filename in entries file (%s) -- ignored",
+ name);
+
+ return (p);
+}
+
+/*
+ * Write out/Clear the CVS/Tag file.
+ */
+void
+WriteTag (dir, tag, date)
+ char *dir;
+ char *tag;
+ char *date;
+{
+ FILE *fout;
+ char tmp[PATH_MAX];
+
+ if (noexec)
+ return;
+
+ if (dir == NULL)
+ (void) strcpy (tmp, CVSADM_TAG);
+ else
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
+
+ if (tag || date)
+ {
+ fout = open_file (tmp, "w+");
+ if (tag)
+ {
+ if (fprintf (fout, "T%s\n", tag) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ }
+ else
+ {
+ if (fprintf (fout, "D%s\n", date) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ }
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+ }
+ else
+ (void) unlink_file (tmp);
+}
+
+/*
+ * Parse the CVS/Tag file for the current directory.
+ */
+void
+ParseTag (tagp, datep)
+ char **tagp;
+ char **datep;
+{
+ FILE *fp;
+ char line[MAXLINELEN];
+ char *cp;
+
+ if (tagp)
+ *tagp = (char *) NULL;
+ if (datep)
+ *datep = (char *) NULL;
+ fp = fopen (CVSADM_TAG, "r");
+ if (fp)
+ {
+ if (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0';
+ if (*line == 'T' && tagp)
+ *tagp = xstrdup (line + 1);
+ else if (*line == 'D' && datep)
+ *datep = xstrdup (line + 1);
+ }
+ (void) fclose (fp);
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/find_names.c b/gnu/usr.bin/cvs/cvs/find_names.c
new file mode 100644
index 000000000000..187bd2366ceb
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/find_names.c
@@ -0,0 +1,272 @@
+/*
+ * 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.3 kit.
+ *
+ * Find Names
+ *
+ * Finds all the pertinent file names, both from the administration and from the
+ * repository
+ *
+ * Find Dirs
+ *
+ * Finds all pertinent sub-directories of the checked out instantiation and the
+ * repository (and optionally the attic)
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)find_names.c 1.38 92/04/10";
+#endif
+
+#if __STDC__
+static int find_dirs (char *dir, List * list, int checkadm);
+static int find_rcs (char *dir, List * list);
+#else
+static int find_rcs ();
+static int find_dirs ();
+#endif /* __STDC__ */
+
+static List *filelist;
+
+/*
+ * add the key from entry on entries list to the files list
+ */
+static int
+add_entries_proc (node)
+ Node *node;
+{
+ Node *fnode;
+
+ fnode = getnode ();
+ fnode->type = FILES;
+ fnode->key = xstrdup (node->key);
+ if (addnode (filelist, fnode) != 0)
+ freenode (fnode);
+ return (0);
+}
+
+/*
+ * compare two files list node (for sort)
+ */
+static int
+fsortcmp (p, q)
+ Node *p, *q;
+{
+ return (strcmp (p->key, q->key));
+}
+
+List *
+Find_Names (repository, which, aflag, optentries)
+ char *repository;
+ int which;
+ int aflag;
+ List **optentries;
+{
+ List *entries;
+ List *files;
+ char dir[PATH_MAX];
+
+ /* make a list for the files */
+ files = filelist = getlist ();
+
+ /* look at entries (if necessary) */
+ if (which & W_LOCAL)
+ {
+ /* parse the entries file (if it exists) */
+ entries = ParseEntries (aflag);
+
+ if (entries != NULL)
+ {
+ /* walk the entries file adding elements to the files list */
+ (void) walklist (entries, add_entries_proc);
+
+ /* if our caller wanted the entries list, return it; else free it */
+ if (optentries != NULL)
+ *optentries = entries;
+ else
+ dellist (&entries);
+ }
+ }
+
+ if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT))
+ {
+ /* search the repository */
+ if (find_rcs (repository, files) != 0)
+ error (1, errno, "cannot open directory %s", repository);
+
+ /* search the attic too */
+ if (which & W_ATTIC)
+ {
+ (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
+ (void) find_rcs (dir, files);
+ }
+ }
+
+ /* sort the list into alphabetical order and return it */
+ sortlist (files, fsortcmp);
+ return (files);
+}
+
+/*
+ * create a list of directories to traverse from the current directory
+ */
+List *
+Find_Dirs (repository, which)
+ char *repository;
+ int which;
+{
+ List *dirlist;
+
+ /* make a list for the directories */
+ dirlist = getlist ();
+
+ /* find the local ones */
+ if (which & W_LOCAL)
+ {
+ /* look only for CVS controlled sub-directories */
+ if (find_dirs (".", dirlist, 1) != 0)
+ error (1, errno, "cannot open current directory");
+ }
+
+ /* look for sub-dirs in the repository */
+ if ((which & W_REPOS) && repository)
+ {
+ /* search the repository */
+ if (find_dirs (repository, dirlist, 0) != 0)
+ error (1, errno, "cannot open directory %s", repository);
+
+#ifdef ATTIC_DIR_SUPPORT /* XXX - FIXME */
+ /* search the attic too */
+ if (which & W_ATTIC)
+ {
+ char dir[PATH_MAX];
+
+ (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
+ (void) find_dirs (dir, dirlist, 0);
+ }
+#endif
+ }
+
+ /* sort the list into alphabetical order and return it */
+ sortlist (dirlist, fsortcmp);
+ return (dirlist);
+}
+
+/*
+ * Finds all the ,v files in the argument directory, and adds them to the
+ * files list. Returns 0 for success and non-zero if the argument directory
+ * cannot be opened.
+ */
+static int
+find_rcs (dir, list)
+ char *dir;
+ List *list;
+{
+ Node *p;
+ CONST char *regex_err;
+ char line[50];
+ struct direct *dp;
+ DIR *dirp;
+
+ /* set up to read the dir */
+ if ((dirp = opendir (dir)) == NULL)
+ return (1);
+
+ /* set up a regular expression to find the ,v files */
+ (void) sprintf (line, ".*%s$", RCSEXT);
+ if ((regex_err = re_comp (line)) != NULL)
+ error (1, 0, "%s", regex_err);
+
+ /* read the dir, grabbing the ,v files */
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (re_exec (dp->d_name))
+ {
+ char *comma;
+
+ comma = rindex (dp->d_name, ','); /* strip the ,v */
+ *comma = '\0';
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (dp->d_name);
+ if (addnode (list, p) != 0)
+ freenode (p);
+ }
+ }
+ (void) closedir (dirp);
+ return (0);
+}
+
+/*
+ * Finds all the subdirectories of the argument dir and adds them to the
+ * specified list. Sub-directories without a CVS administration directory
+ * are optionally ignored Returns 0 for success or 1 on error.
+ */
+static int
+find_dirs (dir, list, checkadm)
+ char *dir;
+ List *list;
+ int checkadm;
+{
+ Node *p;
+ CONST char *regex_err;
+ char tmp[PATH_MAX];
+ char admdir[PATH_MAX];
+ struct direct *dp;
+ DIR *dirp;
+
+ /* build a regex to blow off ,v files */
+ (void) sprintf (tmp, ".*%s$", RCSEXT);
+ if ((regex_err = re_comp (tmp)) != NULL)
+ error (1, 0, "%s", regex_err);
+
+ /* set up to read the dir */
+ if ((dirp = opendir (dir)) == NULL)
+ return (1);
+
+ /* read the dir, grabbing sub-dirs */
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 ||
+ strcmp (dp->d_name, "..") == 0 ||
+ strcmp (dp->d_name, CVSATTIC) == 0 ||
+ strcmp (dp->d_name, CVSLCK) == 0 ||
+ re_exec (dp->d_name)) /* don't bother stating ,v files */
+ continue;
+
+ (void) sprintf (tmp, "%s/%s", dir, dp->d_name);
+ if (isdir (tmp))
+ {
+ /* check for administration directories (if needed) */
+ if (checkadm)
+ {
+ /* blow off symbolic links to dirs in local dir */
+ if (islink (tmp))
+ continue;
+
+ /* check for new style */
+ (void) sprintf (admdir, "%s/%s", tmp, CVSADM);
+ if (!isdir (admdir))
+ {
+ /* and old style */
+ (void) sprintf (admdir, "%s/%s", tmp, OCVSADM);
+ if (!isdir (admdir))
+ continue;
+ }
+ }
+
+ /* put it in the list */
+ p = getnode ();
+ p->type = DIRS;
+ p->key = xstrdup (dp->d_name);
+ if (addnode (list, p) != 0)
+ freenode (p);
+ }
+ }
+ (void) closedir (dirp);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/history.c b/gnu/usr.bin/cvs/cvs/history.c
new file mode 100644
index 000000000000..9675c4c87deb
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/history.c
@@ -0,0 +1,1373 @@
+/*
+ *
+ * You may distribute under the terms of the GNU General Public License
+ * as specified in the README file that comes with the CVS 1.0 kit.
+ *
+ * **************** History of Users and Module ****************
+ *
+ * LOGGING: Append record to "${CVSROOT}/CVSROOTADM/CVSROOTADM_HISTORY".
+ *
+ * On For each Tag, Add, Checkout, Commit, Update or Release command,
+ * one line of text is written to a History log.
+ *
+ * X date | user | CurDir | special | rev(s) | argument '\n'
+ *
+ * where: [The spaces in the example line above are not in the history file.]
+ *
+ * X is a single character showing the type of event:
+ * T "Tag" cmd.
+ * O "Checkout" cmd.
+ * F "Release" cmd.
+ * W "Update" cmd - No User file, Remove from Entries file.
+ * U "Update" cmd - File was checked out over User file.
+ * G "Update" cmd - File was merged successfully.
+ * C "Update" cmd - File was merged and shows overlaps.
+ * M "Commit" cmd - "Modified" file.
+ * A "Commit" cmd - "Added" file.
+ * R "Commit" cmd - "Removed" file.
+ *
+ * date is a fixed length 8-char hex representation of a Unix time_t.
+ * [Starting here, variable fields are delimited by '|' chars.]
+ *
+ * user is the username of the person who typed the command.
+ *
+ * CurDir The directory where the action occurred. This should be the
+ * absolute path of the directory which is at the same level as
+ * the "Repository" field (for W,U,G,C & M,A,R).
+ *
+ * Repository For record types [W,U,G,C,M,A,R] this field holds the
+ * repository read from the administrative data where the
+ * command was typed.
+ * T "A" --> New Tag, "D" --> Delete Tag
+ * Otherwise it is the Tag or Date to modify.
+ * O,F A "" (null field)
+ *
+ * rev(s) Revision number or tag.
+ * T The Tag to apply.
+ * O The Tag or Date, if specified, else "" (null field).
+ * F "" (null field)
+ * W The Tag or Date, if specified, else "" (null field).
+ * U The Revision checked out over the User file.
+ * G,C The Revision(s) involved in merge.
+ * M,A,R RCS Revision affected.
+ *
+ * argument The module (for [TOUF]) or file (for [WUGCMAR]) affected.
+ *
+ *
+ *** Report categories: "User" and "Since" modifiers apply to all reports.
+ * [For "sort" ordering see the "sort_order" routine.]
+ *
+ * Extract list of record types
+ *
+ * -e, -x [TOFWUGCMAR]
+ *
+ * Extracted records are simply printed, No analysis is performed.
+ * All "field" modifiers apply. -e chooses all types.
+ *
+ * Checked 'O'ut modules
+ *
+ * -o, -w
+ * Checked out modules. 'F' and 'O' records are examined and if
+ * the last record for a repository/file is an 'O', a line is
+ * printed. "-w" forces the "working dir" to be used in the
+ * comparison instead of the repository.
+ *
+ * Committed (Modified) files
+ *
+ * -c, -l, -w
+ * All 'M'odified, 'A'dded and 'R'emoved records are examined.
+ * "Field" modifiers apply. -l forces a sort by file within user
+ * and shows only the last modifier. -w works as in Checkout.
+ *
+ * Warning: Be careful with what you infer from the output of
+ * "cvs hi -c -l". It means the last time *you*
+ * changed the file, not the list of files for which
+ * you were the last changer!!!
+ *
+ * Module history for named modules.
+ * -m module, -l
+ *
+ * This is special. If one or more modules are specified, the
+ * module names are remembered and the files making up the
+ * modules are remembered. Only records matching exactly those
+ * files and repositories are shown. Sorting by "module", then
+ * filename, is implied. If -l ("last modified") is specified,
+ * then "update" records (types WUCG), tag and release records
+ * are ignored and the last (by date) "modified" record.
+ *
+ * TAG history
+ *
+ * -T All Tag records are displayed.
+ *
+ *** Modifiers.
+ *
+ * Since ... [All records contain a timestamp, so any report
+ * category can be limited by date.]
+ *
+ * -D date - The "date" is parsed into a Unix "time_t" and
+ * records with an earlier time stamp are ignored.
+ * -r rev/tag - A "rev" begins with a digit. A "tag" does not. If
+ * you use this option, every file is searched for the
+ * indicated rev/tag.
+ * -t tag - The "tag" is searched for in the history file and no
+ * record is displayed before the tag is found. An
+ * error is printed if the tag is never found.
+ * -b string - Records are printed only back to the last reference
+ * to the string in the "module", "file" or
+ * "repository" fields.
+ *
+ * Field Selections [Simple comparisons on existing fields. All field
+ * selections are repeatable.]
+ *
+ * -a - All users.
+ * -u user - If no user is given and '-a' is not given, only
+ * records for the user typing the command are shown.
+ * ==> If -a or -u is not specified, just use "self".
+ *
+ * -f filematch - Only records in which the "file" field contains the
+ * string "filematch" are considered.
+ *
+ * -p repository - Only records in which the "repository" string is a
+ * prefix of the "repos" field are considered.
+ *
+ * -m modulename - Only records which contain "modulename" in the
+ * "module" field are considered.
+ *
+ *
+ * EXAMPLES: ("cvs history", "cvs his" or "cvs hi")
+ *
+ *** Checked out files for username. (default self, e.g. "dgg")
+ * cvs hi [equivalent to: "cvs hi -o -u dgg"]
+ * cvs hi -u user [equivalent to: "cvs hi -o -u user"]
+ * cvs hi -o [equivalent to: "cvs hi -o -u dgg"]
+ *
+ *** Committed (modified) files from the beginning of the file.
+ * cvs hi -c [-u user]
+ *
+ *** Committed (modified) files since Midnight, January 1, 1990:
+ * cvs hi -c -D 'Jan 1 1990' [-u user]
+ *
+ *** Committed (modified) files since tag "TAG" was stored in the history file:
+ * cvs hi -c -t TAG [-u user]
+ *
+ *** Committed (modified) files since tag "TAG" was placed on the files:
+ * cvs hi -c -r TAG [-u user]
+ *
+ *** Who last committed file/repository X?
+ * cvs hi -c -l -[fp] X
+ *
+ *** Modified files since tag/date/file/repos?
+ * cvs hi -c {-r TAG | -D Date | -b string}
+ *
+ *** Tag history
+ * cvs hi -T
+ *
+ *** History of file/repository/module X.
+ * cvs hi -[fpn] X
+ *
+ *** History of user "user".
+ * cvs hi -e -u user
+ *
+ *** Dump (eXtract) specified record types
+ * cvs hi -x [TOFWUGCMAR]
+ *
+ *
+ * FUTURE: J[Join], I[Import] (Not currently implemented.)
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)history.c 1.31 92/04/10";
+#endif
+
+static struct hrec
+{
+ char *type; /* Type of record (In history record) */
+ char *user; /* Username (In history record) */
+ char *dir; /* "Compressed" Working dir (In history record) */
+ char *repos; /* (Tag is special.) Repository (In history record) */
+ char *rev; /* Revision affected (In history record) */
+ char *file; /* Filename (In history record) */
+ char *end; /* Ptr into repository to copy at end of workdir */
+ char *mod; /* The module within which the file is contained */
+ time_t date; /* Calculated from date stored in record */
+ int idx; /* Index of record, for "stable" sort. */
+} *hrec_head;
+
+
+#if __STDC__
+static char *fill_hrec (char *line, struct hrec * hr);
+static int accept_hrec (struct hrec * hr, struct hrec * lr);
+static int select_hrec (struct hrec * hr);
+static int sort_order (CONST PTR l, CONST PTR r);
+static int within (char *find, char *string);
+static time_t date_and_time (char *date_str);
+static void expand_modules (void);
+static void read_hrecs (char *fname);
+static void report_hrecs (void);
+static void save_file (char *dir, char *name, char *module);
+static void save_module (char *module);
+static void save_user (char *name);
+#else
+static int sort_order ();
+static time_t date_and_time ();
+static void save_user ();
+static void save_file ();
+static void save_module ();
+static void expand_modules ();
+static char *fill_hrec ();
+static void read_hrecs ();
+static int within ();
+static int select_hrec ();
+static void report_hrecs ();
+static int accept_hrec ();
+#endif /* __STDC__ */
+
+#define ALL_REC_TYPES "TOFWUCGMAR"
+#define USER_INCREMENT 2
+#define FILE_INCREMENT 128
+#define MODULE_INCREMENT 5
+#define HREC_INCREMENT 128
+
+static short report_count;
+
+static short extract;
+static short v_checkout;
+static short modified;
+static short tag_report;
+static short module_report;
+static short working;
+static short last_entry;
+static short all_users;
+
+static short user_sort;
+static short repos_sort;
+static short file_sort;
+static short module_sort;
+
+static time_t since_date;
+static char since_rev[20]; /* Maxrev ~= 99.99.99.999 */
+static char since_tag[64];
+static struct hrec *last_since_tag;
+static char backto[128];
+static struct hrec *last_backto;
+static char rec_types[20];
+
+static int hrec_count;
+static int hrec_max;
+
+static char **user_list; /* Ptr to array of ptrs to user names */
+static int user_max; /* Number of elements allocated */
+static int user_count; /* Number of elements used */
+
+static struct file_list_str
+{
+ char *l_file;
+ char *l_module;
+} *file_list; /* Ptr to array file name structs */
+static int file_max; /* Number of elements allocated */
+static int file_count; /* Number of elements used */
+
+static char **mod_list; /* Ptr to array of ptrs to module names */
+static int mod_max; /* Number of elements allocated */
+static int mod_count; /* Number of elements used */
+
+static int histsize;
+static char *histdata;
+static char *histfile; /* Ptr to the history file name */
+
+static char *history_usg[] =
+{
+ "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n",
+ " Reports:\n",
+ " -T Produce report on all TAGs\n",
+ " -c Committed (Modified) files\n",
+ " -o Checked out modules\n",
+ " -m <module> Look for specified module (repeatable)\n",
+ " -x [TOFWUCGMAR] Extract by record type\n",
+ " Flags:\n",
+ " -a All users (Default is self)\n",
+ " -e Everything (same as -x, but all record types)\n",
+ " -l Last modified (committed or modified report)\n",
+ " -w Working directory must match\n",
+ " Options:\n",
+ " -D <date> Since date (Many formats)\n",
+ " -b <str> Back to record with str in module/file/repos field\n",
+ " -f <file> Specified file (same as command line) (repeatable)\n",
+ " -n <modulename> In module (repeatable)\n",
+ " -p <repos> In repository (repeatable)\n",
+ " -r <rev/tag> Since rev or tag (looks inside RCS files!)\n",
+ " -t <tag> Since tag record placed in history file (by anyone).\n",
+ " -u <user> For user name (repeatable)\n",
+ NULL};
+
+/* Sort routine for qsort:
+ - If a user is selected at all, sort it first. User-within-file is useless.
+ - If a module was selected explicitly, sort next on module.
+ - Then sort by file. "File" is "repository/file" unless "working" is set,
+ then it is "workdir/file". (Revision order should always track date.)
+ - Always sort timestamp last.
+*/
+static int
+sort_order (l, r)
+ CONST PTR l;
+ CONST PTR r;
+{
+ int i;
+ CONST struct hrec *left = (CONST struct hrec *) l;
+ CONST struct hrec *right = (CONST struct hrec *) r;
+
+ if (user_sort) /* If Sort by username, compare users */
+ {
+ if ((i = strcmp (left->user, right->user)) != 0)
+ return (i);
+ }
+ if (module_sort) /* If sort by modules, compare module names */
+ {
+ if (left->mod && right->mod)
+ if ((i = strcmp (left->mod, right->mod)) != 0)
+ return (i);
+ }
+ if (repos_sort) /* If sort by repository, compare them. */
+ {
+ if ((i = strcmp (left->repos, right->repos)) != 0)
+ return (i);
+ }
+ if (file_sort) /* If sort by filename, compare files, NOT dirs. */
+ {
+ if ((i = strcmp (left->file, right->file)) != 0)
+ return (i);
+
+ if (working)
+ {
+ if ((i = strcmp (left->dir, right->dir)) != 0)
+ return (i);
+
+ if ((i = strcmp (left->end, right->end)) != 0)
+ return (i);
+ }
+ }
+
+ /*
+ * By default, sort by date, time
+ * XXX: This fails after 2030 when date slides into sign bit
+ */
+ if ((i = ((long) (left->date) - (long) (right->date))) != 0)
+ return (i);
+
+ /* For matching dates, keep the sort stable by using record index */
+ return (left->idx - right->idx);
+}
+
+static time_t
+date_and_time (date_str)
+ char *date_str;
+{
+ time_t t;
+
+ t = get_date (date_str, (struct timeb *) NULL);
+ if (t == (time_t) - 1)
+ error (1, 0, "Can't parse date/time: %s", date_str);
+ return (t);
+}
+
+int
+history (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, c;
+ char fname[PATH_MAX];
+
+ if (argc == -1)
+ usage (history_usg);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "Tacelow?D:b:f:m:n:p:r:t:u:x:X:")) != -1)
+ {
+ switch (c)
+ {
+ case 'T': /* Tag list */
+ report_count++;
+ tag_report++;
+ break;
+ case 'a': /* For all usernames */
+ all_users++;
+ break;
+ case 'c':
+ report_count++;
+ modified = 1;
+ break;
+ case 'e':
+ report_count++;
+ extract++;
+ (void) strcpy (rec_types, ALL_REC_TYPES);
+ break;
+ case 'l': /* Find Last file record */
+ last_entry = 1;
+ break;
+ case 'o':
+ report_count++;
+ v_checkout = 1;
+ break;
+ case 'w': /* Match Working Dir (CurDir) fields */
+ working = 1;
+ break;
+ case 'X': /* Undocumented debugging flag */
+ histfile = optarg;
+ break;
+ case 'D': /* Since specified date */
+ if (*since_rev || *since_tag || *backto)
+ {
+ error (0, 0, "date overriding rev/tag/backto");
+ *since_rev = *since_tag = *backto = '\0';
+ }
+ since_date = date_and_time (optarg);
+ break;
+ case 'b': /* Since specified file/Repos */
+ if (since_date || *since_rev || *since_tag)
+ {
+ error (0, 0, "backto overriding date/rev/tag");
+ *since_rev = *since_tag = '\0';
+ since_date = 0;
+ }
+ if (strlen (optarg) >= sizeof (backto))
+ {
+ error (0, 0, "backto truncated to %d bytes",
+ sizeof (backto) - 1);
+ optarg[sizeof (backto) - 1] = '\0';
+ }
+ (void) strcpy (backto, optarg);
+ break;
+ case 'f': /* For specified file */
+ save_file ("", optarg, (char *) NULL);
+ break;
+ case 'm': /* Full module report */
+ report_count++;
+ module_report++;
+ case 'n': /* Look for specified module */
+ save_module (optarg);
+ break;
+ case 'p': /* For specified directory */
+ save_file (optarg, "", (char *) NULL);
+ break;
+ case 'r': /* Since specified Tag/Rev */
+ if (since_date || *since_tag || *backto)
+ {
+ error (0, 0, "rev overriding date/tag/backto");
+ *since_tag = *backto = '\0';
+ since_date = 0;
+ }
+ (void) strcpy (since_rev, optarg);
+ break;
+ case 't': /* Since specified Tag/Rev */
+ if (since_date || *since_rev || *backto)
+ {
+ error (0, 0, "tag overriding date/marker/file/repos");
+ *since_rev = *backto = '\0';
+ since_date = 0;
+ }
+ (void) strcpy (since_tag, optarg); /* tag */
+ break;
+ case 'u': /* For specified username */
+ save_user (optarg);
+ break;
+ case 'x':
+ report_count++;
+ extract++;
+ {
+ char *cp;
+
+ for (cp = optarg; *cp; cp++)
+ if (!index (ALL_REC_TYPES, *cp))
+ error (1, 0, "%c is not a valid report type", cp);
+ }
+ (void) strcpy (rec_types, optarg);
+ break;
+ case '?':
+ default:
+ usage (history_usg);
+ break;
+ }
+ }
+ c = optind; /* Save the handled option count */
+
+ /* ================ Now analyze the arguments a bit */
+ if (!report_count)
+ v_checkout++;
+ else if (report_count > 1)
+ error (1, 0, "Only one report type allowed from: \"-Tcomx\".");
+
+ if (all_users)
+ save_user ("");
+
+ if (mod_list)
+ expand_modules ();
+
+ if (tag_report)
+ {
+ if (!index (rec_types, 'T'))
+ (void) strcat (rec_types, "T");
+ }
+ else if (extract)
+ {
+ if (user_list)
+ user_sort++;
+ }
+ else if (modified)
+ {
+ (void) strcpy (rec_types, "MAR");
+ /*
+ * If the user has not specified a date oriented flag ("Since"), sort
+ * by Repository/file before date. Default is "just" date.
+ */
+ if (!since_date && !*since_rev && !*since_tag && !*backto)
+ {
+ repos_sort++;
+ file_sort++;
+ /*
+ * If we are not looking for last_modified and the user specified
+ * one or more users to look at, sort by user before filename.
+ */
+ if (!last_entry && user_list)
+ user_sort++;
+ }
+ }
+ else if (module_report)
+ {
+ (void) strcpy (rec_types, last_entry ? "OMAR" : ALL_REC_TYPES);
+ module_sort++;
+ repos_sort++;
+ file_sort++;
+ working = 0; /* User's workdir doesn't count here */
+ }
+ else
+ /* Must be "checkout" or default */
+ {
+ (void) strcpy (rec_types, "OF");
+ /* See comments in "modified" above */
+ if (!last_entry && user_list)
+ user_sort++;
+ if (!since_date && !*since_rev && !*since_tag && !*backto)
+ file_sort++;
+ }
+
+ /* If no users were specified, use self (-a saves a universal ("") user) */
+ if (!user_list)
+ save_user (getcaller ());
+
+ /* If we're looking back to a Tag value, must consider "Tag" records */
+ if (*since_tag && !index (rec_types, 'T'))
+ (void) strcat (rec_types, "T");
+
+ argc -= c;
+ argv += c;
+ for (i = 0; i < argc; i++)
+ save_file ("", argv[i], (char *) NULL);
+
+ if (histfile)
+ (void) strcpy (fname, histfile);
+ else
+ (void) sprintf (fname, "%s/%s/%s", CVSroot,
+ CVSROOTADM, CVSROOTADM_HISTORY);
+
+ read_hrecs (fname);
+ qsort ((PTR) hrec_head, hrec_count, sizeof (struct hrec), sort_order);
+ report_hrecs ();
+
+ return (0);
+}
+
+void
+history_write (type, update_dir, revs, name, repository)
+ int type;
+ char *update_dir;
+ char *revs;
+ char *name;
+ char *repository;
+{
+ char fname[PATH_MAX], workdir[PATH_MAX], homedir[PATH_MAX];
+ static char username[20]; /* !!! Should be global */
+ FILE *fp;
+ char *slash = "", *cp, *cp2, *repos;
+ int i;
+ static char *tilde = "";
+ static char *PrCurDir = NULL;
+
+ if (logoff) /* History is turned off by cmd line switch */
+ return;
+ (void) sprintf (fname, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_HISTORY);
+
+ /* turn off history logging if the history file does not exist */
+ if (!isfile (fname))
+ {
+ logoff = 1;
+ return;
+ }
+
+ if (!(fp = Fopen (fname, "a"))) /* Some directory not there! */
+ return;
+
+ repos = Short_Repository (repository);
+
+ if (!PrCurDir)
+ {
+ struct passwd *pw;
+
+ (void) strcpy (username, getcaller ());
+ PrCurDir = CurDir;
+ if (!(pw = (struct passwd *) getpwnam (username)))
+ error (0, 0, "cannot find own username");
+ else
+ {
+ /* Assumes neither CurDir nor pw->pw_dir ends in '/' */
+ i = strlen (pw->pw_dir);
+ if (!strncmp (CurDir, pw->pw_dir, i))
+ {
+ PrCurDir += i; /* Point to '/' separator */
+ tilde = "~";
+ }
+ else
+ {
+ /* Try harder to find a "homedir" */
+ if (!getwd (workdir))
+ error (1, errno, "can't getwd in history");
+ if (chdir (pw->pw_dir) < 0)
+ error (1, errno, "can't chdir(%s)", pw->pw_dir);
+ if (!getwd (homedir))
+ error (1, errno, "can't getwd in:", pw->pw_dir);
+ (void) chdir (workdir);
+
+ i = strlen (homedir);
+ if (!strncmp (CurDir, homedir, i))
+ {
+ PrCurDir += i; /* Point to '/' separator */
+ tilde = "~";
+ }
+ }
+ }
+ }
+
+ if (type == 'T')
+ {
+ repos = update_dir;
+ update_dir = "";
+ }
+ else if (update_dir && *update_dir)
+ slash = "/";
+ else
+ update_dir = "";
+
+ (void) sprintf (workdir, "%s%s%s%s", tilde, PrCurDir, slash, update_dir);
+
+ /*
+ * "workdir" is the directory where the file "name" is. ("^~" == $HOME)
+ * "repos" is the Repository, relative to $CVSROOT where the RCS file is.
+ *
+ * "$workdir/$name" is the working file name.
+ * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository.
+ *
+ * First, note that the history format was intended to save space, not
+ * to be human readable.
+ *
+ * The working file directory ("workdir") and the Repository ("repos")
+ * usually end with the same one or more directory elements. To avoid
+ * duplication (and save space), the "workdir" field ends with
+ * an integer offset into the "repos" field. This offset indicates the
+ * beginning of the "tail" of "repos", after which all characters are
+ * duplicates.
+ *
+ * In other words, if the "workdir" field has a '*' (a very stupid thing
+ * to put in a filename) in it, then every thing following the last '*'
+ * is a hex offset into "repos" of the first character from "repos" to
+ * append to "workdir" to finish the pathname.
+ *
+ * It might be easier to look at an example:
+ *
+ * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
+ *
+ * Indicates that the workdir is really "~/work/cvs/examples", saving
+ * 10 characters, where "~/work*d" would save 6 characters and mean that
+ * the workdir is really "~/work/examples". It will mean more on
+ * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term
+ *
+ * "workdir" is always an absolute pathname (~/xxx is an absolute path)
+ * "repos" is always a relative pathname. So we can assume that we will
+ * never run into the top of "workdir" -- there will always be a '/' or
+ * a '~' at the head of "workdir" that is not matched by anything in
+ * "repos". On the other hand, we *can* run off the top of "repos".
+ *
+ * Only "compress" if we save characters.
+ */
+
+ if (!repos)
+ repos = "";
+
+ cp = workdir + strlen (workdir) - 1;
+ cp2 = repos + strlen (repos) - 1;
+ for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--)
+ i++;
+
+ if (i > 2)
+ {
+ i = strlen (repos) - i;
+ (void) sprintf ((cp + 1), "*%x", i);
+ }
+
+ if (fprintf (fp, "%c%08x|%s|%s|%s|%s|%s\n", type, time ((time_t *) NULL),
+ username, workdir, repos, revs ? revs : "", name) == EOF)
+ error (1, errno, "cannot write to history file: %s", fname);
+ (void) fclose (fp);
+}
+
+/*
+ * save_user() adds a user name to the user list to select. Zero-length
+ * username ("") matches any user.
+ */
+static void
+save_user (name)
+ char *name;
+{
+ if (user_count == user_max)
+ {
+ user_max += USER_INCREMENT;
+ user_list = (char **) xrealloc ((char *) user_list,
+ (int) user_max * sizeof (char *));
+ }
+ user_list[user_count++] = xstrdup (name);
+}
+
+/*
+ * save_file() adds file name and associated module to the file list to select.
+ *
+ * If "dir" is null, store a file name as is.
+ * If "name" is null, store a directory name with a '*' on the front.
+ * Else, store concatenated "dir/name".
+ *
+ * Later, in the "select" stage:
+ * - if it starts with '*', it is prefix-matched against the repository.
+ * - if it has a '/' in it, it is matched against the repository/file.
+ * - else it is matched against the file name.
+ */
+static void
+save_file (dir, name, module)
+ char *dir;
+ char *name;
+ char *module;
+{
+ char *cp;
+ struct file_list_str *fl;
+
+ if (file_count == file_max)
+ {
+ file_max += FILE_INCREMENT;
+ file_list = (struct file_list_str *) xrealloc ((char *) file_list,
+ file_max * sizeof (*fl));
+ }
+ fl = &file_list[file_count++];
+ fl->l_file = cp = xmalloc (strlen (dir) + strlen (name) + 2);
+ fl->l_module = module;
+
+ if (dir && *dir)
+ {
+ if (name && *name)
+ {
+ (void) strcpy (cp, dir);
+ (void) strcat (cp, "/");
+ (void) strcat (cp, name);
+ }
+ else
+ {
+ *cp++ = '*';
+ (void) strcpy (cp, dir);
+ }
+ }
+ else
+ {
+ if (name && *name)
+ {
+ (void) strcpy (cp, name);
+ }
+ else
+ {
+ error (0, 0, "save_file: null dir and file name");
+ }
+ }
+}
+
+static void
+save_module (module)
+ char *module;
+{
+ if (mod_count == mod_max)
+ {
+ mod_max += MODULE_INCREMENT;
+ mod_list = (char **) xrealloc ((char *) mod_list,
+ mod_max * sizeof (char *));
+ }
+ mod_list[mod_count++] = xstrdup (module);
+}
+
+static void
+expand_modules ()
+{
+}
+
+/* fill_hrec
+ *
+ * Take a ptr to 7-part history line, ending with a newline, for example:
+ *
+ * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
+ *
+ * Split it into 7 parts and drop the parts into a "struct hrec".
+ * Return a pointer to the character following the newline.
+ */
+
+#define NEXT_BAR(here) do { while (isspace(*line)) line++; hr->here = line; while ((c = *line++) && c != '|') ; if (!c) return(rtn); *(line - 1) = '\0'; } while (0)
+
+static char *
+fill_hrec (line, hr)
+ char *line;
+ struct hrec *hr;
+{
+ char *cp, *rtn;
+ int c;
+ int off;
+ static int idx = 0;
+
+ bzero ((char *) hr, sizeof (*hr));
+ while (isspace (*line))
+ line++;
+ if (!(rtn = index (line, '\n')))
+ return ("");
+ *rtn++ = '\0';
+
+ hr->type = line++;
+ (void) sscanf (line, "%x", &hr->date);
+ while (*line && index ("0123456789abcdefABCDEF", *line))
+ line++;
+ if (*line == '\0')
+ return (rtn);
+
+ line++;
+ NEXT_BAR (user);
+ NEXT_BAR (dir);
+ if ((cp = rindex (hr->dir, '*')) != NULL)
+ {
+ *cp++ = '\0';
+ (void) sscanf (cp, "%x", &off);
+ hr->end = line + off;
+ }
+ else
+ hr->end = line - 1; /* A handy pointer to '\0' */
+ NEXT_BAR (repos);
+ NEXT_BAR (rev);
+ hr->idx = idx++;
+ if (index ("FOT", *(hr->type)))
+ hr->mod = line;
+
+ NEXT_BAR (file); /* This returns ptr to next line or final '\0' */
+ return (rtn); /* If it falls through, go on to next record */
+}
+
+/* read_hrecs's job is to read the history file and fill in all the "hrec"
+ * (history record) array elements with the ones we need to print.
+ *
+ * Logic:
+ * - Read the whole history file into a single buffer.
+ * - Walk through the buffer, parsing lines out of the buffer.
+ * 1. Split line into pointer and integer fields in the "next" hrec.
+ * 2. Apply tests to the hrec to see if it is wanted.
+ * 3. If it *is* wanted, bump the hrec pointer down by one.
+ */
+static void
+read_hrecs (fname)
+ char *fname;
+{
+ char *cp, *cp2;
+ int i, fd;
+ struct hrec *hr;
+ struct stat st_buf;
+
+ if ((fd = open (fname, O_RDONLY)) < 0)
+ error (1, errno, "cannot open history file: %s", fname);
+
+ if (fstat (fd, &st_buf) < 0)
+ error (1, errno, "can't stat history file");
+
+ /* Exactly enough space for lines data */
+ if (!(i = st_buf.st_size))
+ error (1, 0, "history file is empty");
+ histdata = cp = xmalloc (i + 2);
+ histsize = i;
+
+ if (read (fd, cp, i) != i)
+ error (1, errno, "cannot read log file");
+ (void) close (fd);
+
+ if (*(cp + i - 1) != '\n')
+ {
+ *(cp + i) = '\n'; /* Make sure last line ends in '\n' */
+ i++;
+ }
+ *(cp + i) = '\0';
+ for (cp2 = cp; cp2 - cp < i; cp2++)
+ {
+ if (*cp2 != '\n' && !isprint (*cp2))
+ *cp2 = ' ';
+ }
+
+ hrec_max = HREC_INCREMENT;
+ hrec_head = (struct hrec *) xmalloc (hrec_max * sizeof (struct hrec));
+
+ while (*cp)
+ {
+ if (hrec_count == hrec_max)
+ {
+ struct hrec *old_head = hrec_head;
+
+ hrec_max += HREC_INCREMENT;
+ hrec_head = (struct hrec *) xrealloc ((char *) hrec_head,
+ hrec_max * sizeof (struct hrec));
+ if (hrec_head != old_head)
+ {
+ if (last_since_tag)
+ last_since_tag = hrec_head + (last_since_tag - old_head);
+ if (last_backto)
+ last_backto = hrec_head + (last_backto - old_head);
+ }
+ }
+
+ hr = hrec_head + hrec_count;
+ cp = fill_hrec (cp, hr); /* cp == next line or '\0' at end of buffer */
+
+ if (select_hrec (hr))
+ hrec_count++;
+ }
+
+ /* Special selection problem: If "since_tag" is set, we have saved every
+ * record from the 1st occurrence of "since_tag", when we want to save
+ * records since the *last* occurrence of "since_tag". So what we have
+ * to do is bump hrec_head forward and reduce hrec_count accordingly.
+ */
+ if (last_since_tag)
+ {
+ hrec_count -= (last_since_tag - hrec_head);
+ hrec_head = last_since_tag;
+ }
+
+ /* Much the same thing is necessary for the "backto" option. */
+ if (last_backto)
+ {
+ hrec_count -= (last_backto - hrec_head);
+ hrec_head = last_backto;
+ }
+}
+
+/* Utility program for determining whether "find" is inside "string" */
+static int
+within (find, string)
+ char *find, *string;
+{
+ int c, len;
+
+ if (!find || !string)
+ return (0);
+
+ c = *find++;
+ len = strlen (find);
+
+ while (*string)
+ {
+ if (!(string = index (string, c)))
+ return (0);
+ string++;
+ if (!strncmp (find, string, len))
+ return (1);
+ }
+ return (0);
+}
+
+/* The purpose of "select_hrec" is to apply the selection criteria based on
+ * the command arguments and defaults and return a flag indicating whether
+ * this record should be remembered for printing.
+ */
+static int
+select_hrec (hr)
+ struct hrec *hr;
+{
+ char **cpp, *cp, *cp2;
+ struct file_list_str *fl;
+ int count;
+
+ /* "Since" checking: The argument parser guarantees that only one of the
+ * following four choices is set:
+ *
+ * 1. If "since_date" is set, it contains a Unix time_t specified on the
+ * command line. hr->date fields earlier than "since_date" are ignored.
+ * 2. If "since_rev" is set, it contains either an RCS "dotted" revision
+ * number (which is of limited use) or a symbolic TAG. Each RCS file
+ * is examined and the date on the specified revision (or the revision
+ * corresponding to the TAG) in the RCS file (CVSROOT/repos/file) is
+ * compared against hr->date as in 1. above.
+ * 3. If "since_tag" is set, matching tag records are saved. The field
+ * "last_since_tag" is set to the last one of these. Since we don't
+ * know where the last one will be, all records are saved from the
+ * first occurrence of the TAG. Later, at the end of "select_hrec"
+ * records before the last occurrence of "since_tag" are skipped.
+ * 4. If "backto" is set, all records with a module name or file name
+ * matching "backto" are saved. In addition, all records with a
+ * repository field with a *prefix* matching "backto" are saved.
+ * The field "last_backto" is set to the last one of these. As in
+ * 3. above, "select_hrec" adjusts to include the last one later on.
+ */
+ if (since_date)
+ {
+ if (hr->date < since_date)
+ return (0);
+ }
+ else if (*since_rev)
+ {
+ Vers_TS *vers;
+ time_t t;
+
+ vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL,
+ hr->file, 1, 0, (List *) NULL, (List *) NULL);
+ if (vers->vn_rcs)
+ {
+ if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0))
+ != (time_t) 0)
+ {
+ if (hr->date < t)
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+ }
+ }
+ freevers_ts (&vers);
+ }
+ else if (*since_tag)
+ {
+ if (*(hr->type) == 'T')
+ {
+ /*
+ * A 'T'ag record, the "rev" field holds the tag to be set,
+ * while the "repos" field holds "D"elete, "A"dd or a rev.
+ */
+ if (within (since_tag, hr->rev))
+ {
+ last_since_tag = hr;
+ return (1);
+ }
+ else
+ return (0);
+ }
+ if (!last_since_tag)
+ return (0);
+ }
+ else if (*backto)
+ {
+ if (within (backto, hr->file) || within (backto, hr->mod) ||
+ within (backto, hr->repos))
+ last_backto = hr;
+ else
+ return (0);
+ }
+
+ /* User checking:
+ *
+ * Run down "user_list", match username ("" matches anything)
+ * If "" is not there and actual username is not there, return failure.
+ */
+ if (user_list && hr->user)
+ {
+ for (cpp = user_list, count = user_count; count; cpp++, count--)
+ {
+ if (!**cpp)
+ break; /* null user == accept */
+ if (!strcmp (hr->user, *cpp)) /* found listed user */
+ break;
+ }
+ if (!count)
+ return (0); /* Not this user */
+ }
+
+ /* Record type checking:
+ *
+ * 1. If Record type is not in rec_types field, skip it.
+ * 2. If mod_list is null, keep everything. Otherwise keep only modules
+ * on mod_list.
+ * 3. If neither a 'T', 'F' nor 'O' record, run through "file_list". If
+ * file_list is null, keep everything. Otherwise, keep only files on
+ * file_list, matched appropriately.
+ */
+ if (!index (rec_types, *(hr->type)))
+ return (0);
+ if (!index ("TFO", *(hr->type))) /* Don't bother with "file" if "TFO" */
+ {
+ if (file_list) /* If file_list is null, accept all */
+ {
+ for (fl = file_list, count = file_count; count; fl++, count--)
+ {
+ /* 1. If file_list entry starts with '*', skip the '*' and
+ * compare it against the repository in the hrec.
+ * 2. If file_list entry has a '/' in it, compare it against
+ * the concatenation of the repository and file from hrec.
+ * 3. Else compare the file_list entry against the hrec file.
+ */
+ char cmpfile[PATH_MAX];
+
+ if (*(cp = fl->l_file) == '*')
+ {
+ cp++;
+ /* if argument to -p is a prefix of repository */
+ if (!strncmp (cp, hr->repos, strlen (cp)))
+ {
+ hr->mod = fl->l_module;
+ break;
+ }
+ }
+ else
+ {
+ if (index (cp, '/'))
+ {
+ (void) sprintf (cp2 = cmpfile, "%s/%s",
+ hr->repos, hr->file);
+ }
+ else
+ {
+ cp2 = hr->file;
+ }
+
+ /* if requested file is found within {repos}/file fields */
+ if (within (cp, cp2))
+ {
+ hr->mod = fl->l_module;
+ break;
+ }
+ }
+ }
+ if (!count)
+ return (0); /* String specified and no match */
+ }
+ }
+ if (mod_list)
+ {
+ for (cpp = mod_list, count = mod_count; count; cpp++, count--)
+ {
+ if (hr->mod && !strcmp (hr->mod, *cpp)) /* found module */
+ break;
+ }
+ if (!count)
+ return (0); /* Module specified & this record is not one of them. */
+ }
+
+ return (1); /* Select this record unless rejected above. */
+}
+
+/* The "sort_order" routine (when handed to qsort) has arranged for the
+ * hrecs files to be in the right order for the report.
+ *
+ * Most of the "selections" are done in the select_hrec routine, but some
+ * selections are more easily done after the qsort by "accept_hrec".
+ */
+static void
+report_hrecs ()
+{
+ struct hrec *hr, *lr;
+ struct tm *tm;
+ int i, count, ty;
+ char *cp;
+ int user_len, file_len, rev_len, mod_len, repos_len;
+
+ if (*since_tag && !last_since_tag)
+ {
+ (void) printf ("No tag found: %s\n", since_tag);
+ return;
+ }
+ else if (*backto && !last_backto)
+ {
+ (void) printf ("No module, file or repository with: %s\n", backto);
+ return;
+ }
+ else if (hrec_count < 1)
+ {
+ (void) printf ("No records selected.\n");
+ return;
+ }
+
+ user_len = file_len = rev_len = mod_len = repos_len = 0;
+
+ /* Run through lists and find maximum field widths */
+ hr = lr = hrec_head;
+ hr++;
+ for (count = hrec_count; count--; lr = hr, hr++)
+ {
+ char repos[PATH_MAX];
+
+ if (!count)
+ hr = NULL;
+ if (!accept_hrec (lr, hr))
+ continue;
+
+ ty = *(lr->type);
+ (void) strcpy (repos, lr->repos);
+ if ((cp = rindex (repos, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+ if ((i = strlen (lr->user)) > user_len)
+ user_len = i;
+ if ((i = strlen (lr->file)) > file_len)
+ file_len = i;
+ if (ty != 'T' && (i = strlen (repos)) > repos_len)
+ repos_len = i;
+ if (ty != 'T' && (i = strlen (lr->rev)) > rev_len)
+ rev_len = i;
+ if (lr->mod && (i = strlen (lr->mod)) > mod_len)
+ mod_len = i;
+ }
+
+ /* Walk through hrec array setting "lr" (Last Record) to each element.
+ * "hr" points to the record following "lr" -- It is NULL in the last
+ * pass.
+ *
+ * There are two sections in the loop below:
+ * 1. Based on the report type (e.g. extract, checkout, tag, etc.),
+ * decide whether the record should be printed.
+ * 2. Based on the record type, format and print the data.
+ */
+ for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++)
+ {
+ char workdir[PATH_MAX], repos[PATH_MAX];
+
+ if (!hrec_count)
+ hr = NULL;
+ if (!accept_hrec (lr, hr))
+ continue;
+
+ ty = *(lr->type);
+ tm = localtime (&(lr->date));
+ (void) printf ("%c %02d/%02d %02d:%02d %-*s", ty, tm->tm_mon + 1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min, user_len, lr->user);
+
+ (void) sprintf (workdir, "%s%s", lr->dir, lr->end);
+ if ((cp = rindex (workdir, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+ (void) strcpy (repos, lr->repos);
+ if ((cp = rindex (repos, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+
+ switch (ty)
+ {
+ case 'T':
+ /* 'T'ag records: repository is a "tag type", rev is the tag */
+ (void) printf (" %-*s [%s:%s]", mod_len, lr->mod, lr->rev,
+ repos);
+ if (working)
+ (void) printf (" {%s}", workdir);
+ break;
+ case 'F':
+ case 'O':
+ if (lr->rev && *(lr->rev))
+ (void) printf (" [%s]", lr->rev);
+ (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod,
+ mod_len + 1 - strlen (lr->mod), "=", workdir);
+ break;
+ case 'W':
+ case 'U':
+ case 'C':
+ case 'G':
+ case 'M':
+ case 'A':
+ case 'R':
+ (void) printf (" %-*s %-*s %-*s =%s= %s", rev_len, lr->rev,
+ file_len, lr->file, repos_len, repos,
+ lr->mod ? lr->mod : "", workdir);
+ break;
+ default:
+ (void) printf ("Hey! What is this junk? RecType[0x%2.2x]", ty);
+ break;
+ }
+ (void) putchar ('\n');
+ }
+}
+
+static int
+accept_hrec (lr, hr)
+ struct hrec *hr, *lr;
+{
+ int ty;
+
+ ty = *(lr->type);
+
+ if (last_since_tag && ty == 'T')
+ return (1);
+
+ if (v_checkout)
+ {
+ if (ty != 'O')
+ return (0); /* Only interested in 'O' records */
+
+ /* We want to identify all the states that cause the next record
+ * ("hr") to be different from the current one ("lr") and only
+ * print a line at the allowed boundaries.
+ */
+
+ if (!hr || /* The last record */
+ strcmp (hr->user, lr->user) || /* User has changed */
+ strcmp (hr->mod, lr->mod) ||/* Module has changed */
+ (working && /* If must match "workdir" */
+ (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
+ strcmp (hr->end, lr->end)))) /* the 2nd parts differ */
+
+ return (1);
+ }
+ else if (modified)
+ {
+ if (!last_entry || /* Don't want only last rec */
+ !hr || /* Last entry is a "last entry" */
+ strcmp (hr->repos, lr->repos) || /* Repository has changed */
+ strcmp (hr->file, lr->file))/* File has changed */
+ return (1);
+
+ if (working)
+ { /* If must match "workdir" */
+ if (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
+ strcmp (hr->end, lr->end)) /* the 2nd parts differ */
+ return (1);
+ }
+ }
+ else if (module_report)
+ {
+ if (!last_entry || /* Don't want only last rec */
+ !hr || /* Last entry is a "last entry" */
+ strcmp (hr->mod, lr->mod) ||/* Module has changed */
+ strcmp (hr->repos, lr->repos) || /* Repository has changed */
+ strcmp (hr->file, lr->file))/* File has changed */
+ return (1);
+ }
+ else
+ {
+ /* "extract" and "tag_report" always print selected records. */
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/ignore.c b/gnu/usr.bin/cvs/cvs/ignore.c
new file mode 100644
index 000000000000..74ab90c785ad
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/ignore.c
@@ -0,0 +1,227 @@
+/*
+ * .cvsignore file support contributed by David G. Grubbs <dgg@ksr.com>
+ */
+
+#include "cvs.