+Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ Integrated changes submitted by Ian Taylor <ian@cygnus.com>
+
+ * update.c (update_dirent_proc): cvs co -p doesn't print
+ anything when run from an empty directory.
+
+ * import.c (import_descend_dir): Check for a file in the
+ repository which will be checked out to the same name as the
+ directory.
+
+Sun May 5 15:49:00 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * configure.in: autoconf 2.9 handles AC_CHECK_LIB in a
+ way that it can not be used to check for main(). Check
+ for printf() instead. (Reported by ian@cygnus.com)
+
+ * configure: Regenerated.
+
+Thu May 2 13:34:37 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * Version 1.7.88
+
+Thu May 2 10:42:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * NEWS: Clarify what happened to examples directory.
+
+Thu May 2 02:06:49 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * INSTALL: Updated for NeXTSTEP 3.3 (1.7)
+
+Thu May 2 01:40:55 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * Compatibility fixes affecting QNX, NetBSD, and SCO
+
+ * configure.in (AC_CHECK_FUNCS): Added check for initgroups(),
+ (ac_cv_func_crypt) Added check for crypt() in -lcrypt;
+ define AUTH_SERVER_SUPPORT only if crypt() is found.
+
+ * configure: Regenerated.
+
+ * src/server.c (HAVE_INITGROUPS): Use initgroups() only if
+ located by configure.
+
+Wed May 1 15:38:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * NEWS: Remove item about reserving all-uppercase tag names.
+
+Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * Version 1.7.86
+
+Sun Apr 14 11:06:44 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * configure.in (AC_OUTPUT): generate contrib/elib/Makefile,
+ tools/Makefile, and tools/pcl-cvs/Makefile. Do not any longer
+ generate contrib/pcl-cvs/Makefile.
+
+ * Makefile.in: deal w/ above changes.
+
+ * configure: regenerated.
+
+ * Added `tools' subdir (pcl-cvs will live there, as will other
+ things maintained along with the CVS distribution).
+
+Wed Apr 10 17:15:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * README: Mention documentation and A4 paper in particular.
+
+Thu Mar 28 12:31:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * NEWS: Add "cvs annotate".
+
+Tue Mar 26 10:46:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * INSTALL: In example, change tag name to avoid using a tag name
+ reserved to CVS.
+
+ * NEWS: Document reservation of some tag names.
+
+Fri Mar 22 10:45:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * INSTALL: Clarify that RCS is only for server or local.
+
+Mon Mar 18 10:15:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * README: Mention info@cyclic.com where we mention support
+ contracts, not at the end where people might be tempted to view it
+ as a generic help line.
+
+Thu Mar 14 16:34:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (stamp-h): Don't run ./config.status --recheck.
+
+Thu Mar 14 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * cvsnt.mak: Regenerate dependencies.
+
+Thu Mar 14 13:45:11 1996 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * configure.in (AC_OUTPUT): Don't create examples/Makefile; we're
+ not using the examples directory any more.
+
+Wed Mar 13 17:02:00 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * INSTALL: Refer to cvs.texinfo rather than out-of-date cvsinit
+ instructions. Instead of telling everyone to update modules
+ whenever adding directories (which is optional), refer to the
+ manual regarding all administrative files. Revise "make check"
+ instructions to be even less encouraging about submitting bug
+ reports.
+
+ * examples/*: Removed.
+ * Makefile.in (SUBDIRS): Remove examples.
+ * cvsinit.sh: Removed.
+ * Makefile.in: Remove all cvsinit and PROGS stuff.
+ * NEWS: Mention cvsinit -> cvs init change.
+
+Mon Mar 11 13:12:35 1996 Samuel Tardieu <sam@inf.enst.fr>
+
+ * BUGS: removed previous description from Greg Woods (3/6/96)
+ since the bug seems to be corrected
+
+Wed Mar 6 10:35:32 1996 Greg A. Woods <woods@most.weird.com>
+
+ * BUGS: describe a weird core-dump with 'cvs co -c'. Now I can't
+ even get a stack backtrace again -- dbx dumps core!
+
+Fri Mar 1 09:21:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * README.VMS: Remove distribution information (since it is no
+ longer different for VMS). Various wording fixes to reflect the
+ fact that using rsh is just one of several ways to connect to a
+ cvs server, not "the official" one. Say that the unsuitable rsh
+ is the UCX one. Clarify what rsh uses privileged ports for.
+
+Fri Mar 1 01:26:28 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * README.VMS, build.com: Added for VMS.
+
+Thu Feb 29 10:04:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * NEWS: Mention change to default ignore list.
+
+Thu Feb 29 00:28:08 1996 Peter Wemm <peter@jhome.DIALix.COM>
+
+ * configure.in: correctly spell FNM_PATHNAME in fnmatch() test,
+ the supplied test fails on proposed POSIX.2, lib/fnmatch.*, Linux,
+ FreeBSD, etc.
+ * configure: Regenerated.
+
+Tue Feb 27 10:43:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * INSTALL: Change submission address to bug-cvs from info-cvs.
+ Encourage submissions to be in the form of diffs to INSTALL.
+
Sun Feb 25 15:23:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
* HACKING: Fix typo.
CVS has been tested on the following platforms. The most recent
version of CVS reported to have been tested is indicated, but more
recent versions of CVS probably will work too. Please send updates to
-this list to info-cvs@prep.ai.mit.edu.
+this list to bug-cvs@prep.ai.mit.edu (doing so in the form of a diff
+to this file is encouraged).
Alpha:
DEC Alpha running OSF/1 version 1.3 using cc (about 1.4A2)
DEC Alpha running OSF/1 version 2.0 (1.4.90)
DEC Alpha running OSF/1 version 2.1 (about 1.4A2)
DEC Alpha running OSF/1 version 3.0 (1.5.95) (footnote 7)
+ DEC Alpha running OSF/1 version 3.2 (1.7+obvious patch)
HPPA:
HP 9000/710 running HP-UX 8.07A using gcc (about 1.4A2)
HP 9000/715 running HP-UX 9.01 (1.6)
+ HPPA running HP-UX 10.01 (1.7)
HPPA 1.1 running HP-UX A.09.03 (1.5.95) (footnote 8)
HPPA 1.1 running HP-UX A.09.04 (1.7.1)
NextSTEP 3.3 (1.6.86)
BSDI 2.0 (1.4.93) (footnote 5)
FreeBSD 2.0.5, i486, gcc (1.5.95)
NextSTEP 3.3 (1.6.86)
+ NeXTSTEP 3.3 (1.7), (footnote 10)
SCO Unix 3.2.4.2 (1.4.93) (footnote 4)
SCO OpenServer 5.0.0, "CC='cc -b elf' configure"
Lynx 2.3.0 080695 (1.6.86) (footnote 9)
- Windows NT 3.51 (1.6.86 client-only)
+ Windows NT 3.51 (1.7.87 client-only)
+ QNX 4 (1.7 + obvious patches)
+ OS/2 Version 3 using IBM C/C++ Tools 2.01 (1.7.86 with patches)
m68k:
Sun 3 running SunOS 4.1.1_U1 w/ bundled K&R /usr/5bin/cc (1.6)
NextSTEP 3.3 (1.6.86)
+ NeXTSTEP 3.3 (1.7), (footnote 10)
Lynx 2.3.0 062695 (1.6.86) (footnote 9)
m88k:
Data General AViiON running dgux 5.4R2.10 (1.5)
DECstation running Ultrix 4.2a (1.4.90)
DECstation running Ultrix 4.3 (1.6.86)
SGI running Irix 4.0.5H using gcc and cc (about 1.4A2) (footnote 2)
- SGI running Irix 5.3 (1.4.93)
+ SGI running Irix 5.3 (1.7)
SGI running Irix-6 (about 1.4.90) (footnote 3)
Siemens-Nixdorf RM600 running SINIX-Y (1.6)
PowerPC or RS/6000:
IBM RS/6000 running AIX 3.1 using gcc and cc (1.6.86)
- IBM RS/6000 running AIX 3.2.5 using xlc (1.5)
+ IBM RS/6000 running AIX 3.2.5 (1.7.87)
IBM RS/6000 running AIX 4.1 using gcc and cc (about 1.4A2) (footnote 1)
Lynx 2.3.1 120495 (1.6.86) (footnote 9)
SPARC:
Sun SPARCstation running Solaris 2.4 using gcc and cc (about 1.5.91)
Sun SPARC running Solaris 2.5 (2.5 beta?) (1.6.4)
NextSTEP 3.3 (1.6.86)
+ NeXTSTEP 3.3 (1.7), (footnote 10)
(footnote 1)
AIX 4.1 systems fail to run "configure" due to bugs in their
So after running configure I had to undef HAVE_DIRENT_H and
define HAVE_SYS_DIR_H.
+(footnote 10) Ralf E. Stranzenbach <ralf@reswi.ruhr.de>
+ I've made some modifications to "filesubr.c" to deal with NFS
+ mounted directories (and those funny .nfs* files). This patch
+ should be used whenever the programmers "sandbox" is located on
+ a NFS mounted device --- at least on NeXTSTEP.
+
-------------------------------------------------------------------------------
Installation under Unix:
value is "/usr/local", with binaries in sub-directory "bin", manual
pages in sub-directory "man", and libraries in sub-directory "lib".
- This release of CVS also requires RCS commands to be installed in
+ If you are using server or local CVS, RCS needs to be installed in
the user's PATH (or a path you have configured in src/options.h,
or a path specified with the -b option). If you don't have RCS,
you will need to get it from GNU as well. It is best to get the
If you receive any un-expected output from the regression tests,
it may indicate a bug in CVS (or might just indicate a problem
running the tests). If you choose to submit a bug report,
- relevant information to include might be your "config.status"
- file, your host type, operating system and compiler information,
- the contents of check.log, and any "make check" output.
+ be aware that, as with all bug reports, you may or may not get a
+ response, and your odds might be better if you include enough information
+ to reproduce the bug, an analysis of what is going wrong (if you have
+ the time and ability to provide one), etc. The check.log file is the
+ first place to look.
4) Install the binaries/documentation:
possibly be made to fit your environment). If things look good,
continue on...
-6) Setup the master source repository. Choose a directory with ample disk
- space available for source files. This is where the RCS ",v" files
- will be stored. Note that this should be some shared directory for your
- site. It should probably be auto-mounted, if you're running NFS.
-
- Say you choose "/src/master" as the root of your source repository.
- Run the "cvsinit" script to help you set it up. It will ask you to
- enter the path to your CVSROOT area. You would enter /src/master in
- this example.
-
- $ ./cvsinit
-
- The cvsinit script will setup a reasonable CVSROOT area to start with.
- It is also valuable to folks who already have a CVSROOT area setup from
- using earlier releases of CVS. It assumes that you have installed CVS
- already (step 4) and that the RCS programs (co and ci) are in your
- PATH. There are many ways to customize CVS for your site. Read the
- cvs(5) manual page when you get the chance.
+6) Set up the master source repository. See the "Setting up the repository"
+ section of cvs.texinfo for details; the one-line summary is (if you
+ are putting the repository in /src/master):
+ $ cvs -d /src/master init
7) Have all users of the CVS system set the CVSROOT environment
variable appropriately to reflect the placement of your source
following commands:
$ make distclean
- $ cvs import -m 'CVS 1.6 distribution' cvs CVS CVS-1_6
+ $ cvs import -m 'CVS 1.6 distribution' cvs CVS_DIST CVS-1_6
9) Having done step 8, one should be able to checkout a fresh copy of the
CVS distribution and hack away at the sources with the following command:
This will make the directory "cvs" in your current directory and
populate it with the appropriate CVS files and directories.
-10) Remember to edit the modules file manually when sources are checked in
- with "cvs import" or "cvs add". A copy of the modules file for editing
- can usually be retrieved with the "cvs checkout modules" command, and
- definitely with the "cvs checkout CVSROOT" command. See cvs(5).
+10) You may wish to customize the various administrative files, in particular
+ modules. See cvs.texinfo for details.
11) Read the NEWS file to see what's new.
Changes since 1.7:
+* New "cvs annotate" command to display the last modification for each
+line of a file, with the revision number, user checking in the
+modification, and date of the modification. For more information see
+the `annotate' node in cvs.texinfo.
+
+* The cvsinit shell script has been replaced by a cvs init command.
+The cvs init command creates some example administrative files which
+are similar to the files found in the examples directory (and copied
+by cvsinit) in previous releases.
+
+* Added the patterns *.olb *.exe _$* *$ to default ignore list.
+
* There is now a $USER internal variable for *info files.
* There is no longer a separate `mkmodules' program; the functionality
Bug reports are accepted, however note that someone may or may not
feel like taking care of your bug report. Support contracts are
-available from Cyclic Software (http://www.cyclic.com).
+available from Cyclic Software (http://www.cyclic.com or
+info@cyclic.com).
To report bugs send mail to bug-cvs@prep.ai.mit.edu, or run the "cvsbug"
program and fill out the template:
Thanks for your support!
- -The CVS Team, and the Cyclic CVS Hackers
+ -The CVS Team
-------------------------------------------------------------------------------
$ make install
$ cvsinit
+The documentation is in the doc subdirectory. cvs.texinfo is the main
+manual; cvs.info* and cvs.ps are the info and postscript versions,
+respectively, generated from cvs.texinfo. The postscript version is
+for A4 paper; if you want US letter size, you need to remove the line
+@afourpaper from cvs.texinfo and re-generate cvs.ps using TeX.
+
-------------------------------------------------------------------------------
* How do I get up-to-date information and information about other
Many contributors have added code to the "contrib" directory. See the
README file there for a list of what is available. There is also a
contributed GNU Emacs CVS-mode in contrib/pcl-cvs.
-
--------------------------------------------------------------------------------
-
- Cyclic Software <info@cyclic.com>
+Sun Apr 14 11:30:36 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * Removed pcl-cvs/ subdir; see tools/ subdir in the top-level from
+ now on.
+ Added elib/ subdir.
+
+ * Makefile.in (dist-dir): Removed all references to pcl-cvs/
+ subdir.
+
+Wed Mar 6 10:20:28 1996 Greg A. Woods <woods@most.weird.com>
+
+ * log_accum.pl: ($MAILER): use sendmail directly to allow other
+ headers to be included
+ * log_accum.pl (mail_notification): add support to allow settting
+ of Reply-To and Date header fields in the sent mail; remove $mailto
+ argument and use the global variable (as with $replyto).
+ * log_accum.pl: add -R option for mail_notification()'s optional
+ Reply-To value [default to $login]
+
+Fri Mar 1 01:51:56 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * listener.c: added as mentioned in ../README.VMS
+
Mon Feb 19 13:37:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
* README: Don't just tell people "we don't want your script"; tell
.PHONY: clean
distclean: clean
- rm -f Makefile pcl-cvs/Makefile $(PROGS) $(CONTRIB_PROGS)
+ rm -f Makefile elib/Makefile $(PROGS) $(CONTRIB_PROGS)
.PHONY: distclean
realclean: distclean
for i in ${DISTFILES}; do \
ln $(srcdir)/$${i} ${DISTDIR}; \
done
- cd pcl-cvs; ${MAKE} dist-dir DISTDIR="../${DISTDIR}/pcl-cvs"
+ cd elib; ${MAKE} dist-dir DISTDIR="../${DISTDIR}/elib"
.PHONY: dist-dir
subdir = contrib
clmerge A perl script to handle merge conflicts in GNU
style ChangeLog files .
Contributed by Tom Tromey <tromey@busco.lanl.gov>.
+ listener A program which listens to a TCP port, authenticates
+ by hostname, then runs a subprocess whose input/output
+ is redirected through the port.
+ Contributed by Benjamin J. Lee <benjamin@cyclic.com>
--- /dev/null
+Sun Apr 14 11:21:03 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * Makefile.in: new file; useful only for "make dist".
+
+ * README: new file.
+
+ * Added ChangeLog (this file).
+
+ * Added elib-1.0.tar.gz.
+
--- /dev/null
+# Makefile for elib distributed with GNU CVS.
+
+# NOTE: this is only used for `make dist'. To actually make elib
+# itself, you must unpack the elib tar file, build and install.
+
+# Do not use this makefile directly, but only from `../Makefile'.
+# Copyright (C) 1986, 1988-1990 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# $CVSid: @(#)Makefile.in 1.6 94/10/22 $
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# Where to put the manual pages.
+mandir = $(prefix)/man
+
+# Use cp if you don't have install.
+INSTALL = @INSTALL@
+
+DISTFILES = ChangeLog README Makefile.in elib-1.0.tar.gz
+
+all: Makefile
+.PHONY: all
+
+install: all
+ @echo "You must unpack, build, and install elib yourself."
+.PHONY: install
+
+tags:
+.PHONY: tags
+
+TAGS:
+.PHONY: TAGS
+
+ls:
+ @echo $(DISTFILES)
+.PHONY: ls
+
+clean:
+ /bin/rm -f *.o core
+.PHONY: clean
+
+distclean: clean
+ rm -f Makefile
+.PHONY: distclean
+
+realclean: distclean
+.PHONY: realclean
+
+dist-dir:
+ mkdir ${DISTDIR}
+ for i in ${DISTFILES}; do \
+ ln $(srcdir)/$${i} ${DISTDIR}; \
+ done
+.PHONY: dist-dir
+
+subdir = contrib/elib
+Makefile: ../../config.status Makefile.in
+ cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
--- /dev/null
+ Elib is included in the CVS distribution as a convenience
+only, because pcl-cvs needs elib to run (see tools/pcl-cvs/). The
+maintainers of CVS do not maintain elib, so please do not send them
+bug reports about elib.
+
+ As of 14 April 1996, the latest release of elib can be found
+at "ftp://ftp.lysator.liu.se/pub/emacs/elib-1.0.tar.gz".
+
#! xPERL_PATHx
# -*-Perl-*-
#
+#ident "@(#)ccvs/contrib:$Name: $:$Id: log_accum.pl,v 1.1.1.3 1996/05/06 22:20:41 tholo Exp $"
+#
# 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
#
# hacked greatly by Greg A. Woods <woods@planix.com>
-# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [-f logfile]
+# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [[-R replyto] ...] [-f logfile]
# -d - turn on debugging
# -m mailto - send mail to "mailto" (multiple)
+# -R replyto - set the "Reply-To:" to "replyto" (multiple)
# -M modulename - set module name to "modulename"
# -f logfile - write commit messages to logfile too
# -s - *don't* run "cvs status -v" for each file
# Configurable options
#
-$MAILER = "Mail"; # set this to something that takes "-s"
+# set this to something that takes a whole message on stdin
+$MAILER = "/usr/lib/sendmail -t";
#
# End user configurable options.
sub build_header {
local($header);
- local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
+ local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d",
$cvsroot,
$modulename,
}
sub mail_notification {
- local($name, @text) = @_;
- open(MAIL, "| $MAILER -s \"CVS Update: " . $modulename . "\" " . $name);
+ local(@text) = @_;
+
+ # if only we had strftime()... stuff stolen from perl's ctime.pl:
+ local($[) = 0;
+
+ @DoW = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+ @MoY = ('Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec');
+
+ # Determine what time zone is in effect.
+ # Use GMT if TZ is defined as null, local time if TZ undefined.
+ # There's no portable way to find the system default timezone.
+ #
+ $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '';
+
+ # Hack to deal with 'PST8PDT' format of TZ
+ # Note that this can't deal with all the esoteric forms, but it
+ # does recognize the most common: [:]STDoff[DST[off][,rule]]
+ #
+ if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) {
+ $TZ = $isdst ? $4 : $1;
+ $tzoff = sprintf("%05d", -($2) * 100);
+ }
+
+ # perl-4.036 doesn't have the $zone or $gmtoff...
+ ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $zone, $gmtoff) =
+ ($TZ eq 'GMT') ? gmtime(time) : localtime(time);
+
+ $year += ($year < 70) ? 2000 : 1900;
+
+ if ($gmtoff != 0) {
+ $tzoff = sprintf("%05d", ($gmtoff / 60) * 100);
+ }
+ if ($zone ne '') {
+ $TZ = $zone;
+ }
+
+ # ok, let's try....
+ $rfc822date = sprintf("%s, %2d %s %4d %2d:%02d:%02d %s (%s)",
+ $DoW[$wday], $mday, $MoY[$mon], $year,
+ $hour, $min, $sec, $tzoff, $TZ);
+
+ open(MAIL, "| $MAILER");
+ print MAIL "Date: " . $rfc822date . "\n";
+ print MAIL "Subject: CVS Update: " . $modulename . "\n";
+ print MAIL "To: " . $mailto . "\n";
+ print MAIL "From: " . $login . "@" . $hostdomain . "\n";
+ print MAIL "Reply-To: " . $replyto . "\n";
+ print MAIL "\n";
print MAIL join("\n", @text), "\n";
close(MAIL);
}
$debug = 1;
print STDERR "Debug turned on...\n";
} elsif ($arg eq '-m') {
- $mailto = "$mailto " . shift @ARGV;
+ if ($mailto eq '') {
+ $mailto = shift @ARGV;
+ } else {
+ $mailto = $mailto . ", " . shift @ARGV;
+ }
+ } elsif ($arg eq '-R') {
+ if ($replyto eq '') {
+ $replyto = shift @ARGV;
+ } else {
+ $replyto = $replyto . ", " . shift @ARGV;
+ }
} elsif ($arg eq '-M') {
$modulename = shift @ARGV;
} elsif ($arg eq '-s') {
@files = split(/ /, $arg);
}
}
-($mailto) || die("No -m mail recipient specified\n");
+($mailto) || die("No mail recipient specified (use -m)\n");
+if ($replyto eq '') {
+ $replyto = $login;
+}
# for now, the first "file" is the repository directory being committed,
# relative to the $CVSROOT location
push(@text, $_);
}
- &mail_notification($mailto, @text);
+ &mail_notification(@text);
exit 0;
}
# Mailout the notification.
#
-&mail_notification($mailto, @text);
+&mail_notification(@text);
# cleanup
#
.\lib\savecwd.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\fileattr.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
SOURCE=.\src\client.c
DEP_CLIEN=\
+ ".\windows-NT\config.h"\
.\src\cvs.h\
.\lib\getline.h\
.\src\edit.h\
.\lib\md5.h\
- ".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\getline.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\savecwd.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\getline.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\edit.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\fileattr.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\getline.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\edit.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\getline.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\savecwd.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\edit.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\savecwd.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\getline.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\lib\savecwd.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\fileattr.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\fileattr.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\watch.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\rcs.h\
.\src\error.h\
.\src\update.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/login.obj : $(SOURCE) $(DEP_LOGIN) $(INTDIR)
.\src\rcs.h\
.\src\error.h\
.\src\update.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/scramble.obj : $(SOURCE) $(DEP_SCRAM) $(INTDIR)
DEP_GETWD=\
".\windows-NT\config.h"\
.\lib\system.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/getwd.obj : $(SOURCE) $(DEP_GETWD) $(INTDIR)
DEP_SIGHA=\
".\windows-NT\config.h"\
.\lib\system.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/sighandle.obj : $(SOURCE) $(DEP_SIGHA) $(INTDIR)
DEP_VALLO=\
".\windows-NT\config.h"\
.\lib\system.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/valloc.obj : $(SOURCE) $(DEP_VALLO) $(INTDIR)
DEP_XGETW=\
".\windows-NT\config.h"\
.\lib\system.h\
- ".\windows-NT\alloca.h"\
".\windows-NT\ndir.h"
$(INTDIR)/xgetwd.obj : $(SOURCE) $(DEP_XGETW) $(INTDIR)
SOURCE=.\lib\regex.c
DEP_REGEX=\
".\windows-NT\config.h"\
- .\lib\regex.h\
- ".\windows-NT\alloca.h"
+ .\lib\regex.h
$(INTDIR)/regex.obj : $(SOURCE) $(DEP_REGEX) $(INTDIR)
$(CPP) $(CPP_PROJ) $(SOURCE)
SOURCE=.\lib\getdate.c
DEP_GETDA=\
- ".\windows-NT\config.h"\
- ".\windows-NT\alloca.h"
+ ".\windows-NT\config.h"
$(INTDIR)/getdate.obj : $(SOURCE) $(DEP_GETDA) $(INTDIR)
$(CPP) $(CPP_PROJ) $(SOURCE)
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
.\src\cvs.h\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
".\windows-NT\rcmd.h"\
".\windows-NT\config.h"\
".\windows-NT\options.h"\
- ".\windows-NT\alloca.h"\
.\lib\fnmatch.h\
".\windows-NT\pwd.h"\
.\lib\system.h\
*.dvi
Makefile
+CVSvn.texi
cvs-paper.ps
cvs.aux
cvs.cp
+Wed May 1 15:38:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (Tags): Document un-revision of all-uppercase tag
+ names.
+
+Wed Apr 24 08:41:51 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (Password authentication security): Rewrite sentence
+ on complex and unknown security bugs to clarify that it is
+ referring to people who have been give access to cvs, not to holes
+ in the authentication method (which is relatively simple).
+
+Tue Apr 23 09:31:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (Wrappers): Talk about what -m does (and does not
+ do). Other minor edits.
+
+Wed Apr 17 15:27:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (rcsinfo): Rewrite paragraph concerning remote CVS.
+ * cvsclient.texi (Responses): Document Template response.
+
+Sun Apr 14 16:01:39 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * .cvsignore: added CVSvn.texi.
+
+Wed Apr 10 16:56:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (~/.cvsrc): Mention setting global options with "cvs".
+
+ * cvs.texinfo (release): Change "modules" to "directories".
+ Release does not take module names as arguments.
+
+ * cvs.texinfo (Creating a branch): Add comments about how we
+ should better document tagging the branchpoint.
+
+Tue Apr 9 19:59:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (Top): Use @value{CVSVN}, not a vague refenece to 1.4.
+
+ * cvs.texinfo (From other version control systems): New node.
+
+Mon Apr 8 15:59:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvsclient.texi (Connection and Authentication): Revise kerberos
+ and pserver sections to reflect the fact that port 2401 is now
+ officially registered.
+
+Thu Mar 28 09:51:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (History browsing): Reinstate this node. Try to get
+ it into some minimally useful state (it still needs a lot of
+ work).
+ (annotate): New node, subnode of History browing.
+
+ * cvsclient.texi (Requests): Add annotate request.
+
+Tue Mar 26 08:46:39 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo: In various examples, change tag names to avoid tag
+ names reserved to CVS.
+
+ * cvs.texinfo (Tags): Document what is a valid tag name.
+
+ * cvs.texinfo (Substitution modes): Try to describe how the
+ various keyword expansion settings interract.
+ (Binary files): Suggest cvs update -A, not removing file and then
+ updating it, to get effect of new keyword expansion options.
+
+ * cvs.texinfo (admin options): Mention CVS's use of `dead' state.
+
+Thu Mar 21 08:25:17 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (Environment variables): Expand introduction to RCS
+ environment variables. Expand and correct CVS_SERVER_SLEEP.
+
+ * cvs.texinfo (Environment variables): Remove POSIXLY_CORRECT; cvs
+ requires options to precede arguments regardless of it.
+
+Thu Mar 21 08:18:42 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * cvs.texinfo: Remove paragrahps about a forthcoming CVS
+ newsgroup and about sending patches to think.com.
+ (Environment): Document some more (all?) used environment
+ variables.
+
+Wed Mar 20 09:44:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvsclient.texi (Introduction): New node.
+ * Makefile.in: Add cruft to reflect fact that cvsclient.texi now
+ uses CVSvn.texi.
+
+Mon Mar 18 14:43:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvsclient.texi (Requests): Add Case request.
+
+Wed Mar 13 16:01:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvsclient.texi (Connection and Authentication): New node.
+
+ * cvsclient.texi (Requests): Expand discussion of Root a bit.
+
+ * cvs.texinfo (Setting up): Don't refer to INSTALL file; revise to
+ reflect some information which had been in the INSTALL file.
+
+ * cvs.texinfo (history file): Update to reflect cvsinit -> cvs
+ init. Adjust discussion of whether history file format is
+ documented.
+ (Setting up): Update to reflect cvsinit -> cvs init.
+
+ * cvsclient.texi (Requests): Document init request.
+
+Thu Feb 29 10:08:31 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.texinfo (loginfo example): Adjust example to reflect the way
+ that CVS actually works. Add comments questioning whether that is
+ the best behavior.
+
+ * cvs.texinfo (cvsignore): Document additions to default ignore list.
+
Mon Feb 26 13:48:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
* cvsclient.texi (Filenames): New node, documents / vs \, etc.
cp $(srcdir)/CVSvn.texi . ; else true; fi
$(MAKEINFO) $(srcdir)/cvs.texinfo -o cvs.info
-cvsclient.info: cvsclient.texi
+cvsclient.info: cvsclient.texi CVSvn.texi
+ if [ ! -f ./CVSvn.texi ]; then \
+ ln -s $(srcdir)/CVSvn.texi . || \
+ ln $(srcdir)/CVSvn.texi . || \
+ cp $(srcdir)/CVSvn.texi . ; else true; fi
$(MAKEINFO) $(srcdir)/cvsclient.texi -o cvsclient.info
+# Version of the protocol suitable for emailing
+cvsclient.txt: cvsclient.texi CVSvn.texi
+ if [ ! -f ./CVSvn.texi ]; then \
+ ln -s $(srcdir)/CVSvn.texi . || \
+ ln $(srcdir)/CVSvn.texi . || \
+ cp $(srcdir)/CVSvn.texi . ; else true; fi
+ $(MAKEINFO) $(srcdir)/cvsclient.texi --no-headers -o cvsclient.txt
+
# If the user gets a distribution (which contains *.info), unpacks
# it, and builds it in a seperate build dir, then *.info* are in srcdir.
# If the user builds *.info (e.g. after editing *.texi), then *.info* are
cp $(srcdir)/CVSvn.texi . ; else true; fi
$(TEXI2DVI) $(srcdir)/cvs.texinfo
-cvsclient.dvi cvsclient.aux: cvsclient.texi
+cvsclient.dvi cvsclient.aux: cvsclient.texi CVSvn.texi
+ if [ ! -f ./CVSvn.texi ]; then \
+ ln -s $(srcdir)/CVSvn.texi . || \
+ ln $(srcdir)/CVSvn.texi . || \
+ cp $(srcdir)/CVSvn.texi . ; else true; fi
$(SET_TEXINPUTS) $(TEX) cvsclient.texi
$(SET_TEXINPUTS) $(TEX) cvsclient.texi
$(TEXINDEX) cvsclient.??
@c something they can both cope with.
This info manual describes how to use and administer
-@sc{cvs} and is updated to release 1.4 or something
-similar.
+@sc{cvs} version @value{CVSVN}.
@end ifinfo
@menu
* Tracking sources:: Tracking third-party sources
* Moving files:: Moving and renaming files
* Moving directories:: Moving and renaming directories
+* History browsing:: Viewing the history of files in various ways
* Keyword substitution:: CVS can include the revision inside the file
* Binary files:: CVS can handle binary files
* Revision management:: Policy questions for revision management
<@t{info-cvs-request@@prep.ai.mit.edu}>. Please be
specific about your email address.
-Work is in progress on creating a newsgroup for
-@sc{cvs}-related topics. It will appear somewhere
-under the @samp{gnu.} hierarchy. Gateways to and from
-the mailing list will be set up.
-@c -- Newsgroup? gnu.cvs.info?
-
-@cindex FTP site
-@cindex Patches to CVS
-@cindex CVS FTP site
-@cindex Fixes to CVS
-@cindex FAQ
-@cindex CVS FAQ
-The @sc{ftp} site @t{think.com} has some @sc{cvs}
-material in the @file{/pub/cvs} subdirectory.
-Currently (late summer 1993) it contains an excellent
-@sc{faq} (Frequently Asked Questions, with answers),
-and an improved (but unofficial) version of @sc{cvs}.
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@unnumberedsec CVS is not@dots{}
access implies fairly broad system access as well. It
might be possible to modify @sc{cvs} to prevent that,
but no one has done so as of this writing.
-Furthermore, there may be other security problems with
-@sc{cvs}; it is not a simple program and determining
-how people might use it to gain access to a system is
-difficult.
+Furthermore, there may be other ways in which having
+access to @sc{cvs} allows people to gain more general
+access to the system; noone has done a careful audit.
In summary, anyone who gets the password gets
repository access, and some measure of general system
@menu
* From files:: This method is useful with old projects
where files already exists.
-
+* From other version control systems:: Old projects where you want to
+ preserve history from another system.
* From scratch:: Creating a module from scratch.
@end menu
are reasonable, and that they belong to the proper
groups. @xref{File permissions}.
+@c The node name is too long, but I am having trouble
+@c thinking of something more concise.
+@node From other version control systems
+@subsection Creating Files From Other Version Control Systems
+@cindex Importing files, from other version control systesm
+
+If you have a project which you are maintaining with
+another version control system, such as @sc{rcs}, you
+may wish to put the files from that project into
+@sc{cvs}, and preserve the revision history of the
+files.
+
+@table @asis
+@cindex RCS, importing files from
+@item From RCS
+If you have been using @sc{rcs}, find the @sc{rcs}
+files---usually a file named @file{foo.c} will have its
+@sc{rcs} file in @file{RCS/foo.c,v} (but it could be
+other places; consult the @sc{rcs} documentation for
+details). Then create the appropriate directories in
+@sc{cvs} if they do not already exist. Then copy the
+files into the appropriate directories in the @sc{cvs}
+repository (the name in the repository must be the name
+of the source file with @samp{,v} added; the files go
+directly in the appopriate directory of the repository,
+not in an @file{RCS} subdirectory). This is one of the
+few times when it is a good idea to access the @sc{cvs}
+repository directly, rather than using @sc{cvs}
+commands. Then you are ready to check out a new
+working directory.
+@c Someday there probably should be a "cvs import -t
+@c rcs" or some such. It could even create magic
+@c branches. It could also do something about the case
+@c where the RCS file had a (non-magic) "0" branch.
+
+@c How many is "many"? Or do they just import RCS files?
+@item From another version control system
+Many version control systems have the ability to export
+@sc{rcs} files in the standard format. If yours does,
+export the @sc{rcs} files and then follow the above
+instructions.
+
+@cindex SCCS, importing files from
+@item From SCCS
+There is a script in the @file{contrib} directory of
+the @sc{cvs} source distribution called @file{sccs2rcs}
+which converts @sc{sccs} files to @sc{rcs} files.
+Note: you must run it on a machine which has both
+@sc{sccs} and @sc{rcs} installed, and like everything
+else in contrib it is unsupported (your mileage may
+vary).
+@end table
+
@c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@node From scratch
@subsection Creating a module from scratch
You can use the @code{tag} command to give a symbolic name to a
certain revision of a file. You can use the @samp{-v} flag to the
@code{status} command to see all tags that a file has, and
-which revision numbers they represent.
+which revision numbers they represent. Tag names can
+contain uppercase and lowercase letters, digits,
+@samp{-}, and @samp{_}. The two tag names @code{BASE}
+and @code{HEAD} are reserved for use by @sc{cvs}. It
+is expected that future names which are special to
+@sc{cvs} will contain characters such as @samp{%} or
+@samp{=}, rather than being named analogously to
+@code{BASE} and @code{HEAD}, to avoid conflicts with
+actual tag names.
+@c FIXME: is the above list of valid characters in tag
+@c names complete?
@cindex Adding a tag
@cindex tag, example
@cindex Branch, creating a
@cindex rtag, creating a branch using
+@c FIXME: should be more explicit about the value of
+@c having a tag on the branchpoint. Also should talk
+@c about creating a branch with tag not rtag.
The @code{rtag} command can be used to create a branch.
The @code{rtag} command is much like @code{tag}, but it
does not require that you have a working copy of the
@example
$ tar xfz wdiff-0.04.tar.gz
$ cd wdiff-0.04
-$ cvs import -m "Import of FSF v. 0.04" fsf/wdiff FSF WDIFF_0_04
+$ cvs import -m "Import of FSF v. 0.04" fsf/wdiff FSF_DIST WDIFF_0_04
@end example
-The vendor tag is named @samp{FSF} in the above
+The vendor tag is named @samp{FSF_DIST} in the above
example, and the only release tag assigned is
@samp{WDIFF_0_04}.
@example
$ tar xfz wdiff-0.05.tar.gz
$ cd wdiff-0.05
-$ cvs import -m "Import of FSF v. 0.05" fsf/wdiff FSF WDIFF_0_05
+$ cvs import -m "Import of FSF v. 0.05" fsf/wdiff FSF_DIST WDIFF_0_05
@end example
For files that have not been modified locally, the newly created
into the main trunk, and tell you to use @samp{checkout -j} to do so.
@example
-$ cvs checkout -jFSF:yesterday -jFSF wdiff
+$ cvs checkout -jFSF_DIST:yesterday -jFSF_DIST wdiff
@end example
@noindent
The above command will check out the latest revision of
-@samp{wdiff}, merging the changes made on the vendor branch @samp{FSF}
+@samp{wdiff}, merging the changes made on the vendor branch @samp{FSF_DIST}
since yesterday into the working copy. If any conflicts arise during
the merge they should be resolved in the normal way (@pxref{Conflicts
example}). Then, the modified files may be committed.
releases correctly, since they probably depend on the
name of the directories.
-@ignore
@c ---------------------------------------------------------------------
-@c @node History browsing
+@node History browsing
@chapter History browsing
@cindex History browsing
@cindex Traceability
@cindex Isolation
+@ignore
+@c This is too long for an introduction (goal is
+@c one 20x80 character screen), and also mixes up a
+@c variety of issues (parallel development, history,
+@c maybe even touches on process control).
+
@c -- @quote{To lose ones history is to lose ones soul.}
@c -- ///
@c -- ///Those who cannot remember the past are condemned to repeat it.
@code{log} command, the usefulness of ChangeLogs
even when you run @sc{cvs}, and things like that.
+@end ignore
+
+@c kind of lame, in a lot of ways the above text inside
+@c the @ignore motivates this chapter better
+Once you have used @sc{cvs} to store a version control
+history---what files have changed when, how, and by
+whom, there are a variety of mechanisms for looking
+through the history.
+
@menu
* log messages:: Log messages
* history database:: The history database
* user-defined logging:: User-defined logging
+* annotate:: What revision modified each line of a file?
@end menu
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node log messages
@section Log messages
-Whenever you commit a file you specify a log message. ///
-@c --
+@c FIXME: @xref to place where we talk about how to
+@c specify message to commit.
+Whenever you commit a file you specify a log message.
+
+@c FIXME: bring the information here, and get rid of or
+@c greatly shrink the "log" node.
+To look through the log messages which have been
+specified for every revision which has been committed,
+use the @code{cvs log} command (@pxref{log}).
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node history database
@section The history database
-///
+@c FIXME: bring the information from the history file
+@c and history nodes here. Rewrite it to be motivated
+@c better (start out by clearly explaining what gets
+@c logged in history, for example).
+You can use the history file (@pxref{history file}) to
+log various @sc{cvs} actions. To retrieve the
+information from the history file, use the @code{cvs
+history} command (@pxref{history}).
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node user-defined logging
@section User-defined logging
-///
+@c FIXME: should probably also mention the fact the -l
+@c global option can disable most of the mechanisms
+@c discussed here (why? What is the -l global option for?).
+@c
+@c FIXME: probably should centralize this information
+@c here, at least to some extent. Maybe by moving the
+@c loginfo, etc., nodes here and replacing
+@c the "user-defined logging" node with one node for
+@c each method.
+You can customize @sc{cvs} to log various kinds of
+actions, in whatever manner you choose. These
+mechanisms operate by executing a script at various
+times. The script might append a message to a file
+listing the information and the programmer who created
+it, or send mail to a group of developers, or, perhaps,
+post a message to a particular newsgroup. To log
+commits, use the @file{loginfo} file (@pxref{loginfo}).
+@c FIXME: What is difference between doing it in the
+@c modules file and using loginfo/taginfo? Why should
+@c user use one or the other?
+To log commits, checkouts, exports, and tags,
+respectively, you can also use the @samp{-i},
+@samp{-o}, @samp{-e}, and @samp{-t} options in the
+modules file. For a more flexible way of giving
+notifications to various users, which requires less in
+the way of keeping centralized scripts up to date, use
+the @code{cvs watch add} command (@pxref{Getting
+Notified}); this command is useful even if you are not
+using @code{cvs watch on}.
+
+@cindex taginfo
+The @file{taginfo} file defines programs to execute
+when someone executes a @code{tag} or @code{rtag}
+command. The @file{taginfo} file has the standard form
+for administrative files (@pxref{Administrative
+files}), where each line is a regular expression
+followed by a command to execute. The arguments passed
+to the command are, in order, the @var{tagname},
+@var{operation} (@code{add} for @code{tag},
+@code{mov} for @code{tag -F}, and @code{del} for
+@code{tag -d}), @var{repository}, and any remaining are
+pairs of @var{filename} @var{revision}. A non-zero
+exit of the filter program will cause the tag to be
+aborted.
+
+@node annotate
+@section Annotate command
+@cindex annotate (subcommand)
+
+@deffn Command {cvs annotate} [@code{-l}] files @dots{}
+
+For each file in @var{files}, print the head revision
+of the trunk, together with information on the last
+modification for each line. The @code{-l} option means
+to process the local directory only, not to recurse
+(@pxref{Common options}). For example:
-@end ignore
+@example
+$ cvs annotate ssfile
+Annotations for ssfile
+***************
+1.1 (mary 27-Mar-96): ssfile line 1
+1.2 (joe 28-Mar-96): ssfile line 2
+@end example
+
+The file @file{ssfile} currently contains two lines.
+The @code{ssfile line 1} line was checked in by
+@code{mary} on March 27. Then, on March 28, @code{joe}
+added a line @code{ssfile line 2}, without modifying
+the @code{ssfile line 1} line. This report doesn't
+tell you anything about lines which have been deleted
+or replaced; you need to use @code{cvs diff} for that
+(@pxref{diff}).
+
+@end deffn
@c ---------------------------------------------------------------------
@node Keyword substitution
@cindex -k (RCS kflags)
@cindex Kflag
-You can control how @sc{rcs} expands keywords
-through the use of the @samp{-k} option (@pxref{Common
-options}). The @samp{-k} option is available with the
-@code{add}, @code{checkout}, @code{diff} and
-@code{update} commands.
+@c FIXME: This could be made more coherent, by expanding it
+@c with more examples or something.
+Each file has a stored default substitution mode, and
+each working directory copy of a file also has a
+substitution mode. The former is set by the @samp{-k}
+option to @code{cvs add} and @code{cvs admin}; the
+latter is set by the -k or -A options to @code{cvs
+checkout} or @code{cvs update}. @code{cvs diff} also
+has a @samp{-k} option. For some examples,
+@xref{Binary files}.
The modes available are:
$ cvs add -m"A test file" kotest
$ cvs ci -m"First checkin; contains a keyword" kotest
$ cvs admin -kb kotest
-$ rm kotest
-$ cvs update kotest
+$ cvs update -A kotest
@end example
When you check in the file @file{kotest} the keywords
substitution method for this file, but it does not
alter the working copy of the file that you have. The
easiest way to get the unexpanded version of
-@file{kotest} is to remove it and check it out again.
+@file{kotest} is @code{cvs update -A}.
@c ---------------------------------------------------------------------
@node Revision management
to specify use of the "old" format, so you would need
@samp{cvs -f diff foobar}.
+In place of the command name you can use @code{cvs} to
+specify global options (@pxref{Global options}). For
+example the following line in @file{.cvsrc}
+
+@example
+cvs -z6
+@end example
+causes @sc{cvs} to use compression level 6
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Global options
it is created. The state is visible in the output from
@var{cvs log} (@pxref{log}), and in the
@samp{$@asis{}Log$} and @samp{$@asis{}State$} keywords
-(@pxref{Keyword substitution}).
+(@pxref{Keyword substitution}). Note that @sc{cvs}
+uses the @code{dead} state for its own purposes; to
+take a file to or from the @code{dead} state use
+commands like @code{cvs remove} and @code{cvs add}, not
+@code{cvs admin -s}.
@item -t[@var{file}]
Useful with @sc{cvs}. Write descriptive text from the
benefit of @sc{cvs} conflict resolution. The scenario might
look like:
+@c FIXME: Should we be recommending tagging the branchpoint?
@example
[[ hacked sources are present ]]
$ cvs tag -b EXPR1
automatically commit to the correct branch, because the
@samp{-r} is sticky. You could also do like this:
+@c FIXME: Should we be recommending tagging the branchpoint?
@example
[[ hacked sources are present ]]
$ cvs tag -b EXPR1
@itemize @bullet
@item
-release [-d] modules@dots{}
+release [-d] directories@dots{}
@item
Requires: Working directory.
@item
@cindex Wrappers
Wrappers allow you to set a hook which transforms files on
-their way in and out of cvs
+their way in and out of @sc{cvs}. Most or all of the
+wrappers features do not work with client/server @sc{cvs}.
The file @file{cvswrappers} defines the script that will be
run on a file when its name matches a regular
checked out of the repository (this is denoted with the
@code{-f} flag)
-The @file{cvswrappers} also specifies the merge
-methodology that should be used when the file is
-updated, that is should a MERGE or a straight COPY of
-the diferences be used when checking into the
-repository.
+The @file{cvswrappers} also has a @samp{-m} option to
+specify the merge methodology that should be used when
+the file is updated. @code{MERGE} means the usual
+@sc{cvs} behavior: try to merge the files (this
+generally will not work for binary files). @code{COPY}
+means that @code{cvs update} will merely copy one
+version over the other, and require the user using
+mechanisms outside @sc{cvs}, to insert any necessary
+changes.
+@c FIXME: which version is copied over which version?
+The @samp{-m} wrapper option only affects behavior when
+merging is done on update; it does not affect how files
+are stored. See @xref{Binary files}, for more on
+binary files.
-The basic format of the file @file{cvswrappers} is given as
-such:
+The basic format of the file @file{cvswrappers} is:
@example
wildcard [option value][option value]...
to the file @file{$CVSROOT/CVSROOT/commitlog},
and any commits to the administrative files (inside
the @file{CVSROOT} directory) are also logged in
-@file{/usr/adm/cvsroot-log} and mailed to @t{ceder}.
-
+@file{/usr/adm/cvsroot-log}.
+@c and mailed to @t{ceder}.
+
+@c FIXME: is it a CVS feature or bug that only the
+@c first matching line is used? It is documented
+@c above, but is it useful? This example (with the
+@c mail to ceder put back in) is awkward to write if
+@c only the first matching line is used.
@example
ALL /usr/local/bin/cvs-log $CVSROOT/CVSROOT/commitlog
-^CVSROOT Mail -s %s ceder
+@c ^CVSROOT Mail -s %s ceder
^CVSROOT /usr/local/bin/cvs-log /usr/adm/cvsroot-log
@end example
@xref{editinfo example}, for an example @file{rcsinfo}
file.
-Note: when @sc{CVS} is accessing a remote repository,
-@file{rcsinfo} will be run on the @emph{remote}
-(i.e., server) side, not the client side (@pxref{Remote
-repositories}).
+When @sc{CVS} is accessing a remote repository,
+the contents of @file{rcsinfo} at the time a directory
+is first checked out will specify a template which does
+not then change. If you edit @file{rcsinfo} or its
+templates, you may need to check out a new working
+directory.
+@c Would be nice to fix CVS so this isn't needed. For
+@c example, a mechanism analogous to CVS/Entries, where
+@c the client keeps track of what version of the template
+@c it has.
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node cvsignore
RCSLOG cvslog.*
tags TAGS
.make.state .nse_depinfo
- *~ #* .#* ,*
+ *~ #* .#* ,* _$* *$
*.old *.bak *.BAK *.orig *.rej .del-*
- *.a *.o *.obj *.so *.Z *.elc *.ln
+ *.a *.olb *.o *.obj *.so *.exe
+ *.Z *.elc *.ln
core
@end example
to log information for the @code{history} command
(@pxref{history}). This file must be created to turn
on logging. This is done automatically if the
-@code{cvsinit} script is used to set up the repository.
+@code{cvs init} command is used to set up the
+repository (@pxref{Setting up}).
The file format of the @file{history} file is
-unfortunately not yet documented anywhere, but it is
-fairly easy to understand most of it.
-@c -- document it here?
+documented only in comments in the @sc{cvs} source
+code, but generally programs should use the @code{cvs
+history} command to access it anyway, in case the
+format changes with future releases of @sc{cvs}.
@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@node Setting up
@cindex Creating a repository
@cindex Setting up a repository
-When you install @sc{cvs} for the first time, you should
-follow the instructions in the @file{INSTALL} file to
-set up the repository.
+To set up a @sc{cvs} repository, choose a directory
+with ample disk space available for the revision
+history of the source files. It should be accessable
+(directly or via a networked file system) from all
+machines which want to use @sc{cvs} in server or local
+mode; the client machines need not have any access to
+it other than via the @sc{cvs} protocol.
+
+To create a repository, run the @code{cvs init}
+command. It will set up an empty repository in the
+@sc{cvs} root specified in the usual way
+(@pxref{Repository}). For example,
+
+@example
+cvs -d /usr/local/cvsroot init
+@end example
-If you want to set up another repository, the easiest
-way to get a reasonable set of working administrative
-files is to run the @code{cvsinit} shell script. It
-will set up an empty repository in the directory
-defined by the environment variable @code{$CVSROOT}.
-(@code{cvsinit} is careful to never overwrite any
+@code{cvs init} is careful to never overwrite any
existing files in the repository, so no harm is done if
-you run @code{cvsinit} on an already set-up
-repository. In fact, running it on an already set-up
-repository is the best way to update the various
-scripts from the @samp{contrib} directory.)
+you run @code{cvs init} on an already set-up
+repository.
+
+@code{cvs init} will enable history logging; if you
+don't want that, remove the history file after running
+@code{cvs init}. @xref{history file}.
@node Variables
@appendixsec Expansions in administrative files
A whitespace-separated list of file name patterns that
@sc{cvs} should ignore. @xref{cvsignore}.
+@cindex CVSWRAPPERS
@item $CVSWRAPPERS
A whitespace-separated list of file name patterns that
@sc{cvs} should treat as wrappers. @xref{Wrappers}.
Specifies the full pathname of the location of @sc{rcs} programs,
such as co(1) and ci(1). If not set, a compiled-in
value is used, or your @code{$PATH} is searched.
+
+@cindex HOME
+@item $HOME
+@cindex HOMEPATH
+@item $HOMEPATH
+Used to locate the directory where the @file{.cvsrc}
+file is searched (@code{$HOMEPATH} is used for Windows-NT).
+@pxref{~/.cvsrc}
+
+@cindex CVS_RSH
+@item $CVS_RSH
+Used in client-server mode when accessing a remote
+repository using @sc{rsh}. The default value is
+@code{rsh}. You can set it to use another program for
+accssing the remote server (e.g. for HP-UX 9, you
+should set it to @code{remsh} because @code{rsh}
+invokes the restricted shell). @pxref{Connecting via
+rsh}
+
+@item $CVS_SERVER
+Used in client-server mode when accessing a remote
+repository using @sc{rsh}. It specifies the name of
+the program to start on the server side when accessing
+a remote repository using @sc{rsh}. The default value
+is @code{cvs}. @pxref{Connecting via rsh}
+
+@item $CVS_PASSFILE
+Used in client-server mode when accessing the @code{cvs
+login server}. Default value is @file{$HOME/.cvspass}.
+@pxref{Password authentication client}
+
+@item $CVS_PASSWORD
+Used in client-server mode when accessing the @code{cvs
+login server}.
+@pxref{Password authentication client}
+
+@item $CVS_CLIENT_PORT
+Used in client-server mode when accessing the server
+via Kerberos.
+@pxref{Kerberos authenticated}
+
+@cindex CVS_RCMD_PORT
+@item $CVS_RCMD_PORT
+Used in client-server mode. If set, specifies the port
+number to be used when accessing the @sc{rcmd} demon on
+the server side. (Currently not used for Unix clients).
+
+@cindex CVS_CLIENT_LOG
+@item $CVS_CLIENT_LOG
+Used for debugging only in client-server
+mode. If set, everything send to the server is logged
+into @file{@code{$CVS_CLIENT_LOG}.in} and everything
+send from the server is logged into
+@file{@code{$CVS_CLIENT_LOG}.out}.
+
+@cindex CVS_SERVER_SLEEP
+@item $CVS_SERVER_SLEEP
+Used only for debugging the server side in
+client-server mode. If set, delays the start of the
+server child process the the specified amount of
+seconds so that you can attach to it with a debugger.
+
+@cindex CVS_IGNORE_REMOTE_ROOT
+@item $CVS_IGNORE_REMOTE_ROOT
+(What is the purpose of this variable?)
+
+@cindex COMSPEC
+@item $COMSPEC
+Used under OS/2 only. It specifies the name of the
+command interpreter and defaults to @sc{cmd.exe}.
+
+
@end table
@sc{cvs} is a front-end to @sc{rcs}. The following environment
-variables affect @sc{rcs}:
+variables affect @sc{rcs}. Note that if you are using
+the client/server @sc{cvs}, these variables need to be
+set on the server side (which may or not may be
+possible depending on how you are connecting). There
+is probably not any need to set any of them, however.
@table @code
@cindex LOGNAME
-\input texinfo
+\input texinfo @c -*- texinfo -*-
@setfilename cvsclient.info
+@include CVSvn.texi
@node Top
@top CVS Client/Server
-This manual describes the client/server protocol used by CVS. It does
-not describe how to use or administer client/server CVS; see the
-regular CVS manual for that.
+This document describes the client/server protocol used by CVS. It does
+not describe how to use or administer client/server CVS; see the regular
+CVS manual for that. This is version @value{CVSVN} of the protocol
+specification---@xref{Introduction} for more on what this version number
+means.
@menu
+* Introduction:: What is CVS and what is the client/server protocol for?
* Goals:: Basic design decisions, requirements, scope, etc.
* Notes:: Notes on the current implementation
* Protocol Notes:: Possible enhancements, limitations, etc. of the protocol
+* Connection and Authentication:: Various ways to connect to the server
* Protocol:: Complete description of the protocol
@end menu
+@node Introduction
+@chapter Introduction
+
+CVS is a version control system (with some additional configuration
+management functionality). It maintains a central @dfn{repository}
+which stores files (often source code), including past versions,
+information about who modified them and when, and so on. People who
+wish to look at or modify those files, known as @dfn{developers}, use
+CVS to @dfn{check out} a @dfn{working directory} from the repository, to
+@dfn{check in} new versions of files to the repository, and other
+operations such as viewing the modification history of a file. If
+developers are connected to the repository by a network, particularly a
+slow or flaky one, the most efficient way to use the network is with the
+CVS-specific protocol described in this document.
+
+Developers, using the machine on which they store their working
+directory, run the CVS @dfn{client} program. To perform operations
+which cannot be done locally, it connects to the CVS @dfn{server}
+program, which maintains the repository. For more information on how
+to connect see @ref{Connection and Authentication}.
+
+This document describes the CVS protocol. Unfortunately, it does not
+yet completely document one aspect of the protocol---the detailed
+operation of each CVS command and option---and one must look at the CVS
+user documentation, @file{cvs.texinfo}, for that information. The
+protocol is non-proprietary (anyone who wants to is encouraged to
+implement it) and an implementation, known as CVS, is available under
+the GNU Public License. The CVS distribution, containing this
+implementation, @file{cvs.texinfo}, and a copy (possibly more or less up
+to date than what you are reading now) of this document,
+@file{cvsclient.texi}, can be found at the usual GNU FTP sites, with a
+filename such as @file{cvs-@var{version}.tar.gz}.
+
+This is version @value{CVSVN} of the protocol specification. This
+version number is intended only to aid in distinguishing different
+versions of this specification. Although the specification is currently
+maintained in conjunction with the CVS implementation, and carries the
+same version number, it also intends to document what is involved with
+interoperating with other implementations (such as other versions of
+CVS); see @xref{Requirements}. This version number should not be used
+by clients or servers to determine what variant of the protocol to
+speak; they should instead use the @code{valid-requests} and
+@code{Valid-responses} mechanism (@pxref{Protocol}), which is more
+flexible.
+
@node Goals
@chapter Goals
repositories are implemented, and the rcsmerge is done by the client.
@end itemize
+@node Connection and Authentication
+@chapter How to Connect to and Authenticate Oneself to the CVS server
+
+Connection and authentication occurs before the CVS protocol itself is
+started. There are several ways to connect.
+
+@table @asis
+@item rsh
+If the client has a way to execute commands on the server, and provide
+input to the commands and output from them, then it can connect that
+way. This could be the usual rsh (port 514) protocol, Kerberos rsh,
+SSH, or any similar mechanism. The client may allow the user to specify
+the name of the server program; the default is @code{cvs}. It is
+invoked with one argument, @code{server}. Once it invokes the server,
+the client proceeds to start the cvs protocol.
+
+@item kserver
+The kerberized server listens on a port (in the current implementation,
+by having inetd call "cvs kserver") which defaults to 1999. The client
+connects, sends the usual kerberos authentication information, and then
+starts the cvs protocol. Note: port 1999 is officially registered for
+another use, and in any event one cannot register more than one port for
+CVS, so the kerberized client and server should be changed to use port
+2401 (see below), and send a different string in place of @samp{BEGIN
+AUTH REQUEST} to identify the authentication method in use. However,
+noone has yet gotten around to implementing this.
+
+@item pserver
+The password authenticated server listens on a port (in the current
+implementation, by having inetd call "cvs pserver") which defaults to
+2401 (this port is officially registered). The client
+connects, sends the string @samp{BEGIN AUTH REQUEST}, a linefeed, the
+cvs root, a linefeed, the username, a linefeed, the password trivially
+encoded (see scramble.c in the cvs sources), a linefeed, the string
+@samp{END AUTH REQUEST}, and a linefeed. The server responds with
+@samp{I LOVE YOU} and a linefeed if the authentication is successful or
+@samp{I HATE YOU} and a linefeed if the authentication fails. After
+receiving @samp{I LOVE YOU}, the client proceeds with the cvs protocol.
+If the client wishes to merely authenticate without starting the cvs
+protocol, the procedure is the same, except @samp{BEGIN AUTH REQUEST} is
+replaced with @samp{BEGIN VERIFICATION REQUEST}, @samp{END AUTH REQUEST}
+is replaced with @samp{END VERIFICATION REQUEST}, and upon receipt of
+@samp{I LOVE YOU} the connection is closed rather than continuing.
+@end table
+
@node Protocol
@chapter The CVS client/server protocol
@table @code
@item Root @var{pathname} \n
-Response expected: no.
-Tell the server which @code{CVSROOT} to use.
+Response expected: no. Tell the server which @code{CVSROOT} to use.
+@var{pathname} must already exist; if creating a new root, use the
+@code{init} request, not @code{Root}. @var{pathname} does not include
+the hostname of the server, how to access the server, etc.; by the time
+the CVS protocol is in use, connection, authentication, etc., are
+already taken care of.
@item Valid-responses @var{request-list} \n
Response expected: no.
server sends responses, send (in a @code{M} response) @samp{?} followed
by the directory and filename.
+@item Case \n
+Tell the server that filenames should be matched against ignore patterns
+in a case-insensitive fashion. Note that this does not apply to other
+comparisons---for example the filenames given in @code{Entry} and
+@code{Modified} requests for the same file must match in case regardless
+of whether the @code{Case} request is sent.
+
@item Argument @var{text} \n
Response expected: no.
Save argument for use in a subsequent command. Arguments
@itemx history \n
@itemx watchers \n
@itemx editors \n
+@itemx annotate \n
Response expected: yes. Actually do a cvs command. This uses any
previous @code{Argument}, @code{Repository}, @code{Entry},
@code{Modified}, or @code{Lost} requests, if they have been sent. The
This means that @code{ci} must use a @code{-m} argument if it wants to
specify a log message.
+@itemx init @var{root-name} \n
+Response expected: yes. If it doesn't already exist, create a @sc{cvs}
+repository @var{root-name}. The @code{Root} request need not have been
+previously sent.
+
@itemx update \n
Response expected: yes. Actually do a @code{cvs update} command. This
uses any previous @code{Argument}, @code{Repository}, @code{Entry},
@item Clear-sticky @var{pathname} \n
Clear any sticky tag or date set by @code{Set-sticky}.
+@item Template @var{pathname} \n
+Additional data: file transmission (note: compressed file transmissions
+are not supported). @var{pathname} ends in a slash; its purpose is to
+specify a directory, not a file within a directory. Tell the client to
+store the file transmission as the template log message, and then use
+that template in the future when prompting the user for a log message.
+
@item Set-checkin-prog @var{dir} \n
Additional data: @var{prog} \n. Tell the client to set a checkin
program, which should be supplied with the @code{Checkin-prog} request
+Thu Apr 25 18:26:34 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * getdate.y (get_date): Set Start from nowtime, not now->time,
+ which may not be set.
+ * getdate.c: Regenerated.
+
+Wed Apr 10 17:55:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * getdate.y (get_date): Use a time_t variable rather than a field
+ in a struct timeb. Works around Solaris compiler bug. Sure, it
+ is a compiler bug, but the workaround is completely painless.
+ * getdate.c: Regenerated.
+
+Fri Mar 22 11:17:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * system.h: If EXIT_FAILURE is not defined by stdlib.h, define it
+ ourself.
+
+Thu Mar 14 16:27:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * system.h: Remove alloca cruft.
+
+Wed Feb 28 03:16:48 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * build_lib.com: Changed definition of symbol CC to search
+ for include files in [-.VMS] so VMS config.h can be picked
+ up without copying.
+
+Tue Feb 27 21:26:34 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * build_lib.com: Added. DCL File to build contents of [.lib]
+
+Tue Feb 27 21:18:38 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * system.h: added an existence_error macro check for EVMSERR
+ necessary for happiness under VMS
+
Thu Feb 22 22:30:04 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
* Makefile.in (OBJECTS): Remove @ALLOCA@
/* $CVSid: @(#)system.h 1.18 94/09/25 $ */
-#ifdef __GNUC__
-#ifndef alloca
-#define alloca __builtin_alloca
-#endif
-#else
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-/* AIX alloca decl has to be the first thing in the file, bletch! */
- #pragma alloca
-#else /* not _AIX */
-#ifdef ALLOCA_IN_STDLIB
- /* then we need do nothing */
-#else
-char *alloca ();
-#endif /* not ALLOCA_IN_STDLIB */
-#endif /* not _AIX */
-#endif /* not HAVE_ALLOCA_H */
-#endif /* not __GNUS__ */
-
#include <sys/types.h>
#include <sys/stat.h>
(((x) == ENOTEXIST) || ((x) == ENOENT))
# endif
#else
+# ifdef EVMSERR
+# define existence_error(x) \
+((x) == ENOENT || (x) == EINVAL || (x) == EVMSERR)
+# else
# define existence_error(x) ((x) == ENOENT)
+# endif
#endif
extern int errno;
#endif
+/* SunOS4 apparently does not define this in stdlib.h. */
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
#if defined(USG) || defined(POSIX)
char *getcwd ();
#else
#### this makefile. However, we need this file in order for 'make
#### dist' to work properly on Unix machines.
-srcdir = ../../ccvs/macintosh
-top_srcdir = ../../ccvs
-cvs_srcdir = ../../ccvs/src
-VPATH = ../../ccvs/macintosh
+srcdir = .
+top_srcdir = ..
+cvs_srcdir = ../src
SHELL = /bin/sh
#include <compat.h>
#include <sys/errno.h>
-/* MetroWerks provides alloca, but only for PPC Macs - try CVS 'alloca' */
-
-#if !__POWERPC__
-#undef REGEX_MALLOC
-#undef HAVE_ALLOCA
-#undef ALLOCA_IN_STDLIB
-#undef HAVE_ALLOCA_H
-#else
-#undef REGEX_MALLOC
-#define HAVE_ALLOCA 1
-#undef ALLOCA_IN_STDLIB
-#define HAVE_ALLOCA_H 1
-#endif
-
/* Define if files are crlf terminated. */
#define LINES_CRLF_TERMINATED 1
{
char *cvs_server;
char *command;
- struct servent *s;
+ char *portenv;
+ struct servent *sptr;
unsigned short port;
if (! (cvs_server = getenv ("CVS_SERVER")))
cvs_server = "cvs";
- command = alloca (strlen (cvs_server)
- + strlen (server_cvsroot)
- + 50);
+ command = xmalloc (strlen (cvs_server)
+ + strlen (server_cvsroot)
+ + 50);
sprintf (command, "%s -d %s server", cvs_server, server_cvsroot);
- if ((s = getservbyname("shell", "tcp")) == NULL)
- error (1, errno, "cannot getservbyname for shell, tcp");
-
+ portenv = getenv("CVS_RCMD_PORT");
+ if (portenv)
+ port = atoi(portenv);
+ else if ((sptr = getservbyname("shell", "tcp")) != NULL)
+ port = sptr->s_port;
else
- port = s->s_port;
+ error (1, errno, "cannot getservbyname for shell, tcp");
read_fd = rcmd (&server_host,
port,
*tofd = write_fd;
*fromfd = read_fd;
+ free (command);
}
+Wed Mar 13 17:06:39 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvsinit.8: Removed.
+ * Makefile.in, cvs.1, cvs.5: Remove references to cvsinit.
+
Tue Feb 13 22:30:54 1996 Samuel Tardieu <sam@inf.enst.fr>
* Makefile.in: Remove reference to mkmodules.1
MAN1FILES = cvs.1
MAN5FILES = cvs.5
-MAN8FILES = cvsbug.8 cvsinit.8
+MAN8FILES = cvsbug.8
MANFILES = $(MAN1FILES) $(MAN5FILES) $(MAN8FILES)
DISTFILES = .cvsignore ChangeLog Makefile.in $(MANFILES)
.ds Rv \\$3
.ds Dt \\$4
..
-.Id $Id: cvs.1,v 1.1.1.3 1996/04/27 19:42:30 tholo Exp $
+.Id $Id: cvs.1,v 1.1.1.4 1996/05/06 22:20:18 tholo Exp $
.TH CVS 1 "\*(Dt"
.\" Full space in nroff; half space in troff
.de SP
.BR co ( 1 ),
.BR cvs ( 5 ),
.BR cvsbug ( 8 ),
-.BR cvsinit ( 8 ),
.BR diff ( 1 ),
.BR grep ( 1 ),
.BR patch ( 1 ),
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
you use \fBcvs\fP to check in a file with the right format to
`\|\fB$CVSROOT/CVSROOT/modules,v\fP\|'.
.SP
-The
-.BR cvsinit ( 8 )
-script will setup a sample `\|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
+Thu Apr 25 09:28:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * README: Add note about LF vs. CRLF in makefile.
+
+Tue Apr 23 20:43:01 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * filesubr.c (get_homedir, expand_wild): New functions.
+
+Wed Feb 28 11:08:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * options.h: Remove AUTH_SERVER_SUPPORT; no longer should be
+ defined in options.h.
+
+ * config.h: Remove C_ALLOCA, CRAY_STACKSEG_END, HAVE_ALLOCA,
+ HAVE_ALLOCA_H, and STACK_DIRECTION to reflect alloca removal.
+
Mon Feb 19 00:35:24 1996 Karl Fogel <kfogel@floss.red-bean.com>
* Makefile.in: deal with new objects `mkmodules' and `vasprintf'.
have edited the `install_dir' variable in the Makefile, you may type
"make install-cvs" to put cvs.exe in the right place.
+ If the makefile has linefeeds only at the end of lines, make
+(at least the port of GNU make that I have) will interpret it
+differently. This is (IMHO) a bizarre behavior, but you need to
+convert the linefeeds to CRLF pairs (editing the file with an editor
+such as emacs will generally do this).
+
You will get warnings during the compilation; ignore them.
Report bugs to <bug-cvs@prep.ai.mit.edu>.
We just want to avoid a redefinition error message. */
#undef _ALL_SOURCE
-/* Define if using alloca.c. */
-#undef C_ALLOCA
-
/* Define if type char is unsigned and you are not using gcc. */
/* We wrote a little test program whose output suggests that char is
signed on this system. Go back and check the verdict when CVS
/* Const is working. */
#undef const
-/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
- This function is required for alloca.c support on those systems. */
-/* This shouldn't matter, but pro forma: */
-#undef CRAY_STACKSEG_END
-
/* Define to `int' if <sys/types.h> doesn't define. */
/* OS/2 doesn't have gid_t. It doesn't even really have group
numbers, I think. This will take more thought to get right, but
let's get it running first. */
#define gid_t int
-/* Define if you have alloca, as a function or macro. */
-#define HAVE_ALLOCA 1
-/* OS/2 has alloca() in <stdlib.h>! */
-#define ALLOCA_IN_STDLIB 1
-
-/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
-/* but calls it _alloca and says it returns void *. We provide our
- own header file. */
-/* OS/2 declares alloca in `stdlib.h'. */
-/* #define HAVE_ALLOCA_H 1 */
-#undef HAVE_ALLOCA_H
-
/* Define if you support file names longer than 14 characters. */
/* We support long file names, but not long corporate acronyms. */
#define HAVE_LONG_FILE_NAMES 1
#includes, so things should be okay. */
/* #undef size_t */
-/* If using the C implementation of alloca, define if you know the
- direction of stack growth for your system; otherwise it will be
- automatically deduced at run-time.
- STACK_DIRECTION > 0 => grows toward higher addresses
- STACK_DIRECTION < 0 => grows toward lower addresses
- STACK_DIRECTION = 0 => direction of growth unknown
- */
-/* This shouldn't matter, but pro forma: */
-#undef STACK_DIRECTION
-
/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
/* sys/stat.h apparently doesn't even have them; setting this will let
../lib/system.h define them. */
if (close (infd) < 0)
error (0, errno, "warning: couldn't close %s", infile);
}
+
+/* Return the home directory. Returns a pointer to storage
+ managed by this function or its callees (currently getenv). */
+char *
+get_homedir ()
+{
+ return getenv ("HOME");
+}
+
+/* See cvs.h for description. On OS/2 this does nothing, but it probably
+ should be expanding wildcards like on NT. */
+void
+expand_wild (argc, argv, pargc, pargv)
+ int argc;
+ char **argv;
+ int *pargc;
+ char ***pargv;
+{
+ int i;
+ *pargc = argc;
+ *pargv = (char **) xmalloc (argc * sizeof (char *));
+ for (i = 0; i < argc; ++i)
+ (*pargv)[i] = xstrdup (argv[i]);
+}
* unless the user overrides the default with the RCSBIN environment variable
* or the "-b" option to CVS.
*
- * If you're compiling the authenticating server (see
- * AUTH_SERVER_SUPPORT farther down), then you probably want to set
- * RCSBIN_DFLT. The authenticating server starts out running as root,
- * and then switches to run as the appropriate user once
- * authentication is complete. No shell is ever started by that user,
- * so the PATH environment variable may not contain the directory with
- * the RCS binaries, even though if that user logged in normally, PATH
- * would include the directory. An alternative to setting RCSBIN_DFLT
- * is to make sure that root has the right directory in its path
- * already.
+ * If you use the password-authenticating server, then you need to
+ * make sure that the server can find the RCS programs to invoke them.
+ * The authenticating server starts out running as root, and then
+ * switches to run as the appropriate user once authentication is
+ * complete. But no actual shell is ever started by that user, so the
+ * PATH environment variable may not contain the directory with the
+ * RCS binaries, even though if that user logged in normally, PATH
+ * would include the directory.
+ *
+ * One way to solve this problem is to set RCSBIN_DFLT here. An
+ * alternative is to make sure that root has the right directory in
+ * its path already. Another, probably better alternative is to
+ * specify -b in /etc/inetd.conf.
*
* This define should be either the empty string ("") or a full pathname to the
* directory containing all the installed programs from the RCS distribution.
/*
* Under OS/2, we build the authenticated client by default.
- * But not the server, because there is no server support for OS/2
- * yet.
*/
#define AUTH_CLIENT_SUPPORT 1
-/* #define AUTH_SERVER_SUPPORT 1 */
/* End of CVS configuration section */
+Sun May 5 21:39:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * vers_ts.c (Version_TS): If sdtp is NULL, go ahead and check
+ RCS_getexpand for options. Fixes binaries and non-unix clients.
+ * sanity.sh: Fix binfiles-5.5 to test for the correct behavior
+ rather than the buggy behavior which existed when the binfiles-5.5
+ test was written.
+ (binfiles-14c,binfiles-14f): Likewise.
+
+Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ Integrated changes submitted by Ian Taylor <ian@cygnus.com>
+
+ * update.c (update_dirent_proc): cvs co -p doesn't print
+ anything when run from an empty directory.
+
+ * import.c (import_descend_dir): Check for a file in the
+ repository which will be checked out to the same name as the
+ directory.
+
+Thu May 2 13:34:37 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * Version 1.7.88
+
+Thu May 2 01:40:55 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * server.c (HAVE_INITGROUPS): Use initgroups() only if
+ located by configure, in the event a system has crypt(), but
+ no initgroups()
+
+Wed May 1 18:05:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh (basica): When testing rejection of reserved tag name,
+ use BASE instead of RESERVED.
+
+Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com>
+
+ * rcs.c (linevector_delete): Only copy up to vec->nlines - nlines,
+ not to vec->nlines.
+
+Wed May 1 15:43:21 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * rcscmds.c (RCS_settag): Instead of reserving all tag names
+ containing only uppercase letters, reserve only BASE and HEAD.
+ * sanity.sh (mflag): Revert 26 Mar change; use all-uppercase tag
+ name again.
+
+Wed May 1 15:15:11 1996 Tom Jarmolowski <tjj@booklink.com>
+
+ * rcs.c (linevector_add): Move increment of i out of larger
+ statement, to avoid assumptions about evaluation order.
+
+Tue Apr 30 15:46:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.7.87.
+
+ * server.c (check_password): Don't use ANSI string concatenation.
+ Reindent function.
+
+Wed Apr 24 17:27:53 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * vers_ts.c (Version_TS): xmalloc enough space (1 more
+ byte). Thanks to purify!
+
+Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * Version 1.7.86
+
+Thu Apr 18 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * client.c (try_read_from_server): Compare return value from fwrite
+ with a size_t not an int (Visual C++ lint).
+
+Wed Apr 17 11:56:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (try_read_from_server): New function.
+ (read_from_server): Use it.
+ (read_counted_file): New function.
+ * client.c, server.c: Add Template response.
+ * cvs.h (CVSADM_TEMPLATE): Added.
+ * logmsg.c (do_editor): If repository is NULL, use CVSADM_TEMPLATE
+ file in place of rcsinfo.
+ * server.c, server.h (server_template): New function.
+ * create_adm.c (Create_Admin): Call it.
+
+Tue Apr 16 13:56:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * repos.c (Name_Repository): Fix comments.
+ * create_adm.c (Create_Admin): Fix indentation.
+
+Wed Apr 10 16:46:54 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * options.h.in: Include relevant information here rather than
+ citing (former) FAQ.
+
+ * ChangeLog-9395: Fix typo in introductory paragraph.
+
+Wed Apr 10 14:55:10 1996 code by Mike Spengler mks@msc.edu
+ comments by Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * filesubr.c (unlink_file_dir,deep_remove_dir): Don't call unlink
+ on something which might be a directory; check using isdir instead.
+
+Wed Apr 10 14:55:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * checkout.c (build_dirs_and_chdir): Pass path, not cp, to
+ Create_Admin. The former is the correct update dir.
+ * sanity.sh (modules): New tests modules-155* test, for above fix.
+
+Mon Apr 8 13:53:27 1996 Samuel Tardieu <sam@inf.enst.fr>
+
+ * rcs.c (annotate_fileproc): If the file is not under CVS control,
+ return instead of dumping a core. Don't bug on files with an empty
+ first revision.
+
+Fri Mar 29 16:08:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * rcs.c (annotate_fileproc): If last line of add-chunk is not
+ newline terminated, end the loop when we find that out.
+
+Fri Mar 29 16:59:34 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * rcs.c (annotate_fileproc): allow last line of add-chunk not to
+ be newline terminated
+
+Thu Mar 28 10:56:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ Add more diff tests:
+ * sanity.sh (basic2): Use dotest for test 61.
+ (basica): Add test basica-6.2.
+ (branches): Add tests branches-14.4 and branches-14.5.
+ (basic1): Remove tests 19, 20, 25, and 26. The only thing this
+ might miss out on is diff's interaction with added and removed
+ files, but those tests didn't test that very well anyway.
+
+ * rcs.c (RCS_getrevtime): Add comment regarding years after 1999.
+
+ * rcs.c: Add "cvs annotate" command and related code.
+ (getrcskey): Move special handling of RCSDESC from here to
+ callers. Handle those keys (desc, log, text) which do not
+ end in a semicolon.
+ * rcs.h (RCSVers): Add author field.
+ * rcs.c (RCS_reparsercsfile): Set it.
+ * cvs.h (annotate), main.c (cmd_usage, cmds), client.h client.c
+ (client_annotate), server.c (serve_annotate, requests): Usual
+ machinery to add a new command.
+ * sanity.sh (basica): Test cvs annotate.
+
+ * sanity.sh (branches): More tests, of things like adding files on
+ the trunk after a branch has been made.
+
+Tue Mar 26 09:48:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * expand_path.c: Don't declare free and xmalloc; cvs.h already
+ takes care of that.
+
+ * sanity.sh (mflag): Don't use tag name reserved to CVS.
+
+ NT local changes plus miscellaneous things noticed in the process:
+ * import.c (add_rcs_file): Use binary mode to write RCS file. Use
+ \012 where linefeed is intended. Copy data a small block at a
+ time, until we hit EOF, rather than trying to read the whole file
+ into memory at once.
+ * client.c (send_modified): Add comments regarding st_size.
+ * commit.c (commit): Add comments regarding binary mode and read().
+ * logmsg.c (do_editor): Add comments regarding st_size.
+ * server.c (server_updated): Use binary mode to read file we are
+ sending.
+
+ * rcscmds.c (RCS_settag): Complain if user tries to add a tag name
+ reserved to CVS.
+ * sanity.sh (basica): Test for this behavior.
+
+ * sanity.sh (binfiles): New tests test ability to change keyword
+ expansion.
+
+Mon Mar 25 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * cvs.h, filesubr.c (expand_wild): New function.
+ * recurse.c (start_recursion): Call expand_wild at beginning and
+ free its results at the end.
+ * cvs.h, subr.c (xrealloc): Make argument and return value void *.
+ * client.h, client.c (send_file_names): Add flags argument. If
+ SEND_EXPAND_WILD flag is passed, call expand_wild at beginning and
+ free its results at the end.
+ * admin.c, add.c, log.c, tag.c, status.c, edit.c, watch.c,
+ update.c, commit.c, remove.c, client.c, diff.c: Update callers.
+
+Fri Mar 22 10:09:55 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * error.c (error, fperror): Exit with status EXIT_FAILURE rather
+ than STATUS. We had been neglecting to check for 256, and the
+ value of providing a count of errors is probably minimal anyway.
+ * add.c, modules.c, mkmodules.c, tag.c, server.c, main.c,
+ import.c, client.c, scramble.c, recurse.c: Exit with status
+ EXIT_FAILURE rather than 1. On VMS, 1 is success, not failure.
+ * main.c (main): Return EXIT_FAILURE or 0. The value of providing
+ a count of errors is minimal.
+
+ * client.c (init_sockaddr): Exit with status 1 rather than
+ EXIT_FAILURE. The latter apparently doesn't exist on SunOS4.
+ Reindent function.
+
+Mon Mar 18 14:28:00 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h, ignore.c: New variable ign_case.
+ * ignore.c (ign_name): If it is set, match in a case-insensitive
+ fashion.
+ * server.c (serve_case): New function.
+ (requests): Add Case request.
+ * client.c (start_server): If FILENAMES_CASE_INSENSITIVE is
+ defined, send Case request.
+
+Sat Mar 16 08:20:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ For reference, this change takes cvs's text segment from 315392
+ bytes to 311296 bytes (one 4096 byte page).
+ * cvs.h (struct file_info): Add fullname field.
+ * recurse.c (do_file_proc): Set it.
+ * commit.c (find_fileproc), client.c (send_fileproc), commit.c
+ (check_fileproc), diff.c (diff_fileproc), edit.c
+ (unedit_fileproc), patch.c (patch_fileproc), remove.c
+ (remove_fileproc), rtag.c (rtag_fileproc), tag.c (tag_fileproc),
+ update.c (update_fileproc), watch.c (watchers_fileproc): Use it
+ instead of computing it each time.
+ * diff.c (diff_fileproc), remove.c (remove_fileproc): Use fullname
+ where we had been (bogusly) omitting the directory from user
+ messages.
+ * edit.c (unedit_fileproc, edit_fileproc): If we cannot close
+ CVSADM_NOTIFY, mention CVSADM_NOTIFY rather than finfo->file in
+ error message.
+ * rtag.c (rtag_fileproc), tag.c (tag_fileproc): Reindent.
+
+Fri Mar 15 15:12:11 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * server.h: fix prototype of server_pause_check (was
+ server_check_pause)
+
+Thu Mar 14 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * vers_ts.c (Version_TS), entries.c (Scratch_Entry, AddEntryNode):
+ Change findnode to findnode_fn.
+
+ * main.c: Depending on HAVE_WINSOCK_H, include winsock.h or
+ declare gethostname.
+ * cvs.h: Don't declare it here.
+
+Thu Mar 14 07:06:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * commit.c (find_fileproc): If vn_user is NULL and ts_user is not,
+ print an error rather than silently succeeding.
+ * sanity.sh (basica-notadded): New test, for above fix.
+ (dotest_internal): New function.
+ (dotest,dotest_fail): Call it instead of duplicating code between
+ these two functions.
+
+ * sanity.sh: Skip tests binfiles-9 through binfiles-13 for remote.
+
+ * options.h.in: Adjust comment to reflect kfogel change.
+
+Thu Mar 14 01:38:30 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * options.h.in (AUTH_CLIENT_SUPPORT): turn on by default.
+
+Wed Mar 13 09:25:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * vers_ts.c (Version_TS): Don't try to override options from rcs
+ file if there isn't an rcs file (e.g. called from send_fileproc).
+ This fixes a bug detected by test 59 in "make remotecheck".
+
+ * rcs.c (RCS_reparsercsfile, RCS_getexpand): Assert that argument
+ is not NULL.
+
+ Fix a gcc -Wall warning:
+ * rcs.c, rcs.h (RCS_getexpand): New function.
+ * vers_ts.c (Version_TS): Call it.
+ * rcs.c (RCS_reparsercsfile): Make static.
+
+ Add a "cvs init" command. This is needed because cvsinit.sh
+ invoked mkmodules which doesn't exist any more.
+ * mkmodules.c: Break filelist out of mkmodules function, rename
+ struct _checkout_file to struct admin_file (for namespace
+ correctness), and add contents field.
+ (init,mkdir_if_needed): New functions.
+ * cvs.h (init): Declare.
+ * main.c (cmds): Add init.
+ (main): If command is init, don't require cvsroot to exist.
+ * client.c, client.h (client_init, send_init_command): New functions.
+ * client.c (start_server): Don't send Root request if command is init.
+ * server.c (serve_init): New function.
+ (requests): Add "init".
+
+Wed Mar 13 09:51:03 MET 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * vers_ts.c (Version_TS): set options to default option if the
+ file if no -k option but -A was given. This avoids the (wrong)
+ update message for binary files which are up-to-date when
+ running 'cvs -A'.
+
+ * update.c (checkout_file): remove test of -k option stored in the
+ file itself because it was moved to vers_ts.c
+
+ * sanity.sh: added tests for the above fix.
+
+Tue Mar 12 13:47:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * hash.c (findnode): Adjust comment regarding errors.
+
+ * hash.c (findnode, findnode_fn): Assert that key != NULL. This
+ way the check still happens even if the function is later
+ rewritten to not start out by calling hashp.
+
+Mon Mar 11 10:21:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: If expr accepts multi-line patterns but is too
+ liberal in matching them, print a warning but keep going.
+
+ * sanity.sh: Add QUESTION variable, analogous to PLUS. Use it
+ instead of \? to match a question mark.
+
+ * cvs.h (CVSMODULE_OPTS, CVSMODULE_SPEC): Move from here...
+ * modules.c: ...to here. They are only used here and the code to
+ handle the syntax of modules files should not be scattered all over.
+ * modules.c (CVSMODULE_OPTS): Add "+" as first character.
+ * sanity.sh (modules): New tests 148a0 and 148a1 test for
+ above-fixed bug.
+
+Mon Mar 11 13:11:04 1996 Samuel Tardieu <sam@inf.enst.fr>
+
+ * modules.c (cat_module): set optind to 0 to force getopt() to
+ reinitialize its internal nextchar
+
+Mon Mar 11 00:09:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * hash.c (findnode, findnode_fn): Revert changes of 7-8 Mar 1996.
+ The correct style is to assert() that key != NULL (see HACKING),
+ which is already done in the hashp function.
+ * fileattr.c (fileattr_delproc): Likewise, assert() that
+ node->data != NULL rather than trying to deal with it being NULL.
+
+Fri Mar 8 01:31:04 1996 Greg A. Woods <woods@most.weird.com>
+
+ * hash.c (findnode_fn): one more place to avoid calling hashp()
+ with a NULL key
+
+Thu Mar 7 17:30:01 1996 Greg A. Woods <woods@most.weird.com>
+
+ * hash.c (findnode): also return NULL if key is not set
+ [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and
+ supposedly in a PR that should be marked "fixed"..... ]]
+
+ * fileattr.c (fileattr_set): set node->data to NULL after freeing
+ it to prevent subsequent accesses
+ (fileattr_delproc): don't free node->data if it's NULL, and set it
+ to NULL after freeing
+ [[ reported by Chris_Eich@optilink.optilink.dsccc.com, and
+ supposedly in a PR that should be marked "fixed"..... ]]
+
+Fri Mar 1 14:56:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh (basica): New test basica-4a tests for bug fixed by
+ sam@inf.enst.fr on 1 Mar 96.
+
+Fri Mar 1 18:10:49 1996 Samuel Tardieu <sam@inf.enst.fr>
+
+ * tag.c (check_fileproc): Check for file existence before trying
+ to tag it.
+
+Fri Mar 1 07:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (update_entries): If command is export, set options to
+ NULL.
+
+Thu Feb 29 16:54:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * lock.c (write_lock, Reader_Lock): Remove
+ BOGUS_UNLESS_PROVEN_OTHERWISE code. It was pretty bogus, and has
+ been ifdeffed out for a long time.
+ * cvs.h (CVSTFL): Removed; no longer used.
+
+ * cvsrc.c, cvs.h (read_cvsrc): Pass in command name rather than
+ using global variable command_name.
+ * main.c (command_name): Initialize to "", not "cvs" so that error
+ messages don't say "cvs cvs". Update calls to read_cvsrc to pass
+ in command_name or "cvs" as appropriate.
+ * sanity.sh (basica): New test basica-9 tests for above-fixed bug.
+
+ * lock.c: Rename unlock to lock_simple_remove to avoid conflict
+ with builtin function on QNX.
+
+Thu Feb 29 17:02:22 1996 Samuel Tardieu <sam@inf.enst.fr>
+
+ * fileattr.c (fileattr_get): Removed NULL pointer dereference
+ which occurred in the absence of default attribute.
+
+Thu Feb 29 07:36:57 1996 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ * rcs.c (RCS_isbranch, RCS_whatbranch): Remove no longer used file
+ argument, swap order of remaining two arguments to be like other
+ RCS_* functions.
+ (RCS_nodeisbranch): swap order of arguments to be like other RCS_*
+ functions.
+ * rcs.h (RCS_isbranch, RCS_whatbranch, RCS_nodeisbranch): Update
+ prototypes for above changes.
+ * commit.c, rtag.c, status.c, tag.c: Update for above calling
+ convention changes.
+
+Thu Feb 29 08:39:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (start_server): Revert changes which claimed to fall
+ back to a different way of connecting. Add comments explaining
+ why. (I don't think the changes did what they claimed, anyway).
+ Use indentation rather than comments to line up #if, #else, and
+ #endif.
+
+ * patch.c (patch, patch_fileproc): Revert change to add optional
+ arguments to -c and -u. Optional arguments are evil and in
+ violation of the POSIX argument syntax guidelines. The correct
+ way to do this is -C and -U. Also change DIFF back to "diff" in
+ output (see comments).
+
+ gcc -Wall lint:
+ * client.c (copy_a_file): Declare p inside the #ifdef in which is
+ it used.
+ * commit.c (remove_file): Remove unused variable p.
+ * commit.c (checkaddfile): Remove unused variables p.
+ * rcs.c (RCS_isbranch): Remove unused variable p.
+ * rcs.c: Remove unused declarations and definitions of
+ parse_rcs_proc, rcsnode_delproc, rcslist, and repository.
+ * rtag.c (rtag_fileproc): Remove unused variable p.
+ * patch.c (patch_fileproc): Remove unused variable p.
+ * tag.c (val_fileproc): Remove unused variable node.
+ * client.c, import.c, lock.c, server.c: Cast pid_t to long before
+ passing it to %ld.
+
+ * cvs.h: Don't prototype gethostname; merely declare it (on linux,
+ second argument is size_t not int).
+
+Thu Feb 29 10:29:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de>
+
+ * sanity.sh: added "cat > /dev/null" to loginfo entry to avoid the
+ SIGPIPE signal
+
+Thu Feb 29 10:28:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de>
+
+ * patch.c: added new variable diff_opt
+ (patch): allow optional parameter to -c and -u option, send it to
+ server
+ (patch_fileproc): cleaned up the code which prints the current
+ filename. For "-s" option, print the pathname relative to CVSROOT
+ instead of just the filename.
+
+ * filesubr.c (xchmod): added cast to shut up gcc
+
+ * cvs.h: added prototype for gethostname
+
+Thu Feb 29 10:27:25 MET 1996 Norbert Kiesel (nk) <nk@col.sw-ley.de>
+
+ * lock.c (write_lock), (Reader_Lock), import.c (update_rcs_file),
+ client.c (update_entries), (send_modified), server.c (server),
+ (receive_file), (server_updated): use %ld for printing pid_t
+ variables
+
+Thu Feb 29 02:22:12 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * run.c (run_exec): Added VMS return status support.
+
+Thu Feb 29 01:07:43 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * client.c (send_to_server): wrtn wasn't being declared under
+ VMS for some reason.
+
+Wed Feb 28 23:27:04 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * client.c: Changed #ifdef VMS && NO_SOCKET_TO_FD to
+ #if defined(VMS) && defined(NO_SOCKET_TO_FD)
+
+Wed Feb 28 22:28:43 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * build_src.com: Added DCL command procedure to build
+ and link CVS client for VMS.
+
+Wed Feb 28 22:07:20 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * client.c: VMS CVS client specific changes.
+
+ Added USE_DIRECT_TCP to allow CVS_PORT to be used to specify
+ a TCP connection port (no Kerberos). Changed
+ start_kerberos_server() to start_tcp_server().
+
+ In copy_a_file(): transform a backup file to have a
+ VMS-friendly name.
+
+ Added HAVE_CONFIG_H to include "config.h".
+
+ start_server() will starts the first successful of any
+ mutually exclusive methods of starting the CVS server
+ which might be enabled.
+
+ Initialized use_socket_style and server_sock for VMS in
+ start_server().
+
+Wed Feb 28 21:49:48 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * find_names.c, recurse.c, cvs.h: Changed Find_Dirs() to
+ Find_Directories().
+ * cvs.h: Added VMS filenames enabled through USE_VMS_FILENAMES
+ VMS POSIX will require to use the regular CVS filenames
+ while VMS is #define'd.
+
+Wed Feb 28 21:26:22 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * ignore.c: Added the patterns *.olb *.exe _$* *$ to default
+ ignore list for VMS.
+
+Wed Feb 28 13:32:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * logmsg.c (do_editor): Fix indentation.
+
+Wed Feb 28 12:56:49 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * logmsg.c (do_editor): If no editor is defined, exit and print
+ a message.
+
+Wed Feb 28 10:40:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * vers_ts.c (time_stamp, time_stamp_server): Reindent and revise
+ comments.
+
+Tue Feb 27 23:57:55 1996 Benjamin J. Lee <benjamin@cyclic.com>
+
+ * vers_ts.c: gmtime() returns NULL on some systems (VMS)
+ revert to local time via ctime() if GMT is not avaiable.
+
+Tue Feb 27 13:07:45 1996 J.T. Conklin <jtc@rtl.cygnus.com>
+
+ The changes listed below cause cvs to parse each rcs file (and
+ free the associated rcsnode after the file has been processed)
+ sequentially. cvs used to parse all files in a directory, an
+ approach that does not scale to huge repositories with lots
+ of revisions/branches/tags/etc.
+
+ * cvs.h (struct file_info): Removed srcfiles field. Added rcs
+ (node) field.
+ * recurse.c (do_recursion): Removed code that pre-parsed all
+ rcs files in the directory.
+ (do_file_proc): Parse current rcs file.
+ * rcs.c (RCS_parsefiles, parse_rcs_proc, RCS_addnode): Removed.
+ (RCS_isbranch, RCS_whatbranch): Changed srcfiles argument to
+ rcs (node).
+ * rcs.h (RCS_parsefiles, RCS_addnode): Removed prototypes.
+ (RCS_isbranch, RCS_whatbranch): Updated prototypes.
+ * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c,
+ commit.c, diff.c, history.c, import.c, log.c, patch.c, remove.c,
+ rtag.c, status.c, tag.c, update.c, vers_ts: Updated for above
+ calling convention / data structure changes.
+
Mon Feb 26 16:07:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+ * Version 1.7.3.
+
* Version 1.7.2.
Mon Feb 26 1996 Jim Kingdon <kingdon@cyclic.com>
(which produced releases such as 1.4A1 and 1.4A2) went into what has
become ChangeLog-9194, and changes which existed only at Cygnus went
into this file (ChangeLog-9395). Eventually the Cygnus release became
-Cyclic CVS (it is was then called), which became CVS 1.5, so probably
+Cyclic CVS (as it was then called), which became CVS 1.5, so probably
all the changes in both (what are now) ChangeLog-9194 and
ChangeLog-9395 made it into 1.5.
free (date);
free (rcsdir);
}
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, 0, 0);
send_to_server ("add\012", 0);
return get_responses_and_close ();
}
vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
- user, 0, 0, entries, (List *) NULL);
+ user, 0, 0, entries, (RCSNode *) NULL);
if (vers->vn_user == NULL)
{
/* No entry available, ts_rcs is invalid */
(void) printf ("%s", message);
out:
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
return (0);
}
for (i = 0; i <= ac; ++i) /* XXX send -ko too with i = 0 */
send_arg (av[i]);
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
int status = 0;
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
- finfo->file, 0, 0, finfo->entries, finfo->srcfiles);
+ finfo->file, 0, 0, finfo->entries, finfo->rcs);
version = vers->vn_user;
if (version == NULL)
/* re-register with the new data */
vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL,
- file, 1, set_time, entries, (List *) NULL);
+ file, 1, set_time, entries, (RCSNode *) NULL);
if (strcmp (vers->options, "-V4") == 0)
vers->options[0] = '\0';
Register (entries, file, vers->vn_rcs, vers->ts_user,
user = argv[i];
vers = Version_TS (repository, options, tag, date, user,
- force_tag_match, 0, entries, (List *) NULL);
+ force_tag_match, 0, entries, (RCSNode *) NULL);
if (vers->ts_user == NULL)
{
line = xmalloc (strlen (user) + 15);
/* I'm not sure whether this check is redundant. */
if (!isdir (repository))
error (1, 0, "there is no repository %s", repository);
- Create_Admin (".", cp, repository, sticky ? (char *) NULL : tag,
+ Create_Admin (".", path, repository, sticky ? (char *) NULL : tag,
sticky ? (char *) NULL : date);
if (!noexec)
{
*/
Ctype
Classify_File (file, tag, date, options, force_tag_match, aflag, repository,
- entries, srcfiles, versp, update_dir, pipeout)
+ entries, rcsnode, versp, update_dir, pipeout)
char *file;
char *tag;
char *date;
int aflag;
char *repository;
List *entries;
- List *srcfiles;
+ RCSNode *rcsnode;
Vers_TS **versp;
char *update_dir;
int pipeout;
/* get all kinds of good data about the file */
vers = Version_TS (repository, options, tag, date, file,
- force_tag_match, 0, entries, srcfiles);
+ force_tag_match, 0, entries, rcsnode);
if (vers->vn_user == NULL)
{
/* CVS client-related stuff. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
#include "cvs.h"
#include "getline.h"
#include "edit.h"
#include "md5.h"
-#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS
-#ifdef HAVE_WINSOCK_H
-#include <winsock.h>
-#else /* No winsock.h */
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#endif /* No winsock.h */
-#endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS */
+#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP
+# ifdef HAVE_WINSOCK_H
+# include <winsock.h>
+# else /* No winsock.h */
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netdb.h>
+# endif /* No winsock.h */
+#endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP */
#ifdef AUTH_CLIENT_SUPPORT
char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot));
#endif /* AUTH_CLIENT_SUPPORT */
-#if HAVE_KERBEROS
+#if HAVE_KERBEROS || USE_DIRECT_TCP
#define CVS_PORT 1999
+#if HAVE_KERBEROS
#include <krb.h>
extern char *krb_realmofhost ();
#endif /* HAVE_KRB_GET_ERR_TEXT */
#endif /* HAVE_KERBEROS */
+#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */
\f
static void add_prune_candidate PROTO((char *));
static void handle_e PROTO((char *, int));
static void handle_notified PROTO((char *, int));
+static size_t try_read_from_server PROTO ((char *, size_t));
#endif /* CLIENT_SUPPORT */
\f
#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
char *filename;
{
char *newname;
+#ifdef USE_VMS_FILENAMES
+ char *p;
+#endif
read_line (&newname, 0);
+
+#ifdef USE_VMS_FILENAMES
+ /* Mogrify the filename so VMS is happy with it. */
+ for(p = newname; *p; p++)
+ if(*p == '.' || *p == '#') *p = '_';
+#endif
+
copy_file (filename, newname);
free (newname);
}
call_in_directory (args, copy_a_file, (char *)NULL);
}
\f
+
+static void read_counted_file PROTO ((char *, char *));
+
+/* Read from the server the count for the length of a file, then read
+ the contents of that file and write them to FILENAME. FULLNAME is
+ the name of the file for use in error messages. FIXME-someday:
+ extend this to deal with compressed files and make update_entries
+ use it. On error, gives a fatal error. */
+static void
+read_counted_file (filename, fullname)
+ char *filename;
+ char *fullname;
+{
+ char *size_string;
+ size_t size;
+ char *buf;
+
+ /* Pointers in buf to the place to put data which will be read,
+ and the data which needs to be written, respectively. */
+ char *pread;
+ char *pwrite;
+ /* Number of bytes left to read and number of bytes in buf waiting to
+ be written, respectively. */
+ size_t nread;
+ size_t nwrite;
+
+ FILE *fp;
+
+ read_line (&size_string, 0);
+ if (size_string[0] == 'z')
+ error (1, 0, "\
+protocol error: compressed files not supported for that operation");
+ /* FIXME: should be doing more error checking, probably. Like using
+ strtoul and making sure we used up the whole line. */
+ size = atoi (size_string);
+ free (size_string);
+
+ /* A more sophisticated implementation would use only a limited amount
+ of buffer space (8K perhaps), and read that much at a time. We allocate
+ a buffer for the whole file only to make it easy to keep track what
+ needs to be read and written. */
+ buf = xmalloc (size);
+
+ /* FIXME-someday: caller should pass in a flag saying whether it
+ is binary or not. I haven't carefully looked into whether
+ CVS/Template files should use local text file conventions or
+ not. */
+ fp = fopen (filename, "wb");
+ if (fp == NULL)
+ error (1, errno, "cannot write %s", fullname);
+ nread = size;
+ nwrite = 0;
+ pread = buf;
+ pwrite = buf;
+ while (nread > 0 || nwrite > 0)
+ {
+ size_t n;
+
+ if (nread > 0)
+ {
+ n = try_read_from_server (pread, nread);
+ nread -= n;
+ pread += n;
+ nwrite += n;
+ }
+
+ if (nwrite > 0)
+ {
+ n = fwrite (pwrite, 1, nwrite, fp);
+ if (ferror (fp))
+ error (1, errno, "cannot write %s", fullname);
+ nwrite -= n;
+ pwrite += n;
+ }
+ }
+ free (buf);
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", fullname);
+}
+\f
/*
* The Checksum response gives the checksum for the file transferred
* over by the next Updated, Merged or Patch response. We just store
else if (*tag_or_date == 'D')
date = tag_or_date + 1;
}
+ else
+ /* For cvs export, assume it is a text file. FIXME: This is
+ broken behavior--we should be having the server tell us
+ whether it is text or binary and dealing accordingly. I
+ think maybe we can parse the entries line, get the options,
+ and then ignore the entries line otherwise, but I haven't
+ checked to see whether the server sends the entries line
+ correctly in this case. */
+ options = NULL;
if (data->contents == UPDATE_ENTRIES_UPDATE
|| data->contents == UPDATE_ENTRIES_PATCH)
free (size_string);
temp_filename = xmalloc (strlen (filename) + 80);
+#ifdef USE_VMS_FILENAMES
+ /* A VMS rename of "blah.dat" to "foo" to implies a
+ destination of "foo.dat" which is unfortinate for CVS */
+ sprintf (temp_filename, "%s_new_", filename);
+#else
#ifdef _POSIX_NO_TRUNC
sprintf (temp_filename, ".new.%.9s", filename);
#else /* _POSIX_NO_TRUNC */
sprintf (temp_filename, ".new.%s", filename);
#endif /* _POSIX_NO_TRUNC */
+#endif /* USE_VMS_FILENAMES */
buf = xmalloc (size);
/* Some systems, like OS/2 and Windows NT, end lines with CRLF
flag, then we don't want to convert, else we do (because
CVS assumes text files by default). */
- /* Actually, I don't believe options can be NULL here, but I'm
- not dead certain of that. */
- if (options)
- bin = !(strcmp (options, "-kb"));
- else
- bin = 0;
+ if (options)
+ bin = !(strcmp (options, "-kb"));
+ else
+ bin = 0;
fd = open (temp_filename,
O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0),
if (gzip_pid > 0)
{
if (waitpid (gzip_pid, &gzip_status, 0) == -1)
- error (1, errno, "waiting for gzip process %d", gzip_pid);
+ error (1, errno, "waiting for gzip process %ld",
+ (long) gzip_pid);
else if (gzip_status != 0)
error (1, 0, "gzip process exited %d", gzip_status);
}
call_in_directory (pathname, clear_sticky, (char *)NULL);
}
\f
+
+static void template PROTO ((char *, List *, char *, char *));
+
+static void
+template (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ /* FIXME: should be computing second argument from CVSADM_TEMPLATE
+ and short_pathname. */
+ read_counted_file (CVSADM_TEMPLATE, "<CVS/Template file>");
+}
+
+static void handle_template PROTO ((char *, int));
+
+static void
+handle_template (pathname, len)
+ char *pathname;
+ int len;
+{
+ call_in_directory (pathname, template, NULL);
+}
+
+\f
struct save_prog {
char *name;
char *dir;
static struct save_prog *update_progs;
/*
- * Unlike some requests this doesn't include the repository. So we can't
+ * Unlike some responses this doesn't include the repository. So we can't
* just call call_in_directory and have the right thing happen; we save up
* the requests and do them at the end.
*/
cleaner if we genuinely expanded module names, all the way to a
local directory and repository, but that isn't the way it works
now. */
- send_file_names (module_argc, module_argv);
+ send_file_names (module_argc, module_argv, 0);
for (i = 0; i < modules_count; ++i)
{
rs_optional),
RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
rs_optional),
+ RSP_LINE("Template", handle_template, response_type_normal,
+ rs_optional),
RSP_LINE("Set-checkin-prog", handle_set_checkin_prog, response_type_normal,
rs_optional),
RSP_LINE("Set-update-prog", handle_set_update_prog, response_type_normal,
int just_wrtn = 0;
size_t wrtn = 0;
+#ifdef VMS
+ /* send() blocks under VMS */
+ if (send (server_sock, str + wrtn, len - wrtn, 0) < 0)
+ error (1, errno, "writing to server socket");
+#else /* VMS */
while (wrtn < len)
{
just_wrtn = send (server_sock, str + wrtn, len - wrtn, 0);
if (just_wrtn == -1)
- error (1, errno, "reading from server socket");
+ error (1, errno, "writing to server socket");
wrtn += just_wrtn;
if (wrtn == len)
break;
}
+#endif /* VMS */
}
else
#endif /* NO_SOCKET_TO_FD */
error (0, errno, "writing to to-server logfile");
}
-/*
- * Read LEN bytes from the server or die trying.
- */
-void
-read_from_server (buf, len)
- char *buf;
- size_t len;
+/* Read up to LEN bytes from the server. Returns actual number of bytes
+ read. Gives a fatal error on EOF or error. */
+static size_t
+try_read_from_server (buf, len)
+ char *buf;
+ size_t len;
{
+ int nread;
+
#ifdef NO_SOCKET_TO_FD
- if (use_socket_style)
+ if (use_socket_style)
{
- int just_red = 0;
- size_t red = 0;
+ nread = recv (server_sock, buf, len, 0);
+ if (nread == -1)
+ error (1, errno, "reading from server");
+ }
+ else
+#endif
+ {
+ nread = fread (buf, 1, len, from_server);
+ if (ferror (from_server))
+ error (1, errno, "reading from server");
+ if (feof (from_server))
+ error (1, 0,
+ "end of file from server (consult above messages if any)");
+ }
- while (red < len)
- {
- just_red = recv (server_sock, buf + red, len - red, 0);
+ /* Log, if that's what we're doing. */
+ if (from_server_logfile != NULL && nread > 0)
+ {
+ size_t towrite = nread;
+ if (fwrite (buf, 1, towrite, from_server_logfile) < towrite)
+ error (0, errno, "writing to from-server logfile");
+ }
- if (just_red == -1)
- error (1, errno, "reading from server");
+ return nread;
+}
- red += just_red;
- if (red == len)
- break;
- }
- }
- else
-#endif /* NO_SOCKET_TO_FD */
+/*
+ * Read LEN bytes from the server or die trying.
+ */
+void
+read_from_server (buf, len)
+ char *buf;
+ size_t len;
+{
+ size_t red = 0;
+ while (red < len)
{
- size_t red = 0;
-
- while (red < len)
- {
- red += fread (buf + red, 1, len - red, from_server);
-
- if (red == len)
- break;
-
- if (ferror (from_server))
- error (1, errno, "reading from server");
- if (feof (from_server))
- error (1, 0, "end of file from server (consult above messages if any)");
- }
+ red += try_read_from_server (buf + red, len - red);
+ if (red == len)
+ break;
}
-
- /* Log, if that's what we're doing. */
- if (from_server_logfile)
- if (fwrite (buf, 1, len, from_server_logfile) < len)
- error (0, errno, "writing to from-server logfile");
}
-
/*
* Get some server responses and process them. Returns nonzero for
* error, 0 for success. */
else
#endif /* NO_SOCKET_TO_FD */
{
-#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT)
+#if defined(HAVE_KERBEROS) || defined(USE_DIRECT_TCP) || defined(AUTH_CLIENT_SUPPORT)
if (server_fd != -1)
{
if (shutdown (server_fd, 1) < 0)
}
}
else
-#endif /* HAVE_KERBEROS || AUTH_CLIENT_SUPPORT */
+#endif /* HAVE_KERBEROS || USE_DIRECT_TCP || AUTH_CLIENT_SUPPORT */
#ifdef SHUTDOWN_SERVER
SHUTDOWN_SERVER (fileno (to_server));
#ifdef AUTH_CLIENT_SUPPORT
void
init_sockaddr (name, hostname, port)
- struct sockaddr_in *name;
- const char *hostname;
- unsigned short int port;
+ struct sockaddr_in *name;
+ const char *hostname;
+ unsigned short int port;
{
- struct hostent *hostinfo;
-
- memset (name, 0, sizeof (*name));
- name->sin_family = AF_INET;
- name->sin_port = htons (port);
- hostinfo = gethostbyname (hostname);
- if (hostinfo == NULL)
+ struct hostent *hostinfo;
+
+ memset (name, 0, sizeof (*name));
+ name->sin_family = AF_INET;
+ name->sin_port = htons (port);
+ hostinfo = gethostbyname (hostname);
+ if (hostinfo == NULL)
{
- fprintf (stderr, "Unknown host %s.\n", hostname);
- exit (EXIT_FAILURE);
+ fprintf (stderr, "Unknown host %s.\n", hostname);
+ exit (EXIT_FAILURE);
}
- name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
+ name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}
if (sock == -1)
{
fprintf (stderr, "socket() failed\n");
- exit (1);
+ exit (EXIT_FAILURE);
}
port_number = auth_server_port_number ();
init_sockaddr (&client_sai, server_host, port_number);
#endif /* AUTH_CLIENT_SUPPORT */
\f
-#if HAVE_KERBEROS
+#if HAVE_KERBEROS || USE_DIRECT_TCP
/*
* FIXME: this function has not been changed to deal with
* have to make take care of this.
*/
void
-start_kerberos_server (tofdp, fromfdp)
+start_tcp_server (tofdp, fromfdp)
int *tofdp, *fromfdp;
{
int tofd, fromfd;
struct hostent *hp;
char *hname;
- const char *realm;
const char *portenv;
int port;
struct sockaddr_in sin;
int s;
+
+
+#if HAVE_KERBEROS
KTEXT_ST ticket;
+ const char *realm;
+#endif /* HAVE_KERBEROS */
+
int status;
/*
hname = xmalloc (strlen (hp->h_name) + 1);
strcpy (hname, hp->h_name);
+#if HAVE_KERBEROS
realm = krb_realmofhost (hname);
+#endif /* HAVE_KERBEROS */
+ /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */
portenv = getenv ("CVS_CLIENT_PORT");
if (portenv != NULL)
{
port = atoi (portenv);
if (port <= 0)
goto try_rsh_no_message;
+ if (trace)
+ fprintf(stderr, "Using TCP port %d to contact server.\n", port);
port = htons (port);
}
else
tofd = -1;
if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
{
- error (0, errno, "kerberos connect");
+ error (0, errno, "connect");
close (s);
}
else
{
+#ifdef HAVE_KERBEROS
struct sockaddr_in laddr;
int laddrlen;
MSG_DAT msg_data;
}
else
{
+#endif /* HAVE_KERBEROS */
+
server_fd = s;
close_on_exec (server_fd);
tofd = fromfd = s;
+
+#ifdef HAVE_KERBEROS
}
+#endif /* HAVE_KERBEROS */
}
if (tofd == -1)
{
+ /* FIXME: Falling back like this is slow and we should probably
+ just make it a fatal error (so that people use the right
+ environment variables or, when we get around to implementing
+ the right ones, access methods). */
error (0, 0, "trying to start server using rsh");
try_rsh_no_message:
server_fd = -1;
*fromfdp = fromfd;
}
-#endif /* HAVE_KERBEROS */
+#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */
static int send_variable_proc PROTO ((Node *, void *));
int tofd, fromfd;
char *log = getenv ("CVS_CLIENT_LOG");
+ /* Note that generally speaking we do *not* fall back to a different
+ way of connecting if the first one does not work. This is slow
+ (*really* slow on a 14.4kbps link); the clean way to have a CVS
+ which supports several ways of connecting is with access methods. */
+
/* Init these to NULL. They will be set later if logging is on. */
from_server_logfile = (FILE *) NULL;
to_server_logfile = (FILE *) NULL;
else
#endif /* AUTH_CLIENT_SUPPORT */
{
-#if HAVE_KERBEROS
- start_kerberos_server (&tofd, &fromfd);
-#else /* ! HAVE_KERBEROS */
+#if HAVE_KERBEROS || USE_DIRECT_TCP
+ start_tcp_server (&tofd, &fromfd);
+#else
-#if ! RSH_NOT_TRANSPARENT
+# if ! RSH_NOT_TRANSPARENT
start_rsh_server (&tofd, &fromfd);
-#else /* RSH_NOT_TRANSPARENT */
+# else
-#if defined(START_SERVER)
+# if defined(START_SERVER)
START_SERVER (&tofd, &fromfd, getcaller (),
server_user, server_host, server_cvsroot);
-#endif /* defined(START_SERVER) */
-
-#endif /* ! RSH_NOT_TRANSPARENT */
-#endif /* HAVE_KERBEROS */
+# endif
+# endif
+#endif
}
+#if defined(VMS) && defined(NO_SOCKET_TO_FD)
+ /* Avoid mixing sockets with stdio */
+ use_socket_style = 1;
+ server_sock = tofd;
+#endif /* VMS && NO_SOCKET_TO_FD */
+
/* "Hi, I'm Darlene and I'll be your server tonight..." */
server_started = 1;
stored_checksum_valid = 0;
stored_mode_valid = 0;
- send_to_server ("Root ", 0);
- send_to_server (server_cvsroot, 0);
- send_to_server ("\012", 1);
+ if (strcmp (command_name, "init") != 0)
+ {
+ send_to_server ("Root ", 0);
+ send_to_server (server_cvsroot, 0);
+ send_to_server ("\012", 1);
+ }
{
struct response *rs;
send_to_server ("valid-requests\012", 0);
if (get_server_responses ())
- exit (1);
+ exit (EXIT_FAILURE);
/*
* Now handle global options.
gzip_level = 0;
}
}
+
+#ifdef FILENAMES_CASE_INSENSITIVE
+ if (supported_request ("Case"))
+ send_to_server ("Case\012", 0);
+#endif
+
/* If "Set" is not supported, just silently fail to send the variables.
Users with an old server should get a useful error message when it
fails to recognize the ${=foo} syntax. This way if someone uses
- several server, some of which are new and some old, they can still
+ several servers, some of which are new and some old, they can still
set user variables in their .cvsrc without trouble. */
if (supported_request ("Set"))
walklist (variable_list, send_variable_proc, NULL);
#endif /* LINES_CRLF_TERMINATED */
fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
+
+ /* FIXME: is there any reason to go through all this realloc'ing
+ when we could just be writing the data to the network as we read
+ it from gzip? */
while (1)
{
if ((bufp - buf) + readsize >= bufsize)
error (0, errno, "warning: can't close %s", short_pathname);
if (waitpid (gzip_pid, &gzip_status, 0) != gzip_pid)
- error (1, errno, "waiting for gzip proc %d", gzip_pid);
+ error (1, errno, "waiting for gzip proc %ld", (long) gzip_pid);
else if (gzip_status != 0)
error (1, errno, "gzip exited %d", gzip_status);
char *bufp = buf;
int len;
+ /* FIXME: This is gross. It assumes that we might read
+ less than st_size bytes (true on NT), but not more.
+ Instead of this we should just be reading a block of
+ data (e.g. 8192 bytes), writing it to the network, and
+ so on until EOF. */
while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
bufp += len;
struct file_info *finfo;
{
Vers_TS *vers;
- int update_dir_len = strlen (finfo->update_dir);
- char *short_pathname = xmalloc (update_dir_len + strlen (finfo->file) + 40);
- strcpy (short_pathname, finfo->update_dir);
- if (finfo->update_dir[0] != '\0')
- strcat (short_pathname, "/");
- strcat (short_pathname, finfo->file);
send_a_repository ("", finfo->repository, finfo->update_dir);
vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
(char *)NULL,
- finfo->file, 0, 0, finfo->entries, (List *)NULL);
+ finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL);
if (vers->vn_user != NULL)
{
else if (vers->ts_rcs == NULL
|| strcmp (vers->ts_user, vers->ts_rcs) != 0)
{
- send_modified (finfo->file, short_pathname, vers);
+ send_modified (finfo->file, finfo->fullname, vers);
}
else
{
}
freevers_ts (&vers);
- free (short_pathname);
return 0;
}
/* Send the names of all the argument files to the server. */
void
-send_file_names (argc, argv)
+send_file_names (argc, argv, flags)
int argc;
char **argv;
+ unsigned int flags;
{
int i;
char *p;
int level;
int max_level;
+ /* The fact that we do this here as well as start_recursion is a bit
+ of a performance hit. Perhaps worth cleaning up someday. */
+ if (flags & SEND_EXPAND_WILD)
+ expand_wild (argc, argv, &argc, &argv);
+
/* Send Max-dotdot if needed. */
max_level = 0;
for (i = 0; i < argc; ++i)
}
send_to_server ("\012", 1);
}
+
+ if (flags & SEND_EXPAND_WILD)
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ free (argv[i]);
+ free (argv);
+ }
}
send_dirent_proc, (DIRLEAVEPROC)NULL,
argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0);
if (err)
- exit (1);
+ exit (EXIT_FAILURE);
if (toplevel_repos == NULL)
/*
* This happens if we are not processing any files,
return unedit (argc, argv); /* Call real code */
}
+void
+send_init_command ()
+{
+ /* This is here because we need the server_cvsroot variable. */
+ send_to_server ("init ", 0);
+ send_to_server (server_cvsroot, 0);
+ send_to_server ("\012", 0);
+}
+
+int
+client_init (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return init (argc, argv); /* Call real code */
+}
+
+int
+client_annotate (argc, argv)
+ int argc;
+ char **argv;
+{
+ parse_cvsroot ();
+
+ return annotate (argc, argv); /* Call real code */
+}
#endif /* CLIENT_SUPPORT */
extern int client_editors PROTO((int argc, char **argv));
extern int client_edit PROTO((int argc, char **argv));
extern int client_unedit PROTO((int argc, char **argv));
+extern int client_init PROTO ((int argc, char **argv));
+extern int client_annotate PROTO ((int argc, char **argv));
/*
* Flag variable for seeing whether common code is running as a client
/* Send the names of all the argument files to the server. */
void
-send_file_names PROTO((int argc, char **argv));
+send_file_names PROTO((int argc, char **argv, unsigned int flags));
+
+/* Flags for send_file_names. */
+/* Expand wild cards? */
+#define SEND_EXPAND_WILD 1
/*
* Send Repository, Modified and Entry. argc and argv contain only
extern void client_expand_modules PROTO((int argc, char **argv, int local));
extern void client_send_expansions PROTO((int local));
extern void client_nonexpanded_setup PROTO((void));
+
+extern void send_init_command PROTO ((void));
\f
extern char **failed_patches;
extern int failed_patches_count;
#ifdef SERVER_SUPPORT
if (trace)
- {
+ {
char wd[PATH_MAX];
getwd (wd);
fprintf (stderr, "%c-> Create_Admin (%s, %s, %s, %s, %s) in %s\n",
(server_active) ? 'S' : ' ',
dir, update_dir, repository, tag ? tag : "",
date ? date : "", wd);
- }
+ }
#endif
if (noexec)
#ifdef SERVER_SUPPORT
if (server_active)
- server_set_sticky (update_dir, repository, tag, date);
+ {
+ server_set_sticky (update_dir, repository, tag, date);
+ server_template (update_dir, repository);
+ }
if (trace)
- {
+ {
fprintf (stderr, "%c<- Create_Admin\n",
(server_active) ? 'S' : ' ');
- }
+ }
#endif
}
extern char *strtok ();
+/* Read cvsrc, processing options matching CMDNAME ("cvs" for global
+ options, and update *ARGC and *ARGV accordingly. */
+
void
-read_cvsrc (argc, argv)
- int *argc;
- char ***argv;
+read_cvsrc (argc, argv, cmdname)
+ int *argc;
+ char ***argv;
+ char *cmdname;
{
char *homedir;
char *homeinit;
line = NULL;
line_chars_allocated = 0;
- command_len = strlen (command_name);
+ command_len = strlen (cmdname);
cvsrcfile = open_file (homeinit, "r");
while ((line_length = getline (&line, &line_chars_allocated, cvsrcfile))
>= 0)
continue;
/* stop if we match the current command */
- if (!strncmp (line, command_name, command_len)
+ if (!strncmp (line, cmdname, command_len)
&& isspace (*(line + command_len)))
{
found = 1;
static int diff_filesdoneproc PROTO((int err, char *repos, char *update_dir));
static int diff_dirleaveproc PROTO((char *dir, int err, char *update_dir));
static int diff_file_nodiff PROTO((char *file, char *repository, List *entries,
- List *srcfiles, Vers_TS *vers));
+ RCSNode *rcs, Vers_TS *vers));
static int diff_fileproc PROTO((struct file_info *finfo));
static void diff_mark_errors PROTO((int err));
if (diff_date2)
client_senddate (diff_date2);
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
#if 0
/* FIXME: We shouldn't have to send current files to diff two
revs, but it doesn't work yet and I haven't debugged it.
user_file_rev = 0;
#endif
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
- finfo->file, 1, 0, finfo->entries, finfo->srcfiles);
+ finfo->file, 1, 0, finfo->entries, finfo->rcs);
if (diff_rev2 != NULL || diff_date2 != NULL)
{
}
else if (vers->vn_user == NULL)
{
- error (0, 0, "I know nothing about %s", finfo->file);
+ error (0, 0, "I know nothing about %s", finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
empty_file = DIFF_ADDED;
else
{
- error (0, 0, "%s is a new entry, no comparison available", finfo->file);
+ error (0, 0, "%s is a new entry, no comparison available",
+ finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
empty_file = DIFF_REMOVED;
else
{
- error (0, 0, "%s was removed, no comparison available", finfo->file);
+ error (0, 0, "%s was removed, no comparison available",
+ finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
{
if (vers->vn_rcs == NULL && vers->srcfile == NULL)
{
- error (0, 0, "cannot find revision control file for %s", finfo->file);
+ error (0, 0, "cannot find revision control file for %s",
+ finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
{
if (vers->ts_user == NULL)
{
- error (0, 0, "cannot find %s", finfo->file);
+ error (0, 0, "cannot find %s", finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
}
}
- if (empty_file == DIFF_NEITHER && diff_file_nodiff (finfo->file, finfo->repository, finfo->entries, finfo->srcfiles, vers))
+ if (empty_file == DIFF_NEITHER && diff_file_nodiff (finfo->file, finfo->repository, finfo->entries, finfo->rcs, vers))
{
freevers_ts (&vers);
return (0);
/* Output an "Index:" line for patch to use */
(void) fflush (stdout);
- if (finfo->update_dir[0])
- (void) printf ("Index: %s/%s\n", finfo->update_dir, finfo->file);
- else
- (void) printf ("Index: %s\n", finfo->file);
+ (void) printf ("Index: %s\n", finfo->fullname);
(void) fflush (stdout);
tocvsPath = wrap_tocvs_process_file(finfo->file);
if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
{
+ /* This is file, not fullname, because it is the "Index:" line which
+ is supposed to contain the directory. */
(void) printf ("===================================================================\nRCS file: %s\n",
finfo->file);
(void) printf ("diff -N %s\n", finfo->file);
* verify that a file is different 0=same 1=different
*/
static int
-diff_file_nodiff (file, repository, entries, srcfiles, vers)
+diff_file_nodiff (file, repository, entries, rcs, vers)
char *file;
char *repository;
List *entries;
- List *srcfiles;
+ RCSNode *rcs;
Vers_TS *vers;
{
Vers_TS *xvers;
else
{
xvers = Version_TS (repository, (char *) NULL, diff_rev1,
- diff_date1, file, 1, 0, entries, srcfiles);
+ diff_date1, file, 1, 0, entries, rcs);
if (xvers->vn_rcs == NULL)
{
/* Don't gripe if it doesn't exist, just ignore! */
else
{
xvers = Version_TS (repository, (char *) NULL, diff_rev2,
- diff_date2, file, 1, 0, entries, srcfiles);
+ diff_date2, file, 1, 0, entries, rcs);
if (xvers->vn_rcs == NULL)
{
/* Don't gripe if it doesn't exist, just ignore! */
if (local)
send_arg ("-l");
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
if (fclose (fp) < 0)
{
if (finfo->update_dir[0] == '\0')
- error (0, errno, "cannot close %s", finfo->file);
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
else
- error (0, errno, "cannot close %s/%s", finfo->update_dir, finfo->file);
+ error (0, errno, "cannot close %s/%s", finfo->update_dir,
+ CVSADM_NOTIFY);
}
xchmod (finfo->file, 1);
if (xcmp (finfo->file, basefilename) != 0)
{
- if (finfo->update_dir[0] != '\0')
- printf ("%s/", finfo->update_dir);
- printf ("%s has been modified; revert changes? ", finfo->file);
+ printf ("%s has been modified; revert changes? ", finfo->fullname);
if (!yesno ())
{
/* "no". */
if (fclose (fp) < 0)
{
if (finfo->update_dir[0] == '\0')
- error (0, errno, "cannot close %s", finfo->file);
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
else
- error (0, errno, "cannot close %s/%s", finfo->update_dir, finfo->file);
+ error (0, errno, "cannot close %s/%s", finfo->update_dir,
+ CVSADM_NOTIFY);
}
xchmod (finfo->file, 0);
if (them == NULL)
return 0;
- if (finfo->update_dir[0] == '\0')
- printf ("%s", finfo->file);
- else
- printf ("%s/%s", finfo->update_dir, finfo->file);
+ fputs (finfo->fullname, stdout);
p = them;
while (1)
if (local)
send_arg ("-l");
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
#endif
/* hashlookup to see if it is there */
- if ((node = findnode (list, fname)) != NULL)
+ if ((node = findnode_fn (list, fname)) != NULL)
{
delnode (node); /* delete the node */
#ifdef SERVER_SUPPORT
Node *p;
/* was it already there? */
- if ((p = findnode (list, entdata->user)) != NULL)
+ if ((p = findnode_fn (list, entdata->user)) != NULL)
{
/* take it out */
delnode (p);
/* Print the program name and error message MESSAGE, which is a printf-style
format string with optional args.
If ERRNUM is nonzero, print its corresponding system error message.
- Exit with status STATUS if it is nonzero. */
+ Exit with status EXIT_FAILURE if STATUS is nonzero. */
/* VARARGS */
void
#if defined (HAVE_VPRINTF) && __STDC__
{
if (cleanup_fn)
(*cleanup_fn) ();
- exit (status);
+ exit (EXIT_FAILURE);
}
}
/* Print the program name and error message MESSAGE, which is a printf-style
format string with optional args to the file specified by FP.
If ERRNUM is nonzero, print its corresponding system error message.
- Exit with status STATUS if it is nonzero. */
+ Exit with status EXIT_FAILURE if STATUS is nonzero. */
/* VARARGS */
void
#if defined (HAVE_VPRINTF) && __STDC__
{
if (cleanup_fn)
(*cleanup_fn) ();
- exit (status);
+ exit (EXIT_FAILURE);
}
}
#include <sys/types.h>
static char *expand_variable PROTO((char *env, char *file, int line));
-extern char *xmalloc ();
-extern void free ();
\f
/* User variables. */
fileattr_delproc (node)
Node *node;
{
+ assert (node->data != NULL);
free (node->data);
+ node->data = NULL;
}
/* Read all the attributes for the current directory into memory. */
return NULL;
p = node->data;
}
- while (1)
+ while (p)
{
if (strncmp (attrname, p, attrname_len) == 0
&& p[attrname_len] == '=')
p = fileattr_modify (node->data, attrname, attrval, '=', ';');
free (node->data);
+ node->data = NULL;
if (p == NULL)
delnode (node);
else
if (trace)
#ifdef SERVER_SUPPORT
(void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
- (server_active) ? 'S' : ' ', fname, mode);
+ (server_active) ? 'S' : ' ', fname,
+ (unsigned int) mode);
#else
- (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
+ (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname,
+ (unsigned int) mode);
#endif
if (noexec)
return;
if (noexec)
return (0);
- if (unlink (f) != 0)
+ /* For at least some unices, if root tries to unlink() a directory,
+ instead of doing something rational like returning EISDIR,
+ the system will gleefully go ahead and corrupt the filesystem.
+ So we first call isdir() to see if it is OK to call unlink(). This
+ doesn't quite work--if someone creates a directory between the
+ call to isdir() and the call to unlink(), we'll still corrupt
+ the filesystem. Where is the Unix Haters Handbook when you need
+ it? */
+ if (isdir(f))
+ return deep_remove_dir(f);
+ else
{
- /* under NEXTSTEP errno is set to return EPERM if
- * the file is a directory,or if the user is not
- * allowed to read or write to the file.
- * [This is probably a bug in the O/S]
- * other systems will return EISDIR to indicate
- * that the path is a directory.
- */
- if (errno == EISDIR || errno == EPERM)
- return deep_remove_dir (f);
- else
- /* The file wasn't a directory and some other
- * error occured
- */
- return -1;
+ if (unlink (f) != 0)
+ return -1;
}
/* We were able to remove the file from the disk */
return 0;
sprintf (buf, "%s/%s", path, dp->d_name);
- if (unlink (buf) != 0 )
+ /* See comment in unlink_file_dir explanation of why we use
+ isdir instead of just calling unlink and checking the
+ status. */
+ if (isdir(buf))
{
- if (errno == EISDIR || errno == EPERM)
+ if (deep_remove_dir(buf))
{
- if (deep_remove_dir (buf))
- {
- closedir (dirp);
- return -1;
- }
+ closedir(dirp);
+ return -1;
}
- else
+ }
+ else
+ {
+ if (unlink (buf) != 0)
{
- /* buf isn't a directory, or there are
- * some sort of permision problems
- */
- closedir (dirp);
+ closedir(dirp);
return -1;
}
}
{
return getenv ("HOME");
}
+
+/* See cvs.h for description. On unix this does nothing, because the
+ shell expands the wildcards. */
+void
+expand_wild (argc, argv, pargc, pargv)
+ int argc;
+ char **argv;
+ int *pargc;
+ char ***pargv;
+{
+ int i;
+ *pargc = argc;
+ *pargv = (char **) xmalloc (argc * sizeof (char *));
+ for (i = 0; i < argc; ++i)
+ (*pargv)[i] = xstrdup (argv[i]);
+}
* create a list of directories to traverse from the current directory
*/
List *
-Find_Dirs (repository, which)
+Find_Directories (repository, which)
char *repository;
int which;
{
return (0);
}
-/*
- * look up an entry in hash list table and return a pointer to the
- * node. Return NULL on error or not found.
- */
+/* Look up an entry in hash list table and return a pointer to the
+ node. Return NULL if not found. Abort with a fatal error for
+ errors. */
Node *
findnode (list, key)
List *list;
if ((list == (List *) NULL))
return ((Node *) NULL);
+ assert (key != NULL);
+
head = list->hasharray[hashp (key)];
if (head == (Node *) NULL)
/* Not found. */
{
Node *head, *p;
+ /* This probably should be "assert (list != NULL)" (or if not we
+ should document the current behavior), but only if we check all
+ the callers to see if any are relying on this behavior. */
if (list == (List *) NULL)
return ((Node *) NULL);
+ assert (key != NULL);
+
head = list->hasharray[hashp (key)];
if (head == (Node *) NULL)
return ((Node *) NULL);
time_t t;
vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL,
- hr->file, 1, 0, (List *) NULL, (List *) NULL);
+ hr->file, 1, 0, (List *) NULL, (RCSNode *) NULL);
if (vers->vn_rcs)
{
if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0))
char *tocvsPath;
vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
- 1, 0, (List *) NULL, (List *) NULL);
+ 1, 0, (List *) NULL, (RCSNode *) NULL);
if (vers->vn_rcs != NULL
&& !RCS_isdead(vers->srcfile, vers->vn_rcs))
{
if (tmpdir == NULL || tmpdir[0] == '\0')
tmpdir = "/tmp";
- (void) sprintf (xtmpfile, "%s/cvs-imp%d", tmpdir, getpid());
+ (void) sprintf (xtmpfile, "%s/cvs-imp%ld", tmpdir, (long) getpid());
/*
* The rcs file does have a revision on the vendor branch. Compare
return (1);
}
vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
- 1, 0, (List *) NULL, (List *) NULL);
+ 1, 0, (List *) NULL, (RCSNode *) NULL);
for (i = 0; i < targc; i++)
{
if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0)
#ifndef HAVE_RCS5
char altdate2[50];
#endif
- char *author, *buf;
+ char *author;
int i, ierrno, err = 0;
mode_t mode;
char *tocvsPath;
if (noexec)
return (0);
-#ifdef LINES_CRLF_TERMINATED
- /* There exits a port of RCS to such a system that stores files with
- straight newlines. If we ever reach this point on such a system,
- we'll need to decide what to do with the open_file call below. */
- abort ();
-#endif
+ /* FIXME? We always import files as text files (note that means
+ that files get stored with straight linefeeds). There isn't an
+ obvious, clean, way to let people specify which files are binary.
+ Maybe based on the file name.... */
tocvsPath = wrap_tocvs_process_file (user);
userfile = (tocvsPath == NULL ? user : tocvsPath);
fpuser = fopen (userfile, "r");
- if (fpuser == NULL) {
+ if (fpuser == NULL)
+ {
/* not fatal, continue import */
fperror (logfp, 0, errno, "ERROR: cannot read file %s", userfile);
error (0, errno, "ERROR: cannot read file %s", userfile);
goto read_error;
}
- fprcs = fopen (rcs, "w+");
- if (fprcs == NULL) {
+ fprcs = fopen (rcs, "w+b");
+ if (fprcs == NULL)
+ {
ierrno = errno;
goto write_error_noclose;
}
/*
* putadmin()
*/
- if (fprintf (fprcs, "head %s;\n", vhead) < 0 ||
- fprintf (fprcs, "branch %s;\n", vbranch) < 0 ||
- fprintf (fprcs, "access ;\n") < 0 ||
+ if (fprintf (fprcs, "head %s;\012", vhead) < 0 ||
+ fprintf (fprcs, "branch %s;\012", vbranch) < 0 ||
+ fprintf (fprcs, "access ;\012") < 0 ||
fprintf (fprcs, "symbols ") < 0)
{
goto write_error;
if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) < 0)
goto write_error;
- if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) < 0 ||
- fprintf (fprcs, "locks ; strict;\n") < 0 ||
+ if (fprintf (fprcs, "%s:%s;\012", vtag, vbranch) < 0 ||
+ fprintf (fprcs, "locks ; strict;\012") < 0 ||
/* XXX - make sure @@ processing works in the RCS file */
- fprintf (fprcs, "comment @%s@;\n", get_comment (user)) < 0)
+ fprintf (fprcs, "comment @%s@;\012", get_comment (user)) < 0)
{
goto write_error;
}
if (keyword_opt != NULL)
- if (fprintf (fprcs, "expand @%s@;\n", keyword_opt) < 0)
+ if (fprintf (fprcs, "expand @%s@;\012", keyword_opt) < 0)
{
goto write_error;
}
- if (fprintf (fprcs, "\n") < 0)
+ if (fprintf (fprcs, "\012") < 0)
goto write_error;
/*
#endif
author = getcaller ();
- if (fprintf (fprcs, "\n%s\n", vhead) < 0 ||
- fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ if (fprintf (fprcs, "\012%s\012", vhead) < 0 ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate1, author) < 0 ||
- fprintf (fprcs, "branches %s.1;\n", vbranch) < 0 ||
- fprintf (fprcs, "next ;\n") < 0 ||
- fprintf (fprcs, "\n%s.1\n", vbranch) < 0 ||
- fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ fprintf (fprcs, "branches %s.1;\012", vbranch) < 0 ||
+ fprintf (fprcs, "next ;\012") < 0 ||
+ fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate2, author) < 0 ||
- fprintf (fprcs, "branches ;\n") < 0 ||
- fprintf (fprcs, "next ;\n\n") < 0 ||
+ fprintf (fprcs, "branches ;\012") < 0 ||
+ fprintf (fprcs, "next ;\012\012") < 0 ||
/*
* putdesc()
*/
- fprintf (fprcs, "\ndesc\n") < 0 ||
- fprintf (fprcs, "@@\n\n\n") < 0 ||
+ fprintf (fprcs, "\012desc\012") < 0 ||
+ fprintf (fprcs, "@@\012\012\012") < 0 ||
/*
* putdelta()
*/
- fprintf (fprcs, "\n%s\n", vhead) < 0 ||
- fprintf (fprcs, "log\n") < 0 ||
- fprintf (fprcs, "@Initial revision\n@\n") < 0 ||
- fprintf (fprcs, "text\n@") < 0)
+ fprintf (fprcs, "\012%s\012", vhead) < 0 ||
+ fprintf (fprcs, "log\012") < 0 ||
+ fprintf (fprcs, "@Initial revision\012@\012") < 0 ||
+ fprintf (fprcs, "text\012@") < 0)
{
goto write_error;
}
- if (sb.st_size > 0)
+ /* Now copy over the contents of the file, expanding at signs. */
{
- off_t size;
+ unsigned char buf[8192];
+ unsigned int len;
- size = sb.st_size;
- buf = xmalloc ((int) size);
- if (fread (buf, (int) size, 1, fpuser) != 1)
- error (1, errno, "cannot read file %s for copying", user);
- if (expand_at_signs (buf, size, fprcs) < 0)
+ while (1)
{
- free (buf);
- goto write_error;
+ len = fread (buf, 1, sizeof buf, fpuser);
+ if (len == 0)
+ {
+ if (ferror (fpuser))
+ error (1, errno, "cannot read file %s for copying", user);
+ break;
+ }
+ if (expand_at_signs (buf, len, fprcs) < 0)
+ goto write_error;
}
- free (buf);
}
- if (fprintf (fprcs, "@\n\n") < 0 ||
- fprintf (fprcs, "\n%s.1\n", vbranch) < 0 ||
- fprintf (fprcs, "log\n@") < 0 ||
+ if (fprintf (fprcs, "@\012\012") < 0 ||
+ fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
+ fprintf (fprcs, "log\012@") < 0 ||
expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 ||
- fprintf (fprcs, "@\ntext\n") < 0 ||
- fprintf (fprcs, "@@\n") < 0)
+ fprintf (fprcs, "@\012text\012") < 0 ||
+ fprintf (fprcs, "@@\012") < 0)
{
goto write_error;
}
if (!isdir (repository))
#endif
{
- if (isfile (repository))
+ char rcs[PATH_MAX];
+
+ (void) sprintf (rcs, "%s%s", repository, RCSEXT);
+ if (isfile (repository) || isfile(rcs))
{
fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
repository);
else
repository[0] = '\0';
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
return (err);
}
for (i = 1; i < argc && argv[i][0] == '-'; i++)
send_arg (argv[i]);
- send_file_names (argc - i, argv + i);
+ send_file_names (argc - i, argv + i, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files to get log entries, but it
doesn't work yet and I haven't debugged it. So send the files --
it's slower but it works. gnu@cygnus.com Apr94 */
RCSNode *rcsfile;
int retcode = 0;
- p = findnode (finfo->srcfiles, finfo->file);
- if (p == NULL || (rcsfile = (RCSNode *) p->data) == NULL)
+ if ((rcsfile = finfo->rcs) == NULL)
{
/* no rcs file. What *do* we know about this file? */
p = findnode (finfo->entries, finfo->file);
* editor on the file. The header garbage in the resultant file is then
* stripped and the log message is stored in the "message" argument.
*
- * rcsinfo - is the name of a file containing lines tacked onto the end of the
- * RCS info offered to the user for editing. If specified, the '-m' flag to
- * "commit" is disabled -- users are forced to run the editor.
- *
+ * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
+ * is NULL, use the CVSADM_TEMPLATE file instead.
*/
void
do_editor (dir, messagep, repository, changes)
if (noexec || reuse_log_message)
return;
+ /* Abort creation of temp file if no editor is defined */
+ if (strcmp (Editor, "") == 0 && !editinfo_editor)
+ error(1, 0, "no editor defined, must use -e or -m");
+
/* Create a temporary file */
(void) tmpnam (fname);
again:
if (repository != NULL)
/* tack templates on if necessary */
(void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
+ else
+ {
+ FILE *tfp;
+ char buf[1024];
+ char *p;
+ size_t n;
+ size_t nwrite;
+
+ /* Why "b"? */
+ tfp = fopen (CVSADM_TEMPLATE, "rb");
+ if (tfp == NULL)
+ {
+ if (!existence_error (errno))
+ error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
+ }
+ else
+ {
+ while (!feof (tfp))
+ {
+ n = fread (buf, 1, sizeof buf, tfp);
+ nwrite = n;
+ p = buf;
+ while (nwrite > 0)
+ {
+ n = fwrite (p, 1, nwrite, fp);
+ nwrite -= n;
+ p += n;
+ }
+ if (ferror (tfp))
+ error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
+ }
+ if (fclose (tfp) < 0)
+ error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
+ }
+ }
(void) fprintf (fp,
"%s----------------------------------------------------------------------\n",
*messagep = NULL;
else
{
+ /* On NT, we might read less than st_size bytes, but we won't
+ read more. So this works. */
*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
*messagep[0] = '\0';
}
List *changes;
{
char cwd[PATH_MAX];
- FILE *pipefp, *run_popen ();
+ FILE *pipefp;
char *prog = xmalloc (MAXPROGLEN);
char *cp;
int c;
+ int pipestatus;
- /* XXX <woods@web.net> -- this is gross, ugly, and a hack! FIXME! */
/*
- * A maximum of 6 %s arguments are supported in the filter
+ * Only 1 %s argument is supported in the filter
*/
- (void) sprintf (prog, filter, title, title, title, title, title, title);
+ (void) sprintf (prog, filter, title);
if ((pipefp = run_popen (prog, "w")) == NULL)
{
if (!noexec)
(void) putc ((char) c, pipefp);
}
free (prog);
- return (pclose (pipefp));
+ pipestatus = pclose (pipefp);
+ return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0;
}
/*
static void write_dbmfile PROTO((char *temp));
#endif /* !MY_NDBM */
+/* Structure which describes an administrative file. */
+struct admin_file {
+ /* Name of the file, within the CVSROOT directory. */
+ char *filename;
+
+ /* This is a one line description of what the file is for. It is not
+ currently used, although one wonders whether it should be, somehow.
+ If NULL, then don't process this file in mkmodules (FIXME: a bit of
+ a kludge; probably should replace this with a flags field). */
+ char *errormsg;
+
+ /* Contents which the file should have in a new repository. To avoid
+ problems with brain-dead compilers which choke on long string constants,
+ this is a pointer to an array of char * terminated by NULL--each of
+ the strings is concatenated. */
+ const char * const *contents;
+};
+
+static const char *const loginfo_contents[] = {
+ "# The \"loginfo\" file is used to control where \"cvs commit\" log information\n",
+ "# is sent. The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being made to, relative to the\n",
+ "# $CVSROOT. For the first match that is found, then the remainder of the\n",
+ "# line is a filter program that should expect log information on its standard\n",
+ "# input.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in the\n",
+ "# first field of this file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ "#\n",
+ "# The filter program may use one and only one \"%s\" modifier (ala printf). If\n",
+ "# such a \"%s\" is specified in the filter program, a brief title is included\n",
+ "# (as one argument, enclosed in single quotes) showing the relative directory\n",
+ "# name and listing the modified file names.\n",
+ "#\n",
+ "# For example:\n",
+ "#DEFAULT (echo \"\"; who am i; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
+ NULL
+};
+
+static const char *const rcsinfo_contents[] = {
+ "# The \"rcsinfo\" file is used to control templates with which the editor\n",
+ "# is invoked on commit and import.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being made to, relative to the\n",
+ "# $CVSROOT. For the first match that is found, then the remainder of the\n",
+ "# line is the name of the file that contains the template.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const editinfo_contents[] = {
+ "# The \"editinfo\" file is used to allow verification of logging\n",
+ "# information. It works best when a template (as specified in the\n",
+ "# rcsinfo file) is provided for the logging procedure. Given a\n",
+ "# template with locations for, a bug-id number, a list of people who\n",
+ "# reviewed the code before it can be checked in, and an external\n",
+ "# process to catalog the differences that were code reviewed, the\n",
+ "# following test can be applied to the code:\n",
+ "#\n",
+ "# Making sure that the entered bug-id number is correct.\n",
+ "# Validating that the code that was reviewed is indeed the code being\n",
+ "# checked in (using the bug-id number or a seperate review\n",
+ "# number to identify this particular code set.).\n",
+ "#\n",
+ "# If any of the above test failed, then the commit would be aborted.\n",
+ "#\n",
+ "# Actions such as mailing a copy of the report to each reviewer are\n",
+ "# better handled by an entry in the loginfo file.\n",
+ "#\n",
+ "# One thing that should be noted is the the ALL keyword is not\n",
+ "# supported. There can be only one entry that matches a given\n",
+ "# repository.\n",
+ NULL
+};
+
+static const char *const commitinfo_contents[] = {
+ "# The \"commitinfo\" file is used to control pre-commit checks.\n",
+ "# The filter on the right is invoked with the repository and a list \n",
+ "# of files to check. A non-zero exit of the filter program will \n",
+ "# cause the commit to be aborted.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to, relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const taginfo_contents[] = {
+ "# The \"taginfo\" file is used to control pre-tag checks.\n",
+ "# The filter on the right is invoked with the following arguments:\n",
+ "#\n",
+ "# $1 -- tagname\n",
+ "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n",
+ "# $3 -- repository\n",
+ "# $4-> file revision [file revision ...]\n",
+ "#\n",
+ "# A non-zero exit of the filter program will cause the tag to be aborted.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to, relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const checkoutlist_contents[] = {
+ "# The \"checkoutlist\" file is used to support additional version controlled\n",
+ "# administrative files in $CVSROOT/CVSROOT, such as template files.\n",
+ "#\n",
+ "# The first entry on a line is a filename which will be checked out from\n",
+ "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n",
+ "# The remainder of the line is an error message to use if the file cannot\n",
+ "# be checked out.\n",
+ "#\n",
+ "# File format:\n",
+ "#\n",
+ "# [<whitespace>]<filename><whitespace><error message><end-of-line>\n",
+ "#\n",
+ "# comment lines begin with '#'\n",
+ NULL
+};
+
+static const char *const cvswrappers_contents[] = {
+ "# This file describes wrappers and other binary files to CVS.\n",
+ "#\n",
+ "# Wrappers are the concept where directories of files are to be\n",
+ "# treated as a single file. The intended use is to wrap up a wrapper\n",
+ "# into a single tar such that the tar archive can be treated as a\n",
+ "# single binary file in CVS.\n",
+ "#\n",
+ "# To solve the problem effectively, it was also necessary to be able to\n",
+ "# prevent rcsmerge from merging these files.\n",
+ "#\n",
+ "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n",
+ "#\n",
+ "# wildcard [option value][option value]...\n",
+ "#\n",
+ "# where option is one of\n",
+ "# -f from cvs filter value: path to filter\n",
+ "# -t to cvs filter value: path to filter\n",
+ "# -m update methodology value: MERGE or COPY\n",
+ "#\n",
+ "# and value is a single-quote delimited value.\n",
+ "#\n",
+ "# For example:\n",
+ NULL
+};
+
+static const char *const notify_contents[] = {
+ "# The \"notify\" file controls where notifications from watches set by\n",
+ "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n",
+ "# a regular expression which is tested against the directory that the\n",
+ "# change is being made to, relative to the $CVSROOT. If it matches,\n",
+ "# then the remainder of the line is a filter program that should contain\n",
+ "# one occurrence of %s for the user to notify, and information on its\n",
+ "# standard input.\n",
+ "#\n",
+ "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n",
+ "#\n",
+ "# For example:\n",
+ "#ALL mail %s -s \"CVS notification\"\n",
+ NULL
+};
+
+static const char *const modules_contents[] = {
+ "# Three different line formats are valid:\n",
+ "# key -a aliases...\n",
+ "# key [options] directory\n",
+ "# key [options] directory files...\n",
+ "#\n",
+ "# Where \"options\" are composed of:\n",
+ "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n",
+ "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n",
+ "# -e prog Run \"prog\" on \"cvs export\" of module.\n",
+ "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n",
+ "# -u prog Run \"prog\" on \"cvs update\" of module.\n",
+ "# -d dir Place module in directory \"dir\" instead of module name.\n",
+ "# -l Top-level directory only -- do not recurse.\n",
+ "#\n",
+ "# And \"directory\" is a path to a directory relative to $CVSROOT.\n",
+ "#\n",
+ "# The \"-a\" option specifies an alias. An alias is interpreted as if\n",
+ "# everything on the right of the \"-a\" had been typed on the command line.\n",
+ "#\n",
+ "# You can encode a module within a module by using the special '&'\n",
+ "# character to interpose another module into the current module. This\n",
+ "# can be useful for creating a module that consists of many directories\n",
+ "# spread out over the entire source repository.\n",
+ NULL
+};
+
+static const struct admin_file filelist[] = {
+ {CVSROOTADM_LOGINFO,
+ "no logging of 'cvs commit' messages is done without a %s file",
+ &loginfo_contents[0]},
+ {CVSROOTADM_RCSINFO,
+ "a %s file can be used to configure 'cvs commit' templates",
+ rcsinfo_contents},
+ {CVSROOTADM_EDITINFO,
+ "a %s file can be used to validate log messages",
+ editinfo_contents},
+ {CVSROOTADM_COMMITINFO,
+ "a %s file can be used to configure 'cvs commit' checking",
+ commitinfo_contents},
+ {CVSROOTADM_TAGINFO,
+ "a %s file can be used to configure 'cvs tag' checking",
+ taginfo_contents},
+ {CVSROOTADM_IGNORE,
+ "a %s file can be used to specify files to ignore",
+ NULL},
+ {CVSROOTADM_CHECKOUTLIST,
+ "a %s file can specify extra CVSROOT files to auto-checkout",
+ checkoutlist_contents},
+ {CVSROOTADM_WRAPPER,
+ "a %s file can be used to specify files to treat as wrappers",
+ cvswrappers_contents},
+ {CVSROOTADM_NOTIFY,
+ "a %s file can be used to specify where notifications go",
+ notify_contents},
+ {CVSROOTADM_MODULES,
+ /* modules is special-cased in mkmodules. */
+ NULL,
+ modules_contents},
+ {NULL, NULL}
+};
/* Rebuild the checked out administrative files in directory DIR. */
int
FILE *fp;
/* FIXME: arbitrary limit */
char line[512];
- static struct _checkout_file {
- char *filename;
- char *errormsg;
- } *fileptr, filelist[] = {
- {CVSROOTADM_LOGINFO,
- "no logging of 'cvs commit' messages is done without a %s file"},
- {CVSROOTADM_RCSINFO,
- "a %s file can be used to configure 'cvs commit' templates"},
- {CVSROOTADM_EDITINFO,
- "a %s file can be used to validate log messages"},
- {CVSROOTADM_COMMITINFO,
- "a %s file can be used to configure 'cvs commit' checking"},
- {CVSROOTADM_TAGINFO,
- "a %s file can be used to configure 'cvs tag' checking"},
- {CVSROOTADM_IGNORE,
- "a %s file can be used to specify files to ignore"},
- {CVSROOTADM_CHECKOUTLIST,
- "a %s file can specify extra CVSROOT files to auto-checkout"},
- {CVSROOTADM_WRAPPER,
- "a %s file can be used to specify files to treat as wrappers"},
- {CVSROOTADM_NOTIFY,
- "a %s file can be used to specify where notifications go"},
- {NULL, NULL}};
+ const struct admin_file *fileptr;
if (save_cwd (&cwd))
- exit (1);
+ exit (EXIT_FAILURE);
if (chdir (dir) < 0)
error (1, errno, "cannot chdir to %s", dir);
case -1: /* fork failed */
(void) unlink_file (temp);
- exit (1);
+ exit (EXIT_FAILURE);
/* NOTREACHED */
default:
/* Checkout the files that need it in CVSROOT dir */
for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
+ if (fileptr->errormsg == NULL)
+ continue;
make_tempfile (temp);
if (checkout_file (fileptr->filename, temp) == 0)
rename_rcsfile (temp, fileptr->filename);
}
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
return (0);
(void) rename (real, bak); /* mv loginfo .#loginfo */
(void) rename (temp, real); /* mv "temp" loginfo */
}
+\f
+const char *const init_usage[] = {
+ "Usage: %s %s\n",
+ NULL
+};
+
+/* Create directory NAME if it does not already exist; fatal error for
+ other errors. FIXME: This should be in filesubr.c or thereabouts,
+ probably. Perhaps it should be further abstracted, though (for example
+ to handle CVSUMASK where appropriate?). */
+static void
+mkdir_if_needed (name)
+ char *name;
+{
+ if (CVS_MKDIR (name, 0777) < 0)
+ {
+ if (errno != EEXIST
+#ifdef EACCESS
+ /* OS/2; see longer comment in client.c. */
+ && errno != EACCESS
+#endif
+ )
+ error (1, errno, "cannot mkdir %s", name);
+ }
+}
+
+int
+init (argc, argv)
+ int argc;
+ char **argv;
+{
+ /* Name of CVSROOT directory. */
+ char *adm;
+ /* Name of this administrative file. */
+ char *info;
+ /* Name of ,v file for this administrative file. */
+ char *info_v;
+
+ const struct admin_file *fileptr;
+
+ umask (cvsumask);
+
+ if (argc > 1)
+ usage (init_usage);
+
+ if (client_active)
+ {
+ start_server ();
+
+ ign_setup ();
+ send_init_command ();
+ return get_responses_and_close ();
+ }
+
+ /* Note: we do *not* create parent directories as needed like the
+ old cvsinit.sh script did. Few utilities do that, and a
+ non-existent parent directory is as likely to be a typo as something
+ which needs to be created. */
+ mkdir_if_needed (CVSroot);
+
+ adm = xmalloc (strlen (CVSroot) + sizeof (CVSROOTADM) + 10);
+ strcpy (adm, CVSroot);
+ strcat (adm, "/");
+ strcat (adm, CVSROOTADM);
+ mkdir_if_needed (adm);
+
+ /* This is needed by the call to "ci" below. */
+ if (chdir (adm) < 0)
+ error (1, errno, "cannot change to directory %s", adm);
+
+ /* 80 is long enough for all the administrative file names, plus
+ "/" and so on. */
+ info = xmalloc (strlen (adm) + 80);
+ info_v = xmalloc (strlen (adm) + 80);
+ for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr)
+ {
+ if (fileptr->contents == NULL)
+ continue;
+ strcpy (info, adm);
+ strcat (info, "/");
+ strcat (info, fileptr->filename);
+ strcpy (info_v, info);
+ strcat (info_v, RCSEXT);
+ if (isfile (info_v))
+ /* We will check out this file in the mkmodules step.
+ Nothing else is required. */
+ ;
+ else
+ {
+ int retcode;
+
+ if (!isfile (info))
+ {
+ FILE *fp;
+ const char * const *p;
+
+ fp = open_file (info, "w");
+ for (p = fileptr->contents; *p != NULL; ++p)
+ if (fputs (*p, fp) < 0)
+ error (1, errno, "cannot write %s", info);
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", info);
+ }
+ /* Now check the file in. FIXME: we could be using
+ add_rcs_file from import.c which is faster (if it were
+ tweaked slightly). */
+ run_setup ("%s%s -x,v/ -q -u -t-", Rcsbin, RCS_CI);
+ run_args ("-minitial checkin of %s", fileptr->filename);
+ run_arg (fileptr->filename);
+ retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ if (retcode != 0)
+ error (1, retcode == -1 ? errno : 0,
+ "failed to check in %s", info);
+ }
+ }
+
+ /* Turn on history logging by default. The user can remove the file
+ to disable it. */
+ strcpy (info, adm);
+ strcat (info, "/");
+ strcat (info, CVSROOTADM_HISTORY);
+ if (!isfile (info))
+ {
+ FILE *fp;
+
+ fp = open_file (info, "w");
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", info);
+ }
+
+ free (info);
+ free (info_v);
+
+ mkmodules (adm);
+
+ free (adm);
+ return 0;
+}
#include "cvs.h"
#include "savecwd.h"
+\f
+/* Defines related to the syntax of the modules file. */
+
+/* Options in modules file. Note that it is OK to use GNU getopt features;
+ we already are arranging to make sure we are using the getopt distributed
+ with CVS. */
+#define CVSMODULE_OPTS "+ad:i:lo:e:s:t:u:"
+
+/* Special delimiter. */
+#define CVSMODULE_SPEC '&'
+\f
struct sortrec
{
char *modname;
/* remember where we start */
if (save_cwd (&cwd))
- exit (1);
+ exit (EXIT_FAILURE);
/* copy value to our own string since if we go recursive we'll be
really screwed if we do another dbm lookup */
/* cd back to where we started */
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
/* run checkout or tag prog if appropriate */
argc = moduleargc;
argv = moduleargv;
- optind = 1;
+ optind = 0;
wid = 0;
while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1)
{
#define CVS_DIFFDATE
#endif
-/*
- * define this to enable the SETXID support (see FAQ 4D.13)
- */
+/* Define this to enable the SETXID support. The way to use this is
+ to create a group with no users in it (except perhaps cvs
+ administrators), set the cvs executable to setgid that group, chown
+ all the repository files to that group, and change all directory
+ permissions in the repository to 770. The last person to modify a
+ file will own it, but as long as directory permissions are set
+ right that won't matter. You'll need a system which inherits file
+ groups from the parent directory. I don't know how carefully this
+ has been inspected for security holes. */
+
#ifndef SETXID_SUPPORT
/* #define SETXID_SUPPORT */
#endif
-/* The client will not perform password-authentication unless you
- * explicitly ask for it. Whether to include the authenticating
- * server is set in config.h.
- */
-/* #define AUTH_CLIENT_SUPPORT 1 */
+/* Should we build the password-authenticating client? Whether to
+ include the password-authenticating _server_, on the other hand, is
+ set in config.h. */
+#define AUTH_CLIENT_SUPPORT 1
/*
* If you are working with a large remote repository and a 'cvs checkout' is
static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile));
static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch));
static int getrcskey PROTO((FILE * fp, char **keyp, char **valp));
-static int parse_rcs_proc PROTO((Node * file, void *closure));
static int checkmagic_proc PROTO((Node *p, void *closure));
static void do_branches PROTO((List * list, char *val));
static void do_symbols PROTO((List * list, char *val));
-static void rcsnode_delproc PROTO((Node * p));
static void rcsvers_delproc PROTO((Node * p));
-static List *rcslist;
-static char *repository;
-
/*
* We don't want to use isspace() from the C library because:
*
#define whitespace(c) (spacetab[(unsigned char)c] != 0)
-/*
- * Parse all the rcs files specified and return a list
- */
-List *
-RCS_parsefiles (files, xrepos)
- List *files;
- char *xrepos;
-{
- /* initialize */
- repository = xrepos;
- rcslist = getlist ();
-
- /* walk the list parsing files */
- if (walklist (files, parse_rcs_proc, NULL) != 0)
- {
- /* free the list and return NULL on error */
- dellist (&rcslist);
- return ((List *) NULL);
- }
- else
- /* return the list we built */
- return (rcslist);
-}
-
-/*
- * Parse an rcs file into a node on the rcs list
- */
-static int
-parse_rcs_proc (file, closure)
- Node *file;
- void *closure;
-{
- RCSNode *rdata;
-
- /* parse the rcs file into rdata */
- rdata = RCS_parse (file->key, repository);
-
- /* if we got a valid RCSNode back, put it on the list */
- if (rdata != (RCSNode *) NULL)
- RCS_addnode (file->key, rdata, rcslist);
-
- return (0);
-}
-
-/*
- * Add an RCSNode to a list of them.
- */
-
-void
-RCS_addnode (file, rcs, list)
- const char *file;
- RCSNode *rcs;
- List *list;
-{
- Node *p;
-
- p = getnode ();
- p->key = xstrdup (file);
- p->delproc = rcsnode_delproc;
- p->type = RCSNODE;
- p->data = (char *) rcs;
- (void) addnode (list, p);
-}
-
/*
* Parse an rcsfile given a user file name and a repository
if (getrcskey (fp, &key, &value) == -1 || key == NULL)
goto l_error;
+ if (strcmp (key, RCSDESC) == 0)
+ goto l_error;
if (strcmp (RCSHEAD, key) == 0 && value != NULL)
rdata->head = xstrdup (value);
if (getrcskey (fp, &key, &value) == -1 || key == NULL)
goto l_error;
+ if (strcmp (key, RCSDESC) == 0)
+ goto l_error;
if (strcmp (RCSBRANCH, key) == 0 && value != NULL)
{
}
-/*
- * Do the real work of parsing an RCS file
- *
- * There are no allowances for error here.
- */
-void
-RCS_reparsercsfile (rdata)
+/* Do the real work of parsing an RCS file.
+
+ On error, die with a fatal error; if it returns at all it was successful.
+
+ If PFP is NULL, close the file when done. Otherwise, leave it open
+ and store the FILE * in *PFP. */
+static void
+RCS_reparsercsfile (rdata, pfp)
RCSNode *rdata;
+ FILE **pfp;
{
FILE *fp;
char *rcsfile;
char *cp;
char *key, *value;
+ assert (rdata != NULL);
rcsfile = rdata->path;
fp = fopen(rcsfile, FOPEN_BINARY_READ);
/* if key is NULL here, then the file is missing some headers
or we had trouble reading the file. */
- if (getrcskey (fp, &key, &value) == -1 || key == NULL)
+ if (getrcskey (fp, &key, &value) == -1 || key == NULL
+ || strcmp (key, RCSDESC) == 0)
{
if (ferror(fp))
{
vnode->date = xstrdup (valp);
- /* throw away the author field */
+ /* Get author field. */
(void) getrcskey (fp, &key, &value);
+ /* FIXME: should be using errno in case of ferror. */
+ if (key == NULL || strcmp (key, "author") != 0)
+ error (1, 0, "\
+unable to parse rcs file; `author' not in the expected place");
+ vnode->author = xstrdup (value);
- /* throw away the state field */
+ /* Get state field. */
(void) getrcskey (fp, &key, &value);
- if (strcmp (key, "state") != 0)
+ /* FIXME: should be using errno in case of ferror. */
+ if (key == NULL || strcmp (key, "state") != 0)
error (1, 0, "\
unable to parse rcs file; `state' not in the expected place");
if (strcmp (value, "dead") == 0)
/* fill in the branch list (if any branches exist) */
(void) getrcskey (fp, &key, &value);
+ /* FIXME: should be handling various error conditions better. */
+ if (key != NULL && strcmp (key, RCSDESC) == 0)
+ value = NULL;
if (value != (char *) NULL)
{
vnode->branches = getlist ();
/* fill in the next field if there is a next revision */
(void) getrcskey (fp, &key, &value);
+ /* FIXME: should be handling various error conditions better. */
+ if (key != NULL && strcmp (key, RCSDESC) == 0)
+ value = NULL;
if (value != (char *) NULL)
vnode->next = xstrdup (value);
* at this point, we skip any user defined fields XXX - this is where
* we put the symbolic link stuff???
*/
+ /* FIXME: Does not correctly handle errors, e.g. from stdio. */
while ((n = getrcskey (fp, &key, &value)) >= 0)
{
+ assert (key != NULL);
+
+ if (strcmp (key, RCSDESC) == 0)
+ {
+ n = -1;
+ break;
+ }
+
/* Enable use of repositories created by certain obsolete
versions of CVS. This code should remain indefinately;
there is no procedure for converting old repositories, and
break;
}
- fclose (fp);
+ if (pfp == NULL)
+ {
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", rcsfile);
+ }
+ else
+ {
+ *pfp = fp;
+ }
rdata->flags &= ~PARTIAL;
}
-/*
- * rcsnode_delproc - free up an RCS type node
- */
-static void
-rcsnode_delproc (p)
- Node *p;
-{
- freercsnode ((RCSNode **) & p->data);
-}
-
/*
* freercsnode - free up the info for an RCSNode
*/
* o if a word starts with @, do funky rcs processing
* o strip whitespace off end of value or set value to NULL if it empty
* o return 0 since we found something besides "desc"
+ *
+ * Sets *KEYP and *VALUEP to point to storage managed by the getrcskey
+ * function; the contents are only valid until the next call to getrcskey
+ * or getrcsrev.
*/
static char *key = NULL;
}
*cur = '\0';
- /* if we got "desc", we are done with the file */
- if (strcmp (RCSDESC, key) == 0)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
-
/* skip whitespace between key and val */
while (whitespace (c))
{
}
}
+ /* The syntax for some key-value pairs is different; they
+ don't end with a semicolon. */
+ if (strcmp (key, RCSDESC) == 0
+ || strcmp (key, "text") == 0
+ || strcmp (key, "log") == 0)
+ break;
+
/* compress whitespace down to a single space */
if (whitespace (c))
{
return (0);
}
+static void getrcsrev PROTO ((FILE *fp, char **revp));
+
+/* Read an RCS revision number from FP. Put a pointer to it in *REVP;
+ it points to space managed by getrcsrev which is only good until
+ the next call to getrcskey or getrcsrev. */
+static void
+getrcsrev (fp, revp)
+ FILE *fp;
+ char **revp;
+{
+ char *cur;
+ char *max;
+ int c;
+
+ do {
+ c = getc (fp);
+ if (c == EOF)
+ /* FIXME: should be including filename in error message. */
+ error (1, errno, "cannot read rcs file");
+ } while (whitespace (c));
+
+ if (!(isdigit (c) || c == '.'))
+ /* FIXME: should be including filename in error message. */
+ error (1, 0, "error reading rcs file; revision number expected");
+
+ cur = key;
+ max = key + keysize;
+ while (isdigit (c) || c == '.')
+ {
+ if (cur >= max)
+ {
+ key = xrealloc (key, keysize + ALLOCINCR);
+ cur = key + keysize;
+ keysize += ALLOCINCR;
+ max = key + keysize;
+ }
+ *cur++ = c;
+
+ c = getc (fp);
+ if (c == EOF)
+ {
+ /* FIXME: should be including filename in error message. */
+ error (1, errno, "cannot read rcs file");
+ }
+ }
+
+ if (cur >= max)
+ {
+ key = xrealloc (key, keysize + ALLOCINCR);
+ cur = key + keysize;
+ keysize += ALLOCINCR;
+ max = key + keysize;
+ }
+ *cur = '\0';
+ *revp = key;
+}
+
/*
* process the symbols list of the rcs file
*/
/* XXX this is probably not necessary, --jtc */
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
/* If tag is "HEAD", special case to get head RCS revision */
if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0'))
}
/*
- * Given a list of RCSNodes, returns non-zero if the specified
- * revision number or symbolic tag resolves to a "branch" within the
- * rcs file.
+ * Given an RCSNode, returns non-zero if the specified revision number
+ * or symbolic tag resolves to a "branch" within the rcs file.
+ *
+ * FIXME: this is the same as RCS_nodeisbranch except for the special
+ * case for handling a null rcsnode.
*/
int
-RCS_isbranch (file, rev, srcfiles)
- char *file;
- char *rev;
- List *srcfiles;
-{
- Node *p;
+RCS_isbranch (rcs, rev)
RCSNode *rcs;
-
+ const char *rev;
+{
/* numeric revisions are easy -- even number of dots is a branch */
if (isdigit (*rev))
return ((numdots (rev) & 1) == 0);
/* assume a revision if you can't find the RCS info */
- p = findnode (srcfiles, file);
- if (p == NULL)
+ if (rcs == NULL)
return (0);
/* now, look for a match in the symbols list */
- rcs = (RCSNode *) p->data;
- return (RCS_nodeisbranch (rev, rcs));
+ return (RCS_nodeisbranch (rcs, rev));
}
/*
* take into account any magic branches as well.
*/
int
-RCS_nodeisbranch (rev, rcs)
- char *rev;
+RCS_nodeisbranch (rcs, rev)
RCSNode *rcs;
+ const char *rev;
{
int dots;
Node *p;
* for the specified *symbolic* tag. Magic branches are handled correctly.
*/
char *
-RCS_whatbranch (file, rev, srcfiles)
- char *file;
- char *rev;
- List *srcfiles;
+RCS_whatbranch (rcs, rev)
+ RCSNode *rcs;
+ const char *rev;
{
- int dots;
Node *p;
- RCSNode *rcs;
+ int dots;
/* assume no branch if you can't find the RCS info */
- p = findnode (srcfiles, file);
- if (p == NULL)
+ if (rcs == NULL)
return ((char *) NULL);
/* now, look for a match in the symbols list */
- rcs = (RCSNode *) p->data;
p = findnode (RCS_symbols(rcs), rev);
if (p == NULL)
return ((char *) NULL);
assert (rcs != NULL);
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
/* find out if the tag contains a dot, or is on the trunk */
cp = strrchr (tag, '.');
assert (rcs != NULL);
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
/* if the head is on a branch, try the branch first */
if (rcs->branch != NULL)
assert (rcs != NULL);
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
p = findnode (rcs->versions, xrev);
free (xrev);
assert (rcs != NULL);
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
/* look up the revision */
p = findnode (rcs->versions, rev);
(void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
&ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
&ftm->tm_sec);
+
+ /* If the year is from 1900 to 1999, RCS files contain only two
+ digits, and sscanf gives us a year from 0-99. If the year is
+ 2000+, RCS files contain all four digits and we subtract 1900,
+ because the tm_year field should contain years since 1900. */
+
if (ftm->tm_year > 1900)
ftm->tm_year -= 1900;
assert(rcs != NULL);
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
if (rcs->symbols_data) {
rcs->symbols = getlist ();
RCSVers *version;
if (rcs->flags & PARTIAL)
- RCS_reparsercsfile (rcs);
+ RCS_reparsercsfile (rcs, NULL);
p = findnode (rcs->versions, tag);
if (p == NULL)
version = (RCSVers *) p->data;
return (version->dead);
}
+
+/* Return the RCS keyword expansion mode. For example "b" for binary.
+ Returns a pointer into storage which is allocated and freed along with
+ the rest of the RCS information; the caller should not modify this
+ storage. Returns NULL if the RCS file does not specify a keyword
+ expansion mode; for all other errors, die with a fatal error. */
+char *
+RCS_getexpand (rcs)
+ RCSNode *rcs;
+{
+ assert (rcs != NULL);
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL);
+ return rcs->expand;
+}
+\f
+/* Stuff related to annotate command. This should perhaps be split
+ into the stuff which knows about the guts of RCS files, and the
+ command parsing type stuff. */
+
+/* Linked list of allocated blocks. Seems kind of silly to
+ reinvent the obstack wheel, and this isn't as nice as obstacks
+ in some ways, but obstacks are pretty baroque. */
+struct allocblock
+{
+ char *text;
+ struct allocblock *next;
+};
+struct allocblock *blocks;
+
+static void *block_alloc PROTO ((size_t));
+
+static void *
+block_alloc (n)
+ size_t n;
+{
+ struct allocblock *blk;
+ blk = (struct allocblock *) xmalloc (sizeof (struct allocblock));
+ blk->text = xmalloc (n);
+ blk->next = blocks;
+ blocks = blk;
+ return blk->text;
+}
+
+static void block_free PROTO ((void));
+
+static void
+block_free ()
+{
+ struct allocblock *p;
+ struct allocblock *q;
+
+ p = blocks;
+ while (p != NULL)
+ {
+ free (p->text);
+ q = p->next;
+ free (p);
+ p = q;
+ }
+ blocks = NULL;
+}
+
+struct line
+{
+ /* Text of this line, terminated by \n or \0. */
+ char *text;
+ /* Version in which it was introduced. */
+ RCSVers *vers;
+ /* Nonzero if this line ends with \n. This will always be true
+ except possibly for the last line. */
+ int has_newline;
+};
+
+struct linevector
+{
+ /* How many lines in use for this linevector? */
+ unsigned int nlines;
+ /* How many lines allocated for this linevector? */
+ unsigned int lines_alloced;
+ /* Pointer to array containing a pointer to each line. */
+ struct line **vector;
+};
+
+static void linevector_init PROTO ((struct linevector *));
+
+/* Initialize *VEC to be a linevector with no lines. */
+static void
+linevector_init (vec)
+ struct linevector *vec;
+{
+ vec->lines_alloced = 10;
+ vec->nlines = 0;
+ vec->vector = (struct line **)
+ xmalloc (vec->lines_alloced * sizeof (*vec->vector));
+}
+
+static void linevector_add PROTO ((struct linevector *vec, char *text,
+ RCSVers *vers, unsigned int pos));
+
+/* Given some text TEXT, add each of its lines to VEC before line POS
+ (where line 0 is the first line). The last line in TEXT may or may
+ not be \n terminated. All \n in TEXT are changed to \0. Set the
+ version for each of the new lines to VERS. */
+static void
+linevector_add (vec, text, vers, pos)
+ struct linevector *vec;
+ char *text;
+ RCSVers *vers;
+ unsigned int pos;
+{
+ unsigned int i;
+ unsigned int nnew;
+ char *p;
+ struct line *lines;
+
+ assert (vec->lines_alloced > 0);
+
+ /* Count the number of lines we will need to add. */
+ nnew = 1;
+ for (p = text; *p != '\0'; ++p)
+ if (*p == '\n' && p[1] != '\0')
+ ++nnew;
+ /* Allocate the struct line's. */
+ lines = block_alloc (nnew * sizeof (struct line));
+
+ /* Expand VEC->VECTOR if needed. */
+ if (vec->nlines + nnew >= vec->lines_alloced)
+ {
+ while (vec->nlines + nnew >= vec->lines_alloced)
+ vec->lines_alloced *= 2;
+ vec->vector = xrealloc (vec->vector,
+ vec->lines_alloced * sizeof (*vec->vector));
+ }
+
+ /* Make room for the new lines in VEC->VECTOR. */
+ for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i)
+ vec->vector[i] = vec->vector[i - nnew];
+
+ if (pos > vec->nlines)
+ error (1, 0, "invalid rcs file: line to add out of range");
+
+ /* Actually add the lines, to LINES and VEC->VECTOR. */
+ i = pos;
+ lines[0].text = text;
+ lines[0].vers = vers;
+ lines[0].has_newline = 0;
+ vec->vector[i++] = &lines[0];
+ for (p = text; *p != '\0'; ++p)
+ if (*p == '\n')
+ {
+ *p = '\0';
+ lines[i - pos - 1].has_newline = 1;
+ if (p[1] == '\0')
+ /* If there are no characters beyond the last newline, we
+ don't consider it another line. */
+ break;
+ lines[i - pos].text = p + 1;
+ lines[i - pos].vers = vers;
+ lines[i - pos].has_newline = 0;
+ vec->vector[i] = &lines[i - pos];
+ ++i;
+ }
+ vec->nlines += nnew;
+}
+
+static void linevector_delete PROTO ((struct linevector *, unsigned int,
+ unsigned int));
+
+/* Remove NLINES lines from VEC at position POS (where line 0 is the
+ first line). */
+static void
+linevector_delete (vec, pos, nlines)
+ struct linevector *vec;
+ unsigned int pos;
+ unsigned int nlines;
+{
+ unsigned int i;
+ unsigned int last;
+
+ last = vec->nlines - nlines;
+ for (i = pos; i < last; ++i)
+ vec->vector[i] = vec->vector[i + nlines];
+ vec->nlines -= nlines;
+}
+
+static void linevector_copy PROTO ((struct linevector *, struct linevector *));
+
+/* Copy FROM to TO, copying the vectors but not the lines pointed to. */
+static void
+linevector_copy (to, from)
+ struct linevector *to;
+ struct linevector *from;
+{
+ if (from->nlines > to->lines_alloced)
+ {
+ while (from->nlines > to->lines_alloced)
+ to->lines_alloced *= 2;
+ to->vector = (struct line **)
+ xrealloc (to->vector, to->lines_alloced * sizeof (*to->vector));
+ }
+ memcpy (to->vector, from->vector,
+ from->nlines * sizeof (*to->vector));
+ to->nlines = from->nlines;
+}
+
+static void linevector_free PROTO ((struct linevector *));
+
+/* Free storage associated with linevector (that is, the vector but
+ not the lines pointed to). */
+static void
+linevector_free (vec)
+ struct linevector *vec;
+{
+ free (vec->vector);
+}
+
+static char *month_printname PROTO ((char *));
+
+/* Given a textual string giving the month (1-12), terminated with any
+ character not recognized by atoi, return the 3 character name to
+ print it with. I do not think it is a good idea to change these
+ strings based on the locale; they are standard abbreviations (for
+ example in rfc822 mail messages) which should be widely understood.
+ Returns a pointer into static readonly storage. */
+static char *
+month_printname (month)
+ char *month;
+{
+ static const char *const months[] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ int mnum;
+
+ mnum = atoi (month);
+ if (mnum < 1 || mnum > 12)
+ return "???";
+ return (char *)months[mnum - 1];
+}
+
+static int annotate_fileproc PROTO ((struct file_info *));
+
+static int
+annotate_fileproc (finfo)
+ struct file_info *finfo;
+{
+ FILE *fp;
+ char *key;
+ char *value;
+ RCSVers *vers;
+ RCSVers *prev_vers;
+ int n;
+ int ishead;
+ Node *node;
+ struct linevector headlines;
+ struct linevector curlines;
+
+ if (finfo->rcs == NULL)
+ return (1);
+
+ /* Distinguish output for various files if we are processing
+ several files. */
+ cvs_outerr ("Annotations for ", 0);
+ cvs_outerr (finfo->fullname, 0);
+ cvs_outerr ("\n***************\n", 0);
+
+ if (!(finfo->rcs->flags & PARTIAL))
+ /* We are leaking memory by calling RCS_reparsefile again. */
+ error (0, 0, "internal warning: non-partial rcs in annotate_fileproc");
+ RCS_reparsercsfile (finfo->rcs, &fp);
+
+ ishead = 1;
+ vers = NULL;
+
+ do {
+ getrcsrev (fp, &key);
+
+ /* Stash the previous version. */
+ prev_vers = vers;
+
+ /* look up the revision */
+ node = findnode (finfo->rcs->versions, key);
+ if (node == NULL)
+ error (1, 0, "mismatch in rcs file %s between deltas and deltatexts",
+ finfo->rcs->path);
+ vers = (RCSVers *) node->data;
+
+ while ((n = getrcskey (fp, &key, &value)) >= 0)
+ {
+ if (strcmp (key, "text") == 0)
+ {
+ if (ishead)
+ {
+ char *p;
+
+ p = block_alloc (strlen (value) + 1);
+ strcpy (p, value);
+
+ linevector_init (&headlines);
+ linevector_init (&curlines);
+ linevector_add (&headlines, p, NULL, 0);
+ linevector_copy (&curlines, &headlines);
+ ishead = 0;
+ }
+ else
+ {
+ char *p;
+ char *q;
+ int op;
+ /* The RCS format throws us for a loop in that the
+ deltafrags (if we define a deltafrag as an
+ add or a delete) need to be applied in reverse
+ order. So we stick them into a linked list. */
+ struct deltafrag {
+ enum {ADD, DELETE} type;
+ unsigned long pos;
+ unsigned long nlines;
+ char *new_lines;
+ struct deltafrag *next;
+ };
+ struct deltafrag *dfhead;
+ struct deltafrag *df;
+
+ dfhead = NULL;
+ for (p = value; p != NULL && *p != '\0'; )
+ {
+ op = *p++;
+ if (op != 'a' && op != 'd')
+ /* Can't just skip over the deltafrag, because
+ the value of op determines the syntax. */
+ error (1, 0, "unrecognized operation '%c' in %s",
+ op, finfo->rcs->path);
+ df = (struct deltafrag *)
+ xmalloc (sizeof (struct deltafrag));
+ df->next = dfhead;
+ dfhead = df;
+ df->pos = strtoul (p, &q, 10);
+
+ if (p == q)
+ error (1, 0, "number expected in %s",
+ finfo->rcs->path);
+ p = q;
+ if (*p++ != ' ')
+ error (1, 0, "space expected in %s",
+ finfo->rcs->path);
+ df->nlines = strtoul (p, &q, 10);
+ if (p == q)
+ error (1, 0, "number expected in %s",
+ finfo->rcs->path);
+ p = q;
+ if (*p++ != '\012')
+ error (1, 0, "linefeed expected in %s",
+ finfo->rcs->path);
+
+ if (op == 'a')
+ {
+ unsigned int i;
+
+ df->type = ADD;
+ i = df->nlines;
+ /* The text we want is the number of lines
+ specified, or until the end of the value,
+ whichever comes first (it will be the former
+ except in the case where we are adding a line
+ which does not end in newline). */
+ for (q = p; i != 0; ++q)
+ if (*q == '\n')
+ --i;
+ else if (*q == '\0')
+ {
+ if (i != 1)
+ error (1, 0, "\
+invalid rcs file %s: premature end of value",
+ finfo->rcs->path);
+ else
+ break;
+ }
+
+ /* Copy the text we are adding into allocated
+ space. */
+ df->new_lines = block_alloc (q - p + 1);
+ strncpy (df->new_lines, p, q - p);
+ df->new_lines[q - p] = '\0';
+
+ p = q;
+ }
+ else
+ {
+ /* Correct for the fact that line numbers in RCS
+ files start with 1. */
+ --df->pos;
+
+ assert (op == 'd');
+ df->type = DELETE;
+ }
+ }
+ for (df = dfhead; df != NULL;)
+ {
+ unsigned int ln;
+
+ switch (df->type)
+ {
+ case ADD:
+ linevector_add (&curlines, df->new_lines,
+ NULL, df->pos);
+ break;
+ case DELETE:
+ if (df->pos > curlines.nlines
+ || df->pos + df->nlines > curlines.nlines)
+ error (1, 0, "\
+invalid rcs file %s (`d' operand out of range)",
+ finfo->rcs->path);
+ for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
+ curlines.vector[ln]->vers = prev_vers;
+ linevector_delete (&curlines, df->pos, df->nlines);
+ break;
+ }
+ df = df->next;
+ free (dfhead);
+ dfhead = df;
+ }
+ }
+ break;
+ }
+ }
+ if (n < 0)
+ goto l_error;
+ } while (vers->next != NULL);
+
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", finfo->rcs->path);
+
+ /* Now print out the data we have just computed. */
+ {
+ unsigned int ln;
+
+ for (ln = 0; ln < headlines.nlines; ++ln)
+ {
+ char buf[80];
+ /* Period which separates year from month in date. */
+ char *ym;
+ /* Period which separates month from day in date. */
+ char *md;
+ RCSVers *prvers;
+
+ prvers = headlines.vector[ln]->vers;
+ if (prvers == NULL)
+ prvers = vers;
+
+ sprintf (buf, "%-12s (%-8.8s ",
+ prvers->version,
+ prvers->author);
+ cvs_output (buf, 0);
+
+ /* Now output the date. */
+ ym = strchr (prvers->date, '.');
+ if (ym == NULL)
+ cvs_output ("??-???-??", 0);
+ else
+ {
+ md = strchr (ym + 1, '.');
+ if (md == NULL)
+ cvs_output ("??", 0);
+ else
+ cvs_output (md + 1, 2);
+
+ cvs_output ("-", 1);
+ cvs_output (month_printname (ym + 1), 0);
+ cvs_output ("-", 1);
+ /* Only output the last two digits of the year. Our output
+ lines are long enough as it is without printing the
+ century. */
+ cvs_output (ym - 2, 2);
+ }
+ cvs_output ("): ", 0);
+ cvs_output (headlines.vector[ln]->text, 0);
+ cvs_output ("\n", 1);
+ }
+ }
+
+ if (!ishead)
+ {
+ linevector_free (&curlines);
+ linevector_free (&headlines);
+ }
+ block_free ();
+ return 0;
+
+ l_error:
+ if (ferror (fp))
+ error (1, errno, "cannot read %s", finfo->rcs->path);
+ else
+ error (1, 0, "%s does not appear to be a valid rcs file",
+ finfo->rcs->path);
+ /* Shut up gcc -Wall. */
+ return 0;
+}
+
+static const char *const annotate_usage[] =
+{
+ "Usage: %s %s [-l] [files...]\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ NULL
+};
+
+/* Command to show the revision, date, and author where each line of a
+ file was modified. Currently it will only show the trunk, all the
+ way to the head, but it would be useful to enhance it to (a) allow
+ one to specify a revision, and display only as far as that (easy;
+ just have annotate_fileproc set all the ->vers fields to NULL when
+ you hit that revision), and (b) handle branches (not as easy, but
+ doable). The user interface for both (a) and (b) could be a -r
+ option. */
+
+int
+annotate (argc, argv)
+ int argc;
+ char **argv;
+{
+ int local = 0;
+ int c;
+
+ if (argc == -1)
+ usage (annotate_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+l")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case '?':
+ default:
+ usage (annotate_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ start_server ();
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ /* FIXME: We shouldn't have to send current files, but I'm not sure
+ whether it works. So send the files --
+ it's slower but it works. */
+ send_files (argc, argv, local, 0);
+ send_to_server ("annotate\012", 0);
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
+ 1, 0);
+}
{
char *version;
char *date;
+ char *author;
char *next;
int dead;
List *branches;
/*
* exported interfaces
*/
-List *RCS_parsefiles PROTO((List * files, char *xrepos));
RCSNode *RCS_parse PROTO((const char *file, const char *repos));
RCSNode *RCS_parsercsfile PROTO((char *rcsfile));
char *RCS_check_kflag PROTO((const char *arg));
char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date,
int force_tag_match, int return_both));
char *RCS_magicrev PROTO((RCSNode *rcs, char *rev));
-int RCS_isbranch PROTO((char *file, char *rev, List *srcfiles));
-int RCS_nodeisbranch PROTO((char *rev, RCSNode *rcs));
-char *RCS_whatbranch PROTO((char *file, char *tag, List *srcfiles));
+int RCS_isbranch PROTO((RCSNode *rcs, const char *rev));
+int RCS_nodeisbranch PROTO((RCSNode *rcs, const char *tag));
+char *RCS_whatbranch PROTO((RCSNode *rcs, const char *tag));
char *RCS_head PROTO((RCSNode * rcs));
int RCS_datecmp PROTO((char *date1, char *date2));
time_t RCS_getrevtime PROTO((RCSNode * rcs, char *rev, char *date, int fudge));
List *RCS_symbols PROTO((RCSNode *rcs));
void RCS_check_tag PROTO((const char *tag));
void freercsnode PROTO((RCSNode ** rnodep));
-void RCS_addnode PROTO((const char *file, RCSNode *rcs, List *list));
char *RCS_getbranch PROTO((RCSNode * rcs, char *tag, int force_tag_match));
int RCS_isdead PROTO((RCSNode *, const char *));
+char *RCS_getexpand PROTO ((RCSNode *));
#include "cvs.h"
#include <assert.h>
+/* For RCS file PATH, make symbolic tag TAG point to revision REV.
+ This validates that TAG is OK for a user to use. Return value is
+ -1 for error (and errno is set to indicate the error), positive for
+ error (and an error message has been printed), or zero for success. */
+
int
RCS_settag(path, tag, rev)
const char *path;
const char *tag;
const char *rev;
{
+ if (strcmp (tag, TAG_BASE) == 0
+ || strcmp (tag, TAG_HEAD) == 0)
+ {
+ /* Print the name of the tag might be considered redundant
+ with the caller, which also prints it. Perhaps this helps
+ clarify why the tag name is considered reserved, I don't
+ know. */
+ error (0, 0, "Attempt to add reserved tag name %s", tag);
+ return 1;
+ }
+
run_setup ("%s%s -x,v/ -q -N%s:%s", Rcsbin, RCS, tag, rev);
run_arg (path);
return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
List *files_by_dir = NULL;
struct recursion_frame frame;
+ expand_wild (argc, argv, &argc, &argv);
+
if (update_preload == NULL)
update_dir[0] = '\0';
else
* called with the list of sub-dirs of the current dir as args
*/
if ((which & W_LOCAL) && !isdir (CVSADM))
- dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
+ dirlist = Find_Directories ((char *) NULL, W_LOCAL);
else
addlist (&dirlist, ".");
frame.flags, frame.which, frame.aflag,
frame.readlock, frame.dosrcs);
+ /* Free the data which expand_wild allocated. */
+ for (i = 0; i < argc; ++i)
+ free (argv[i]);
+ free (argv);
return (err);
}
/* find sub-directories if we will recurse */
if (flags != R_SKIP_DIRS)
- dirlist = Find_Dirs (repository, which);
+ dirlist = Find_Directories (repository, which);
}
else
{
}
/* process the files (if any) */
- if (filelist != NULL)
+ if (filelist != NULL && fileproc)
{
struct file_info finfo_struct;
notify_check (repository, update_dir);
#endif /* CLIENT_SUPPORT */
- /* pre-parse the source files */
- if (dosrcs && repository)
- finfo_struct.srcfiles = RCS_parsefiles (filelist, repository);
- else
- finfo_struct.srcfiles = (List *) NULL;
-
finfo_struct.repository = repository;
finfo_struct.update_dir = update_dir;
finfo_struct.entries = entries;
/* clean up */
dellist (&filelist);
- dellist (&finfo_struct.srcfiles);
}
if (entries)
void *closure;
{
struct file_info *finfo = (struct file_info *)closure;
+ int ret;
+
finfo->file = p->key;
- if (fileproc != NULL)
- return fileproc (finfo);
- else
- return (0);
+ finfo->fullname = xmalloc (strlen (finfo->file)
+ + strlen (finfo->update_dir)
+ + 2);
+ finfo->fullname[0] = '\0';
+ if (finfo->update_dir[0] != '\0')
+ {
+ strcat (finfo->fullname, finfo->update_dir);
+ strcat (finfo->fullname, "/");
+ }
+ strcat (finfo->fullname, finfo->file);
+
+ if (dosrcs && repository)
+ finfo->rcs = RCS_parse (finfo->file, repository);
+ else
+ finfo->rcs = (RCSNode *) NULL;
+ ret = fileproc (finfo);
+
+ freercsnode(&finfo->rcs);
+ free (finfo->fullname);
+
+ return (ret);
}
/*
{
/* save our current directory and static vars */
if (save_cwd (&cwd))
- exit (1);
+ exit (EXIT_FAILURE);
sdirlist = dirlist;
srepository = repository;
dirlist = NULL;
/* get back to where we started and restore state vars */
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
dirlist = sdirlist;
repository = srepository;
if (strcmp(p->key, ".") != 0)
{
if (save_cwd (&cwd))
- exit (1);
+ exit (EXIT_FAILURE);
if (chdir (p->key) < 0)
error (1, errno, "could not chdir to %s", p->key);
free (save_update_dir);
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
}
ign_setup ();
if (local)
send_arg("-l");
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, local, 0);
send_to_server ("remove\012", 0);
return get_responses_and_close ();
{
if (unlink (finfo->file) < 0 && ! existence_error (errno))
{
- if (finfo->update_dir[0] == '\0')
- error (0, errno, "unable to remove %s", finfo->file);
- else
- error (0, errno, "unable to remove %s/%s", finfo->update_dir,
- finfo->file);
+ error (0, errno, "unable to remove %s", finfo->fullname);
}
}
/* else FIXME should probably act as if the file doesn't exist
}
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
- finfo->file, 0, 0, finfo->entries, finfo->srcfiles);
+ finfo->file, 0, 0, finfo->entries, finfo->rcs);
if (vers->ts_user != NULL)
{
existing_files++;
if (!quiet)
- error (0, 0, "file `%s' still in working directory", finfo->file);
+ error (0, 0, "file `%s' still in working directory",
+ finfo->fullname);
}
else if (vers->vn_user == NULL)
{
if (!quiet)
- error (0, 0, "nothing known about `%s'", finfo->file);
+ error (0, 0, "nothing known about `%s'", finfo->fullname);
}
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
{
(void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
(void) unlink_file (fname);
if (!quiet)
- error (0, 0, "removed `%s'", finfo->file);
+ error (0, 0, "removed `%s'", finfo->fullname);
#ifdef SERVER_SUPPORT
if (server_active)
else if (vers->vn_user[0] == '-')
{
if (!quiet)
- error (0, 0, "file `%s' already scheduled for removal", finfo->file);
+ error (0, 0, "file `%s' already scheduled for removal",
+ finfo->fullname);
}
else
{
Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options,
vers->tag, vers->date, vers->ts_conflict);
if (!quiet)
- error (0, 0, "scheduling `%s' for removal", finfo->file);
+ error (0, 0, "scheduling `%s' for removal", finfo->fullname);
removed_files++;
#ifdef SERVER_SUPPORT
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
- *
- * Name of Repository
- *
- * Determine the name of the RCS repository and sets "Repository" accordingly.
*/
#include "cvs.h"
+/* Determine the name of the RCS repository for directory DIR in the
+ current working directory, or for the current working directory
+ itself if DIR is NULL. Returns the name in a newly-malloc'd
+ string. On error, gives a fatal error and does not return.
+ UPDATE_DIR is the path from where cvs was invoked (for use in error
+ messages), and should contain DIR as its last component.
+ UPDATE_DIR can be NULL to signify the directory in which cvs was
+ invoked. */
+
char *
Name_Repository (dir, update_dir)
char *dir;
p->type = UPDATE;
p->delproc = tag_delproc;
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL,
- (char *) NULL, finfo->file, 0, 0, finfo->entries, finfo->srcfiles);
+ (char *) NULL, finfo->file, 0, 0, finfo->entries, finfo->rcs);
p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
if (p->data != NULL)
{
rtag_fileproc (finfo)
struct file_info *finfo;
{
- Node *p;
RCSNode *rcsfile;
char *version, *rev;
int retcode = 0;
/* find the parsed RCS data */
- p = findnode (finfo->srcfiles, finfo->file);
- if (p == NULL)
+ if ((rcsfile = finfo->rcs) == NULL)
return (1);
- rcsfile = (RCSNode *) p->data;
/*
* For tagging an RCS file which is a symbolic link, you'd best be
}
else
{
- char *oversion;
+ char *oversion;
- /*
- * As an enhancement for the case where a tag is being re-applied to
- * a large body of a module, make one extra call to RCS_getversion to
- * see if the tag is already set in the RCS file. If so, check to
- * see if it needs to be moved. If not, do nothing. This will
- * likely save a lot of time when simply moving the tag to the
- * "current" head revisions of a module -- which I have found to be a
- * typical tagging operation.
- */
- rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
- oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
- if (oversion != NULL)
- {
- int isbranch = RCS_isbranch (finfo->file, symtag, finfo->srcfiles);
-
- /*
- * if versions the same and neither old or new are branches don't
- * have to do anything
- */
- if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
- {
- free (oversion);
- free (version);
- return (0);
- }
+ /*
+ * As an enhancement for the case where a tag is being re-applied to
+ * a large body of a module, make one extra call to RCS_getversion to
+ * see if the tag is already set in the RCS file. If so, check to
+ * see if it needs to be moved. If not, do nothing. This will
+ * likely save a lot of time when simply moving the tag to the
+ * "current" head revisions of a module -- which I have found to be a
+ * typical tagging operation.
+ */
+ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
+ oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
+ if (oversion != NULL)
+ {
+ int isbranch = RCS_isbranch (finfo->rcs, symtag);
+
+ /*
+ * if versions the same and neither old or new are branches don't
+ * have to do anything
+ */
+ if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
+ {
+ free (oversion);
+ free (version);
+ return (0);
+ }
- if (!force_tag_move) { /* we're NOT going to move the tag */
- if (finfo->update_dir[0])
- (void) printf ("W %s/%s", finfo->update_dir, finfo->file);
- else
- (void) printf ("W %s", finfo->file);
-
- (void) printf (" : %s already exists on %s %s",
- symtag, isbranch ? "branch" : "version", oversion);
- (void) printf (" : NOT MOVING tag to %s %s\n",
- branch_mode ? "branch" : "version", rev);
- free (oversion);
- free (version);
- return (0);
- }
- free (oversion);
- }
- retcode = RCS_settag(rcsfile->path, symtag, rev);
+ if (!force_tag_move)
+ {
+ /* we're NOT going to move the tag */
+ (void) printf ("W %s", finfo->fullname);
+
+ (void) printf (" : %s already exists on %s %s",
+ symtag, isbranch ? "branch" : "version",
+ oversion);
+ (void) printf (" : NOT MOVING tag to %s %s\n",
+ branch_mode ? "branch" : "version", rev);
+ free (oversion);
+ free (version);
+ return (0);
+ }
+ free (oversion);
+ }
+ retcode = RCS_settag(rcsfile->path, symtag, rev);
}
if (retcode != 0)
break;
}
#endif
+
if (w == -1)
{
rc = -1;
rerrno = errno;
}
+#ifndef VMS /* status is return status */
else if (WIFEXITED (status))
rc = WEXITSTATUS (status);
else if (WIFSIGNALED (status))
}
else
rc = 1;
+#else /* VMS */
+ rc = WEXITSTATUS (status);
+#endif /* VMS */
/* restore the signals */
#ifdef POSIX_SIGNALS
remote=no
fi
+# The --keep option will eventually cause all the tests to leave around the
+# contents of the /tmp directory; right now only some implement it. Not
+# useful if you are running more than one test.
+# FIXME: need some real option parsing so this doesn't depend on the order
+# in which they are specified.
+if test x"$1" = x"--keep"; then
+ shift
+ keep=yes
+else
+ keep=no
+fi
+
# Use full path for CVS executable, so that CVS_SERVER gets set properly
# for remote.
case $1 in
PLUS='\+'
fi
+# Likewise, for ?
+QUESTION='?'
+if expr 'a?b' : "a${QUESTION}b" >/dev/null; then
+ : good, it works
+else
+ QUESTION='\?'
+fi
+
# Cause NextStep 3.3 users to lose in a more graceful fashion.
if expr 'abc
def' : 'abc
exit 1
fi
+# Warn SunOS, SysVr3.2, etc., users that they may be partially losing
+if expr 'a
+b' : 'a
+c' >/dev/null; then
+ echo 'Warning: you are using a version of expr which does not correctly'
+ echo 'match multi-line patterns. Some tests may spuriously pass.'
+ echo 'You may wish to make sure GNU expr is in your path.'
+else
+ : good, it works
+fi
+
pass ()
{
echo "PASS: $1" >>${LOGFILE}
exit 1
}
-# Usage:
-# dotest TESTNAME COMMAND OUTPUT [OUTPUT2]
-# TESTNAME is the name used in the log to identify the test.
-# COMMAND is the command to run; for the test to pass, it exits with
-# exitstatus zero.
-# OUTPUT is a regexp which is compared against the output (stdout and
-# stderr combined) from the test. It is anchored to the start and end
-# of the output, so should start or end with ".*" if that is what is desired.
-# Trailing newlines are stripped from the command's actual output before
-# matching against OUTPUT.
-# If OUTPUT2 is specified and the output matches it, then it is also
-# a pass (partial workaround for the fact that some versions of expr
-# lack \|).
-dotest ()
+# See dotest and dotest_fail for explanation (this is the parts
+# of the implementation common to the two).
+dotest_internal ()
{
- if $2 >${TESTDIR}/dotest.tmp 2>&1; then
- : so far so good
- else
- status=$?
- cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
- echo "exit status was $status" >>${LOGFILE}
- fail "$1"
- fi
# expr can't distinguish between "zero characters matched" and "no match",
# so special-case it.
if test -z "$3"; then
fi
}
-# Like dotest except exitstatus should be nonzero. Probably their
-# implementations could be unified (if I were a good enough sh script
-# writer to get the quoting right).
+# Usage:
+# dotest TESTNAME COMMAND OUTPUT [OUTPUT2]
+# TESTNAME is the name used in the log to identify the test.
+# COMMAND is the command to run; for the test to pass, it exits with
+# exitstatus zero.
+# OUTPUT is a regexp which is compared against the output (stdout and
+# stderr combined) from the test. It is anchored to the start and end
+# of the output, so should start or end with ".*" if that is what is desired.
+# Trailing newlines are stripped from the command's actual output before
+# matching against OUTPUT.
+# If OUTPUT2 is specified and the output matches it, then it is also
+# a pass (partial workaround for the fact that some versions of expr
+# lack \|).
+dotest ()
+{
+ if $2 >${TESTDIR}/dotest.tmp 2>&1; then
+ : so far so good
+ else
+ status=$?
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ echo "exit status was $status" >>${LOGFILE}
+ fail "$1"
+ fi
+ dotest_internal "$@"
+}
+
+# Like dotest except exitstatus should be nonzero.
dotest_fail ()
{
if $2 >${TESTDIR}/dotest.tmp 2>&1; then
else
: so far so good
fi
- # expr can't distinguish between "zero characters matched" and "no match",
- # so special-case it.
- if test -z "$3"; then
- if test -s ${TESTDIR}/dotest.tmp; then
- echo "** expected: " >>${LOGFILE}
- echo "$3" >>${LOGFILE}
- echo "** got: " >>${LOGFILE}
- cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
- fail "$1"
- else
- cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
- pass "$1"
- fi
+ dotest_internal "$@"
+}
+
+# Like dotest except second argument is the required exitstatus.
+dotest_status ()
+{
+ $3 >${TESTDIR}/dotest.tmp 2>&1
+ status=$?
+ if test "$status" = "$2"; then
+ : so far so good
else
- if expr "`cat ${TESTDIR}/dotest.tmp`" : \
- ${STARTANCHOR}"$3"${ENDANCHOR} >/dev/null; then
- cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
- pass "$1"
- else
- echo "** expected: " >>${LOGFILE}
- echo "$3" >>${LOGFILE}
- echo "** got: " >>${LOGFILE}
- cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
- fail "$1"
- fi
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ echo "exit status was $status; expected $2" >>${LOGFILE}
+ fail "$1"
fi
+ dotest_internal "$1" "$3" "$4" "$5"
}
# clean any old remnants
'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir added to the repository'
cd ssdir
echo ssfile >ssfile
+
+ # Trying to commit it without a "cvs add" should be an error.
+ # The "use `cvs add' to create an entry" message is the one
+ # that I consider to be more correct, but local cvs prints the
+ # "nothing known" message and noone has gotten around to fixing it.
+ dotest_fail basica-notadded "${testcvs} -q ci ssfile" \
+"${PROG} [a-z]*: use "'`cvs add'\'' to create an entry for ssfile
+'"${PROG}"' \[[a-z]* aborted\]: correct above errors first!' \
+"${PROG}"' [a-z]*: nothing known about `ssfile'\''
+'"${PROG}"' \[[a-z]* aborted\]: correct above errors first!'
+
dotest basica-4 "${testcvs} add ssfile" \
"${PROG}"' [a-z]*: scheduling file `ssfile'\'' for addition
'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently'
+ dotest_fail basica-4a "${testcvs} tag tag0 ssfile" \
+"${PROG} [a-z]*: nothing known about ssfile
+${PROG} "'\[[a-z]* aborted\]: correct the above errors first!'
cd ../..
dotest basica-5 "${testcvs} -q ci -m add-it" \
'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v
/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile
initial revision: 1.1
done'
+ dotest_fail basica-5a \
+ "${testcvs} -q tag BASE sdir/ssdir/ssfile" \
+"${PROG} [a-z]*: Attempt to add reserved tag name BASE
+${PROG} \[[a-z]* aborted\]: failed to set tag BASE to revision 1.1 in /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v"
+ dotest basica-5b "${testcvs} -q tag NOT_RESERVED" \
+'T sdir/ssdir/ssfile'
+
dotest basica-6 "${testcvs} -q update" ''
echo "ssfile line 2" >>sdir/ssdir/ssfile
+ dotest_status basica-6.2 1 "${testcvs} -q diff -c" \
+'Index: sdir/ssdir/ssfile
+===================================================================
+RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v
+retrieving revision 1\.1
+diff -c -r1\.1 ssfile
+\*\*\* ssfile [0-9/]* [0-9:]* 1\.1
+--- ssfile [0-9/]* [0-9:]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ ssfile
+'"${PLUS} ssfile line 2"
dotest basica-7 "${testcvs} -q ci -m modify-it" \
'Checking in sdir/ssdir/ssfile;
/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile
"${PROG}"' [a-z]*: nothing known about `nonexist'\''
'"${PROG}"' \[[a-z]* aborted\]: correct above errors first!'
dotest basica-8 "${testcvs} -q update" ''
+ dotest_fail basica-9 \
+ "${testcvs} -q -d /tmp/cvs-sanity/nonexist update" \
+"${PROG}: .*/tmp/cvs-sanity/cvsroot value for CVS Root found in CVS/Root
+${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting
+'"${PROG}"': you may wish to try the cvs command again without the -d option '
+
+ dotest basica-10 "${testcvs} annotate" \
+'Annotations for sdir/ssdir/ssfile
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1.1 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile
+1.2 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile line 2'
cd ..
rm -rf ${CVSROOT_DIRNAME}/first-dir
echo "FAIL: test 18-${do}-$j" | tee -a ${LOGFILE}
fi
- if test "x${do}-$j" = "xadd-add" || test "x${do}-$j" = "xrm-rm" ; then
- true
- else
- # diff -c all
- if ${CVS} diff -c >> ${LOGFILE} || [ $? = 1 ] ; then
- echo "PASS: test 19-${do}-$j" >>${LOGFILE}
- else
- echo "FAIL: test 19-${do}-$j" | tee -a ${LOGFILE}
- fi
-
- # diff -u all
- if ${CVS} diff -u >> ${LOGFILE} || [ $? = 1 ] ; then
- echo "PASS: test 20-${do}-$j" >>${LOGFILE}
- else
- echo "FAIL: test 20-${do}-$j" | tee -a ${LOGFILE}
- fi
- fi
-
cd ..
# update all.
if ${CVS} update ; then
echo "FAIL: test 24-${do}-$j" | tee -a ${LOGFILE} ; exit 1
fi
- if test "x${do}-$j" = "xadd-add" || test "x${do}-$j" = "xrm-rm" ; then
- echo "PASS: test 25-${do}-$j" >>${LOGFILE}
- else
- # diff all
- if ${CVS} diff -u >> ${LOGFILE} || [ $? = 1 ] ; then
- echo "PASS: test 25-${do}-$j" >>${LOGFILE}
- else
- echo "FAIL: test 25-${do}-$j" | tee -a ${LOGFILE}
- # FIXME; exit 1
- fi
-
- # diff all
- if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then
- echo "PASS: test 26-${do}-$j" >>${LOGFILE}
- else
- echo "FAIL: test 26-${do}-$j" | tee -a ${LOGFILE}
- # FIXME; exit 1
- fi
- fi
-
# update all.
if ${CVS} co first-dir ; then
echo "PASS: test 27-${do}-$j" >>${LOGFILE}
mv first-dir.cpy first-dir
cd first-dir
- if ${CVS} diff -u >> ${LOGFILE} || [ $? = 1 ] ; then
- echo "PASS: test 61" >>${LOGFILE}
- else
- echo "FAIL: test 61" | tee -a ${LOGFILE} ; exit 1
- fi
+ dotest 61 "${testcvs} -q diff -u" ''
if ${CVS} update ; then
echo "PASS: test 62" >>${LOGFILE}
echo 1:ancest >file1
echo 2:ancest >file2
echo 3:ancest >file3
- dotest branches-2 "${testcvs} add file1 file2 file3" \
+ echo 4:trunk-1 >file4
+ dotest branches-2 "${testcvs} add file1 file2 file3 file4" \
"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition
'"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition
'"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition
+'"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition
'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently'
dotest branches-3 "${testcvs} -q ci -m add-it" \
'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v
Checking in file3;
/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3
initial revision: 1.1
+done
+RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v
+done
+Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+initial revision: 1.1
+done'
+ echo 4:trunk-2 >file4
+ dotest branches-3.2 "${testcvs} -q ci -m trunk-before-branch" \
+'Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: 1.2; previous revision: 1.1
done'
dotest branches-4 "${testcvs} tag -b br1" "${PROG}"' [a-z]*: Tagging \.
T file1
T file2
-T file3'
+T file3
+T file4'
dotest branches-5 "${testcvs} update -r br1" \
"${PROG}"' [a-z]*: Updating \.'
echo 1:br1 >file1
echo 2:br1 >file2
+ echo 4:br1 >file4
dotest branches-6 "${testcvs} -q ci -m modify" \
'Checking in file1;
/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1
Checking in file2;
/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2
new revision: 1.1.2.1; previous revision: 1.1
+done
+Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: 1.2.2.1; previous revision: 1.2
done'
dotest branches-7 "${testcvs} -q tag -b brbr" 'T file1
T file2
-T file3'
+T file3
+T file4'
dotest branches-8 "${testcvs} -q update -r brbr" ''
echo 1:brbr >file1
+ echo 4:brbr >file4
dotest branches-9 "${testcvs} -q ci -m modify" \
'Checking in file1;
/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1
new revision: 1.1.2.1.2.1; previous revision: 1.1.2.1
+done
+Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: 1.2.2.1.2.1; previous revision: 1.2.2.1
done'
- dotest branches-10 "cat file1 file2 file3" '1:brbr
+ dotest branches-10 "cat file1 file2 file3 file4" '1:brbr
2:br1
-3:ancest'
- dotest branches-11 "${testcvs} -q update -r br1" 'U file1' 'P file1'
- dotest branches-12 "cat file1 file2 file3" '1:br1
+3:ancest
+4:brbr'
+ dotest branches-11 "${testcvs} -q update -r br1" \
+'[UP] file1
+[UP] file4'
+ dotest branches-12 "cat file1 file2 file3 file4" '1:br1
2:br1
-3:ancest'
- dotest branches-13 "${testcvs} -q update -A" '. file1
-. file2'
- dotest branches-14 "cat file1 file2 file3" '1:ancest
+3:ancest
+4:br1'
+ echo 4:br1-2 >file4
+ dotest branches-12.2 "${testcvs} -q ci -m change-on-br1" \
+'Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: 1.2.2.2; previous revision: 1.2.2.1
+done'
+ dotest branches-13 "${testcvs} -q update -A" '[UP] file1
+[UP] file2
+[UP] file4'
+ dotest branches-14 "cat file1 file2 file3 file4" '1:ancest
2:ancest
-3:ancest'
+3:ancest
+4:trunk-2'
+ echo 4:trunk-3 >file4
+ dotest branches-14.2 \
+ "${testcvs} -q ci -m trunk-change-after-branch" \
+'Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: 1.3; previous revision: 1.2
+done'
+ dotest branches-14.3 "${testcvs} log file4" \
+'
+RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v
+Working file: file4
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ brbr: 1\.2\.2\.1\.0\.2
+ br1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 6; selected revisions: 6
+description:
+----------------------------
+revision 1\.3
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1
+trunk-change-after-branch
+----------------------------
+revision 1\.2
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1
+branches: 1\.2\.2;
+trunk-before-branch
+----------------------------
+revision 1\.1
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp;
+add-it
+----------------------------
+revision 1\.2\.2\.2
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1
+change-on-br1
+----------------------------
+revision 1\.2\.2\.1
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1
+branches: 1\.2\.2\.1\.2;
+modify
+----------------------------
+revision 1\.2\.2\.1\.2\.1
+date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1
+modify
+============================================================================='
+ dotest_status branches-14.4 1 \
+ "${testcvs} diff -c -r 1.1 -r 1.3 file4" \
+'Index: file4
+===================================================================
+RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v
+retrieving revision 1\.1
+retrieving revision 1\.3
+diff -c -r1\.1 -r1\.3
+\*\*\* file4 [0-9/]* [0-9:]* 1\.1
+--- file4 [0-9/]* [0-9:]* 1\.3
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! 4:trunk-1
+--- 1 ----
+! 4:trunk-3'
+ dotest_status branches-14.5 1 \
+ "${testcvs} diff -c -r 1.1 -r 1.2.2.1 file4" \
+'Index: file4
+===================================================================
+RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1
+diff -c -r1\.1 -r1\.2\.2\.1
+\*\*\* file4 [0-9/]* [0-9:]* 1\.1
+--- file4 [0-9/]* [0-9:]* 1\.2\.2\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! 4:trunk-1
+--- 1 ----
+! 4:br1'
dotest branches-15 \
"${testcvs} update -j 1.1.2.1 -j 1.1.2.1.2.1 file1" \
'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v
>>>>>>> 1.1.2.1.2.1'
cd ..
+ if test "$keep" = yes; then
+ echo Keeping /tmp/cvs-sanity and exiting due to --keep
+ exit 0
+ fi
+
rm -rf ${CVSROOT_DIRNAME}/first-dir
rm -r first-dir
;;
rcsmerge: warning: conflicts during merge
'"${PROG}"' [a-z]*: conflicts found in a
C a
-\? dir1
-\? sdir' \
-'\? dir1
-\? sdir
+'"${QUESTION}"' dir1
+'"${QUESTION}"' sdir' \
+''"${QUESTION}"' dir1
+'"${QUESTION}"' sdir
RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v
retrieving revision 1.1
retrieving revision 1.2
cd ../../2
dotest conflicts-136 "${testcvs} -q update" \
'[UP] first-dir/abc
-\? first-dir/dir1
-\? first-dir/sdir' \
-'\? first-dir/dir1
-\? first-dir/sdir
+'"${QUESTION}"' first-dir/dir1
+'"${QUESTION}"' first-dir/sdir' \
+''"${QUESTION}"' first-dir/dir1
+'"${QUESTION}"' first-dir/sdir
[UP] first-dir/abc'
dotest conflicts-137 'test -f first-dir/abc' ''
rmdir first-dir/dir1 first-dir/sdir
${testcvs} add subdir >>${LOGFILE}
cd subdir
+ mkdir ssdir
+ ${testcvs} add ssdir >>${LOGFILE}
+
touch a b
if ${testcvs} add a b 2>>${LOGFILE} ; then
echo dirmodule first-dir/subdir >>CVSROOT/modules
echo namedmodule -d nameddir first-dir/subdir >>CVSROOT/modules
echo aliasmodule -a first-dir/subdir/a >>CVSROOT/modules
+ echo aliasnested -a first-dir/subdir/ssdir >>CVSROOT/modules
+
+ # Options must come before arguments. It is possible this should
+ # be relaxed at some point (though the result would be bizarre for
+ # -a); for now test the current behavior.
+ echo bogusalias first-dir/subdir/a -a >>CVSROOT/modules
if ${testcvs} ci -m 'add modules' CVSROOT/modules \
>>${LOGFILE} 2>&1; then
echo 'PASS: test 148' >>${LOGFILE}
exit 1
fi
cd ..
+ dotest 148a0 "${testcvs} co -c" 'CVSROOT CVSROOT
+aliasmodule -a first-dir/subdir/a
+aliasnested -a first-dir/subdir/ssdir
+bogusalias first-dir/subdir/a -a
+dirmodule first-dir/subdir
+namedmodule -d nameddir first-dir/subdir
+realmodule first-dir/subdir a'
+ # I don't know why aliasmodule isn't printed (I would have thought
+ # that it gets printed without the -a; although I'm not sure that
+ # printing expansions without options is useful).
+ dotest 148a1 "${testcvs} co -s" 'CVSROOT NONE CVSROOT
+bogusalias NONE first-dir/subdir/a -a
+dirmodule NONE first-dir/subdir
+namedmodule NONE first-dir/subdir
+realmodule NONE first-dir/subdir a'
# Test that real modules check out to realmodule/a, not subdir/a.
if ${testcvs} co realmodule >>${LOGFILE}; then
echo 'FAIL: test 154' | tee -a ${LOGFILE}
exit 1
fi
+
cd ..
- rm -rf 1 ; rm -rf ${CVSROOT_DIRNAME}/first-dir
+ rm -rf 1
+
+ mkdir 2
+ cd 2
+ dotest modules-155a0 "${testcvs} co aliasnested" \
+"${PROG} [a-z]*: Updating first-dir/subdir/ssdir"
+ dotest modules-155a1 "test -d first-dir" ''
+ dotest modules-155a2 "test -d first-dir/subdir" ''
+ dotest modules-155a3 "test -d first-dir/subdir/ssdir" ''
+ # Test that nothing extraneous got created.
+ dotest modules-155a4 "ls -1" "first-dir"
+ cd ..
+ rm -rf 2
+
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
;;
mflag)
for message in '' ' ' '
U first-dir/foobar.c'
cd first-dir
touch rootig.c defig.o envig.c optig.c notig.c
- dotest 189c "${testcvs} -q update -I optig.c" '\? notig.c'
+ dotest 189c "${testcvs} -q update -I optig.c" "${QUESTION} notig.c"
# The fact that CVS requires us to specify -I CVS here strikes me
# as a bug.
- dotest 189d "${testcvs} -q update -I ! -I CVS" '\? rootig.c
-\? defig.o
-\? envig.c
-\? optig.c
-\? notig.c'
+ dotest 189d "${testcvs} -q update -I ! -I CVS" "${QUESTION} rootig.c
+${QUESTION} defig.o
+${QUESTION} envig.c
+${QUESTION} optig.c
+${QUESTION} notig.c"
cd ..
rm -rf first-dir
dotest binfiles-4 "${testcvs} -q co first-dir" 'U first-dir/binfile'
cd first-dir
dotest binfiles-5 "cmp ../../1/binfile.dat binfile" ''
+ # Testing that sticky options is -kb is the closest thing we have
+ # to testing that binary files work right on non-unix machines
+ # (until there is automated testing for such machines, of course).
+ dotest binfiles-5.5 "${testcvs} status binfile" \
+'===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb'
cp ../../1/binfile2.dat binfile
dotest binfiles-6 "${testcvs} -q ci -m modify-it" \
'Checking in binfile;
dotest binfiles-7 "${testcvs} -q update" '[UP] binfile'
dotest binfiles-8 "cmp ../binfile2.dat binfile" ''
+ # The bugs which these test for are apparently not fixed for remote.
+ if test "$remote" = no; then
+ dotest binfiles-9 "${testcvs} -q update -A" ''
+ dotest binfiles-10 "${testcvs} -q update -kk" '[UP] binfile'
+ dotest binfiles-11 "${testcvs} -q update" ''
+ dotest binfiles-12 "${testcvs} -q update -A" '[UP] binfile'
+ dotest binfiles-13 "${testcvs} -q update -A" ''
+ fi
+
+ cd ../../2/first-dir
+ echo 'this file is $''RCSfile$' >binfile
+ dotest binfiles-14a "${testcvs} -q ci -m modify-it" \
+'Checking in binfile;
+/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile
+new revision: 1.3; previous revision: 1.2
+done'
+ dotest binfiles-14b "cat binfile" 'this file is $''RCSfile$'
+ # See binfiles-5.5 for discussion of -kb.
+ dotest binfiles-14c "${testcvs} status binfile" \
+'===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb'
+ dotest binfiles-14d "${testcvs} admin -kv binfile" \
+'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+done'
+ # cvs admin doesn't change the checked-out file or its sticky
+ # kopts. There probably should be a way which does (but
+ # what if the file is modified? And do we try to version
+ # control the kopt setting?)
+ dotest binfiles-14e "cat binfile" 'this file is $''RCSfile$'
+ dotest binfiles-14f "${testcvs} status binfile" \
+'===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb'
+ dotest binfiles-14g "${testcvs} -q update -A" '[UP] binfile'
+ dotest binfiles-14h "cat binfile" 'this file is binfile,v'
+ dotest binfiles-14i "${testcvs} status binfile" \
+'===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kv'
+
cd ../..
rm -rf ${CVSROOT_DIRNAME}/first-dir
rm -r 1 2
# Test CVS's ability to handle *info files.
dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}"
cd CVSROOT
- echo "ALL echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog" > loginfo
+ echo "ALL sh -c \"echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat >/dev/null\"" > loginfo
dotest info-2 "${testcvs} add loginfo" \
"${PROG}"' [a-z]*: scheduling file `loginfo'"'"' for addition
'"${PROG}"' [a-z]*: use '"'"'cvs commit'"'"' to add this file permanently'
{
fprintf (stderr, "descramble: unknown scrambling method\n", str);
fflush (stderr);
- exit (1);
+ exit (EXIT_FAILURE);
}
#endif /* DIAGNOSTIC */
extern void server_set_sticky PROTO((char *update_dir, char *repository,
char *tag,
char *date));
+/* Send Template response. */
+extern void server_template PROTO ((char *, char *));
extern void server_update_entries
PROTO((char *file, char *update_dir, char *repository,
#ifdef SERVER_FLOWCONTROL
/* Pause if it's convenient to avoid memory blowout */
-extern void server_check_pause PROTO((void));
+extern void server_pause_check PROTO((void));
#endif /* SERVER_FLOWCONTROL */
#endif /* SERVER_SUPPORT */
static int local = 0;
static int long_format = 0;
-static char *xfile;
-static List *xsrcfiles;
+static RCSNode *xrcsnode;
static const char *const status_usage[] =
{
if (local)
send_arg("-l");
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* XXX This should only need to send file info; the file
contents themselves will not be examined. */
send_files (argc, argv, local, 0);
Vers_TS *vers;
status = Classify_File (finfo->file, (char *) NULL, (char *) NULL, (char *) NULL,
- 1, 0, finfo->repository, finfo->entries, finfo->srcfiles, &vers,
+ 1, 0, finfo->repository, finfo->entries, finfo->rcs, &vers,
finfo->update_dir, 0);
switch (status)
{
{
char *branch = NULL;
- if (RCS_isbranch (finfo->file, edata->tag, finfo->srcfiles))
- branch = RCS_whatbranch(finfo->file, edata->tag, finfo->srcfiles);
+ if (RCS_isbranch (finfo->rcs, edata->tag))
+ branch = RCS_whatbranch(finfo->rcs, edata->tag);
(void) printf (" Sticky Tag:\t\t%s (%s: %s)\n",
edata->tag,
(void) printf ("\n Existing Tags:\n");
if (symbols)
{
- xfile = finfo->file;
- xsrcfiles = finfo->srcfiles;
+ xrcsnode = finfo->rcs;
(void) walklist (symbols, tag_list_proc, NULL);
}
else
{
char *branch = NULL;
- if (RCS_isbranch (xfile, p->key, xsrcfiles))
- branch = RCS_whatbranch(xfile, p->key, xsrcfiles) ;
+ if (RCS_isbranch (xrcsnode, p->key))
+ branch = RCS_whatbranch(xrcsnode, p->key) ;
(void) printf ("\t%-25.25s\t(%s: %s)\n", p->key,
branch ? "branch" : "revision",
* a "malloc" if the argument is NULL, but you can't depend on it. Here, I
* can *force* it.
*/
-char *
+void *
xrealloc (ptr, bytes)
- char *ptr;
+ void *ptr;
size_t bytes;
{
char *cp;
send_arg (symtag);
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
p->key = xstrdup (finfo->file);
p->type = UPDATE;
p->delproc = tag_delproc;
- vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
- finfo->file, 0, 0, finfo->entries, finfo->srcfiles);
+ vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL,
+ (char *) NULL, finfo->file, 0, 0,
+ finfo->entries, finfo->rcs);
+ if (vers->srcfile == NULL)
+ {
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", finfo->file);
+ return (1);
+ }
p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
if (p->data != NULL)
{
int retcode = 0;
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
- finfo->file, 0, 0, finfo->entries, finfo->srcfiles);
+ finfo->file, 0, 0, finfo->entries, finfo->rcs);
if ((numtag != NULL) || (date != NULL))
{
/* warm fuzzies */
if (!really_quiet)
{
- if (finfo->update_dir[0])
- (void) printf ("D %s/%s\n", finfo->update_dir, finfo->file);
- else
- (void) printf ("D %s\n", finfo->file);
+ (void) printf ("D %s\n", finfo->fullname);
}
freevers_ts (&vers);
oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
if (oversion != NULL)
{
- int isbranch = RCS_isbranch (finfo->file, symtag, finfo->srcfiles);
-
- /*
- * if versions the same and neither old or new are branches don't have
- * to do anything
- */
- if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
- {
- free (oversion);
- freevers_ts (&vers);
- return (0);
- }
-
- if (!force_tag_move) { /* we're NOT going to move the tag */
- if (finfo->update_dir[0])
- (void) printf ("W %s/%s", finfo->update_dir, finfo->file);
- else
- (void) printf ("W %s", finfo->file);
-
- (void) printf (" : %s already exists on %s %s",
- symtag, isbranch ? "branch" : "version", oversion);
- (void) printf (" : NOT MOVING tag to %s %s\n",
- branch_mode ? "branch" : "version", rev);
- free (oversion);
- freevers_ts (&vers);
- return (0);
- }
- free (oversion);
+ int isbranch = RCS_isbranch (finfo->rcs, symtag);
+
+ /*
+ * if versions the same and neither old or new are branches don't have
+ * to do anything
+ */
+ if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
+ {
+ free (oversion);
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ if (!force_tag_move)
+ {
+ /* we're NOT going to move the tag */
+ (void) printf ("W %s", finfo->fullname);
+
+ (void) printf (" : %s already exists on %s %s",
+ symtag, isbranch ? "branch" : "version", oversion);
+ (void) printf (" : NOT MOVING tag to %s %s\n",
+ branch_mode ? "branch" : "version", rev);
+ free (oversion);
+ freevers_ts (&vers);
+ return (0);
+ }
+ free (oversion);
}
if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0)
/* more warm fuzzies */
if (!really_quiet)
{
- if (finfo->update_dir[0])
- (void) printf ("T %s/%s\n", finfo->update_dir, finfo->file);
- else
- (void) printf ("T %s\n", finfo->file);
+ (void) printf ("T %s\n", finfo->fullname);
}
if (nversion != NULL)
{
- free(nversion);
+ free (nversion);
}
freevers_ts (&vers);
return (0);
struct file_info *finfo;
{
RCSNode *rcsdata;
- Node *node;
struct val_args *args = val_args_static;
char *tag;
- node = findnode (finfo->srcfiles, finfo->file);
- if (node == NULL)
+ if ((rcsdata = finfo->rcs) == NULL)
/* Not sure this can happen, after all we passed only
W_REPOS | W_ATTIC. */
return 0;
- rcsdata = (RCSNode *) node->data;
tag = RCS_gettag (rcsdata, args->name, 1, 0);
if (tag != NULL)
else
{
if (save_cwd (&cwd))
- exit (1);
+ exit (EXIT_FAILURE);
if (chdir (repository) < 0)
error (1, errno, "cannot change to %s directory", repository);
}
if (repository != NULL && repository[0] != '\0')
{
if (restore_cwd (&cwd, NULL))
- exit (1);
+ exit (EXIT_FAILURE);
free_cwd (&cwd);
}
/*
* Fill in and return a Vers_TS structure "user" is the name of the local
- * file; entries is the entries file - preparsed for our pleasure. xfiles is
- * all source code control files, preparsed for our pleasure
+ * file; entries is the entries file - preparsed for our pleasure. rcs is
+ * the current source control file - preparsed for our pleasure.
*/
Vers_TS *
Version_TS (repository, options, tag, date, user, force_tag_match,
- set_time, entries, xfiles)
+ set_time, entries, rcs)
char *repository;
char *options;
char *tag;
int force_tag_match;
int set_time;
List *entries;
- List *xfiles;
+ RCSNode *rcs;
{
Node *p;
RCSNode *rcsdata;
}
else
{
- p = findnode (entries, user);
+ p = findnode_fn (entries, user);
sdtp = (struct stickydirtag *) entries->list->data; /* list-private */
}
*/
if (options)
vers_ts->options = xstrdup (options);
- else if (sdtp && sdtp->aflag == 0)
+ else if (!vers_ts->options)
{
- if (!vers_ts->options)
+ if (sdtp && sdtp->aflag == 0)
vers_ts->options = xstrdup (sdtp->options);
+ else if (rcs != NULL)
+ {
+ /* If no keyword expansion was specified on command line,
+ use whatever was in the rcs file (if there is one). This
+ is how we, if we are the server, tell the client whether
+ a file is binary. */
+ char *rcsexpand = RCS_getexpand (rcs);
+ if (rcsexpand != NULL)
+ {
+ vers_ts->options = xmalloc (strlen (rcsexpand) + 3);
+ strcpy (vers_ts->options, "-k");
+ strcat (vers_ts->options, rcsexpand);
+ }
+ }
}
if (!vers_ts->options)
vers_ts->options = xstrdup ("");
}
/* Now look up the info on the source controlled file */
- if (xfiles != (List *) NULL)
+ if (rcs != NULL)
{
- p = findnode (xfiles, user);
- if (p != NULL)
- {
- rcsdata = (RCSNode *) p->data;
- rcsdata->refcount++;
- }
- else
- rcsdata = NULL;
+ rcsdata = rcs;
+ rcsdata->refcount++;
}
else if (repository != NULL)
rcsdata = RCS_parse (user, repository);
}
else
{
+ struct tm *tm_p;
+ struct tm local_tm;
+
vers_ts->ts_user = xmalloc (25);
- cp = asctime (gmtime (&sb.st_mtime)); /* copy in the modify time */
+ /* We want to use the same timestamp format as is stored in the
+ st_mtime. For unix (and NT I think) this *must* be universal
+ time (UT), so that files don't appear to be modified merely
+ because the timezone has changed. For VMS, or hopefully other
+ systems where gmtime returns NULL, the modification time is
+ stored in local time, and therefore it is not possible to cause
+ st_mtime to be out of sync by changing the timezone. */
+ tm_p = gmtime (&sb.st_mtime);
+ if (tm_p)
+ {
+ memcpy (&local_tm, tm_p, sizeof (local_tm));
+ cp = asctime (&local_tm); /* copy in the modify time */
+ }
+ else
+ cp = ctime (&sb.st_mtime);
+
cp[24] = 0;
(void) strcpy (vers_ts->ts_user, cp);
}
}
else
{
+ struct tm *tm_p;
+ struct tm local_tm;
ts = xmalloc (25);
- cp = asctime (gmtime (&sb.st_mtime)); /* copy in the modify time */
+ /* We want to use the same timestamp format as is stored in the
+ st_mtime. For unix (and NT I think) this *must* be universal
+ time (UT), so that files don't appear to be modified merely
+ because the timezone has changed. For VMS, or hopefully other
+ systems where gmtime returns NULL, the modification time is
+ stored in local time, and therefore it is not possible to cause
+ st_mtime to be out of sync by changing the timezone. */
+ tm_p = gmtime (&sb.st_mtime);
+ if (tm_p)
+ {
+ memcpy (&local_tm, tm_p, sizeof (local_tm));
+ cp = asctime (&local_tm); /* copy in the modify time */
+ }
+ else
+ cp = ctime(&sb.st_mtime);
+
cp[24] = 0;
(void) strcpy (ts, cp);
}
#include "cvs.h"
-char *version_string = "\nConcurrent Versions System (CVS) 1.7.2";
+char *version_string = "\nConcurrent Versions System (CVS) 1.8.1";
#ifdef CLIENT_SUPPORT
#ifdef SERVER_SUPPORT
send_arg ("-a");
send_arg ("none");
}
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
if (them == NULL)
return 0;
- if (finfo->update_dir[0] == '\0')
- printf ("%s", finfo->file);
- else
- printf ("%s/%s", finfo->update_dir, finfo->file);
+ fputs (finfo->fullname, stdout);
p = them;
while (1)
if (local)
send_arg ("-l");
- send_file_names (argc, argv);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
--- /dev/null
+Sun Apr 14 11:07:43 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * .cvsignore: new file.
+
+ * Makefile.in (subdir): `tools', not `contrib'.
+
+ * Added ChangeLog (this file), and subdir `pcl-cvs'.
--- /dev/null
+# Makefile for GNU CVS auxiliary tools.
+# Do not use this makefile directly, but only from `../Makefile'.
+# Copyright (C) 1986, 1988-1990 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# $CVSid: @(#)Makefile.in 1.6 94/10/22 $
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# Where to put the manual pages.
+mandir = $(prefix)/man
+
+# Use cp if you don't have install.
+INSTALL = @INSTALL@
+
+DISTFILES = ChangeLog README .cvsignore Makefile.in
+
+all: Makefile
+.PHONY: all
+
+install: all
+ @echo "pcl-cvs not installed"
+.PHONY: install
+
+tags:
+.PHONY: tags
+
+TAGS:
+.PHONY: TAGS
+
+ls:
+ @echo $(DISTFILES)
+.PHONY: ls
+
+clean:
+ /bin/rm -f *.o core
+.PHONY: clean
+
+distclean: clean
+ rm -f Makefile pcl-cvs/Makefile
+.PHONY: distclean
+
+realclean: distclean
+.PHONY: realclean
+
+dist-dir:
+ mkdir ${DISTDIR}
+ for i in ${DISTFILES}; do \
+ ln $(srcdir)/$${i} ${DISTDIR}; \
+ done
+ cd pcl-cvs; ${MAKE} dist-dir DISTDIR="../${DISTDIR}/pcl-cvs"
+.PHONY: dist-dir
+
+subdir = tools
+Makefile: ../config.status Makefile.in
+ cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
--- /dev/null
+ This subdirectory contains tools that can be used with CVS.
+Note that they will not necessarily be installed when you "make
+install" from the top-level of the CVS source tree.
+
+pcl-cvs ............................. an Emacs interface to CVS.
--- /dev/null
+Makefile
+pcl-cvs.info*
+pcl-cvs.aux
+pcl-cvs.cp
+pcl-cvs.cps
+pcl-cvs.dvi
+pcl-cvs.fn
+pcl-cvs.fns
+pcl-cvs.ky
+pcl-cvs.kys
+pcl-cvs.log
+pcl-cvs.pg
+pcl-cvs.pgs
+pcl-cvs.toc
+pcl-cvs.tp
+pcl-cvs.tps
+pcl-cvs.vr
+pcl-cvs.vrs
+pcl-cvs.ps
--- /dev/null
+Mon Apr 15 01:34:27 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * Makefile: removed. Why was this in the repository in the first
+ place?
+
+ * Makefile.in (BATCHFLAGS): don't pass -q to Emacs when compiling,
+ because Emacs probably can't find Elib's cookie.el[c] if we do
+ that. (Actually, it still can't, but that may be due to a bug in
+ Emacs).
+
+ * INSTALL: reflect changed location of elib in the CVS dist.
+
+Sun Apr 14 12:18:12 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * pcl-cvs.el (cookie): Changed "(load \"cookie\")" to
+ "(require 'cookie)", since elib is now included again.
+ Moved "(provide 'pcl-cvs)" to the end of the file, so it's not
+ provided if the package didn't load successfully.
+
+ * Makefile.in (subdir): tools/pcl-cvs now, not contrib/pcl-cvs.
+ (BATCHFLAGS): removed "-n" from BATCHFLAGS. Emacs 19.30 does not
+ know about this flag.
+ (OBJDIR_DISTFILES): don't include .elc files here, add comment
+ explaining why.
+
+Thu Apr 11 20:22:34 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * pcl-cvs.el (cvs-mode-map): conform to Emacs 19 keybinding
+ conventions by not binding any regular letters under C-c.
+
+Fri Feb 9 14:29:07 1996 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * Makefile.in (mostlyclean clean realclean): Remove 'realclean'
+ from this target list; it's already defined later in the file.
+
+Tue Jan 23 13:02:24 1996 Greg A. Woods <woods@most.weird.com>
+
+ * pcl-cvs.el (pcl-cvs-bugs-address): change the default address
+ as suggested by Per Cederqvist.
+ * pcl-cvs.el: removed comments refering to Signum, etc.
+
+Sun Jan 21 12:51:12 1996 Greg A. Woods <woods@most.weird.com>
+
+ * pcl-cvs.el (cvs-parse-stderr): fix typo (missing '\') that was
+ causing occasional un-reported, un-traced, failures that simply
+ said something like "RE missing '\(' or '\\('" -- hopefully this
+ is the last such bug!
+
+Tue Jan 16 13:57:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in: Rename "dist" target back to "dist-dir". The
+ latter is what actually gets used.
+ (pcl-cvs.dvi): Restore srcdir to pcl-cvs.texinfo. Fix typo
+ (pcl-cvs.texifo -> pcl-cvs.texinfo).
+ (TEXINDEX,TEX,SET_TEXINPUTS): New variables.
+ (.el.elc): Copy .el file to build dir so .elc file gets put there.
+ (dist-dir): Fix typo (cvs.info -> pcl-cvs.info).
+ * cookie.el: New file, copied from elib 1.0.
+ * README: Remove note about requiring elib; it claimed that CVS
+ contained a copy of elib, but it lied.
+ * pcl-cvs.el: Change (require 'cookie) to (load "cookie.el").
+ * pcl-cvs-lucid.el: Change (require 'pcl-cvs) to (load "pcl-cvs.el").
+
+Fri Jan 12 10:32:14 1996 Greg A. Woods <woods@most.weird.com>
+
+ * pcl-cvs.elc, pcl-cvs-lucid.elc: removed
+
+ * pcl-cvs.el: run through the spell checker...
+ - noted some free variables in comments
+ (cvs-inhibit-copyright-message): move this above
+ cvs-startup-message to keep the compiler quiet
+
+ * compile-all.el: removed (use make for dependency checking!)
+
+ * Makefile.in: tweak various comments and echo messages...
+ (elcfiles): removed this target.
+ (.SUFFIXES, .el.elc): added support for elisp files.
+ (CORE): new macro -- list of files all .elc depend on [still empty]
+ (BATCHFLAGS): new macro -- flags to pass to emacs
+ (OBJDIR_DISTFILES): added ELCFILES to be shipped in distribution
+
+ * README: fix the RCS Id.
+
+ * INSTALL: re-copy formatted makeinfo output from pcl-cvs.info,
+ just to keep everything in proper synchronisation.
+
+ * pcl-cvs.texinfo (Pcl-cvs installation): update to match Karl's
+ new wording from INSTALL.
+
+Wed Jan 10 22:04:35 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * INSTALL: make first item read a little more smoothly.
+
+ * README: note that pcl-cvs has been tested under 19.30.
+
+Wed Jan 10 17:59:00 1996 Greg A. Woods <woods@most.weird.com>
+
+ * ChangeLog.woods: these are changes integrated in from my
+ own pcl-cvs repository module, and based on the original PCL-CVS
+ Version 1.05 release. They include most, if not all, of the
+ changes from the Cygnus and Cyclic CVS contrib versions of
+ PCL-CVS (i.e. the changes noted below).
+
+Sat Dec 30 15:01:45 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * pcl-cvs.el (cvs-changelog-ours-p): check that
+ `add-log-full-name' and `add-log-mailing-address' are non-nil, in
+ addition to checking that they are boundp.
+
+Thu Dec 21 16:45:48 1995 Karl Fogel <kfogel@occs.cs.oberlin.edu>
+
+ * pcl-cvs.el (cvs-parse-stderr): ignore kerberos connection
+ failure, since CVS will automatically try rsh next. I think this
+ is okay because if a person needs to know that kerberos failed,
+ then chances are the rsh failed too, and *that* error message will
+ clue them in that something's afoot.
+
+Wed Nov 22 11:01:50 1995 Joshua Cowan <jcowan@hermit.reslife.okstate.edu>
+
+ * pcl-cvs.el (cvs-changelog-ours-p): use `user-full-name' if
+ `add-log-full-name' unbound, as not every uses the stuff in
+ add-log.el. Same with `add-log-mailing-address'.
+ (cvs-changelog-entries): change to `change-log-mode' unless
+ already in it.
+
+Sun Jul 9 20:57:11 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * "/bin/rmdir" as default, not "/usr/local/bin/rmdir".
+
+Fri Jun 16 15:24:34 1995 Jim Kingdon (kingdon@cyclic.com)
+
+ * pcl-cvs.elc, pcl-cvs-lucid.elc: Added.
+
+ * Makefile.in: Rename from Makefile and set srcdir.
+
+Thu May 18 17:10:27 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ Automatically guess CVS log entries from ChangeLog contents.
+ * pcl-cvs.el (cvs-mode-changelog-commit): New command.
+ (cvs-changelog-full-paragraphs): New variable.
+ (cvs-changelog-name, cvs-narrow-changelog,
+ cvs-changelog-paragraph, cvs-changelog-subparagraph,
+ cvs-changelog-entry, cvs-changelog-ours-p, cvs-relative-path,
+ cvs-changelog-entries, cvs-changelog-insert-entries, cvs-union,
+ cvs-insert-changelog-entries, cvs-edit-delete-common-indentation):
+ New functions.
+ (cvs-mode-map): Bind 'C' to cvs-mode-changelog-commit.
+ (cvs-mode): Mention cvs-mode-changelog-commit in docstring.
+
+ Give the info files names ending in ".info".
+ * Makefile (INFOFILES, install_info): Change pcl-cvs to
+ pcl-cvs.info.
+ (pcl-cvs.info): Target renamed from pcl-cvs.
+ (DISTFILES): pcl-cvs removed; we handle the info files explicitly
+ in the dist-dir target.
+ (dist-dir): Depend on pcl-cvs.info. Distribute pcl-cvs.info*.
+ * pcl-cvs.texinfo: Change @setfilename appropriately.
+ * INSTALL: Updated.
+ * .cvsignore: Correctly ignore the info files.
+
+ * README: Note that pcl-cvs has been tested under 19.28, and that
+ the "cookie" naming conflict was resolved in 19.11.
+
+ * Makefile (pcl-cvs-lucid.elc): Changed this target from
+ pcl-cvs-lucid.el. That's a source file, for goodness' sake!
+
+Tue May 9 13:56:50 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * Change references to "Cygnus's remote CVS" to "Cyclic CVS".
+
+Wed May 3 13:55:27 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el (cvs-parse-stderr): Handle colons after both
+ "rcsmerge" and "warning".
+
+Fri Apr 28 22:38:14 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * Makefile (ELFILES): Include pcl-cvs-startup.el.
+ (info, pcl-cvs): Call makeinfo appropriately for modern versions.
+ (pcl-cvs.aux): List dependency on pcl-cvs.texinfo.
+ (pcl-cvs.ps): New target.
+ (DVIPS): New variable.
+ (dist-dir): Renamed from dist, updated to accept DISTDIR value
+ passed from parent.
+ (DISTFILES): New varible.
+ (pcl-cvs.elc, pcl-cvs-lucid.elc): Add targets to elcfiles target.
+
+Tue Apr 25 21:33:49 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el: (cvs-parse-stderr): Recognize "conflicts" as well as
+ "overlaps" before "during merge."
+
+Thu Feb 16 12:17:20 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el (cvs-parse-stderr): Recognize "conflicts found in..."
+ messages attributed to "cvs server", as well as "cvs update".
+
+Sat Feb 4 01:47:01 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el: Deal with the 'P' action, produced by remote CVS.
+ (cvs-parse-stdout): Treat 'P' like 'U' --- file is updated.
+
+Tue Jan 31 23:31:39 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el (cvs-cvsroot-required): New variable.
+ (cvs-do-update): If cvs-cvsroot-required is not set, don't complain if
+ CVSROOT and cvs-cvsroot are both unset.
+
+Sun Jan 22 21:22:22 1995 Jim Blandy <jimb@totoro.bio.indiana.edu>
+
+ * pcl-cvs.el (cvs-parse-stderr):
+ Some changes for Cygnus's Remote CVS. Treat
+ messages like "cvs server: Updating DIRECTORY" as we treat those like
+ "cvs update: Updating DIRECTORY". Ignore other messages starting with
+ "cvs server".
+
+ * pcl-cvs.el (cvs-parse-stderr): Re-indent.
+
+ * .cvsignore: Add ignore list for Texinfo litter.
+
+ * Makefile (lispdir): Set appropriately for totoro.
+ * pcl-cvs.el (cvs-program, cvs-diff-program, cvs-rmdir-program): Same.
+
+Tue Jun 1 00:00:03 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * Release 1.05. (This release was promised before the end of May,
+ but I didn't quite make it. No, I didn't fake the date above).
+
+Mon May 31 01:32:25 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * Removed the elib sub-directory. Users must now get the Elib
+ library separately.
+ * pcl-cvs.texinfo: Document it.
+
+ * pcl-cvs-lucid.el: A new version, supplied by Jamie Zawinsky,
+ added.
+
+ * pcl-cvs Id 68: Transform RCS keywords
+ * Makefile (pcl-cvs-$(VER)): Remove the $ signs in most files in
+ the distribution.
+
+ * pcl-cvs Id 76: Extra " in cvs-mode-add.
+ * pcl-cvs.el (cvs-mode-add): Don't add the extra level of quotes
+ around the log message, since it doesn't work with CVS.
+
+ * pcl-cvs Id 56: '-d <CVSROOT>' support in pcl-cvs
+ * pcl-cvs.el (cvs-change-cvsroot): New function.
+
+ * pcl-cvs Id 77: *cvs* isn't cleared properly
+ * pcl-cvs.el (cvs-do-update): Always erase the *cvs* buffer and
+ re-create the collection.
+
+ * pcl-cvs.el (cvs-do-update): Set mode-line-process in the *cvs*
+ buffer.
+ * pcl-cvs.el (cvs-mode): Reset mode-line-process.
+
+ * pcl-cvs Id 59: sort .cvsignore alphabetically!
+ * pcl-cvs.el (cvs-sort-ignore-file): New variable.
+ * pcl-cvs.el (cvs-mode-ignore): Use it.
+ * pcl-cvs.texinfo: Document it.
+
+ * pcl-cvs Id 75: Require final newline.
+ * pcl-cvs.el (cvs-commit-buffer-require-final-newline): New
+ variable.
+ * pcl-cvs.el (cvs-edit-done): Use it.
+ * pcl-cvs.texinfo: Document it.
+
+ * pcl-cvs Id 72: make clean deletes lucid-emacs.el
+ * dist-makefile (ELCFILES): Fixed a typo.
+
+ * pcl-cvs Id 46: "cvs remove f" "touch f" "cvs update f" -> parse err.
+ * pcl-cvs.el (cvs-fileinfo->type): New type: REM-EXIST.
+ * pcl-cvs.el (cvs-shadow-entry-p): A REMOVED that follows a
+ REM-EXIST is a shadow.
+ * pcl-cvs.el (cvs-parse-stderr): Recognize the "should be removed
+ and is still there" message.
+ * pcl-cvs.el (cvs-pp): Recognize REM-EXIST.
+ * pcl-cvs.el (cvs-mode-undo-local-changes): Recognize and complain
+ about REM-EXIST. Defensive test added: complain about unknown types.
+
+ * pcl-cvs.el (cvs-mode-add): Add an extra level of quotes around
+ the log message. This is apparently needed by RCVS. <This change
+ has been removed. --ceder>.
+
+ * pcl-cvs.el (cvs-parse-stderr): Ignore output from RCVS.
+
+Tue Apr 27 00:48:40 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * pcl-cvs.el (cvs-startup-message): Now a defconst instead of a
+ defvar.
+ * pcl-cvs.el (cvs-mode-commit): Add a defvar for it.
+
+ * dist-makefile (EMACS): Use $(EMACS) instead of hard-coding 'emacs'.
+
+Sat Apr 17 12:47:10 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * Release 1.04.
+
+ * pcl-cvs.texinfo: Updated the Contributors node.
+
+ * pcl-cvs Id 58: Lucid GNU Emacs support
+ * pcl-cvs-lucid.el: New file, contributed by the people at Lucid.
+ * pcl-cvs.el: Autoload pcl-cvs-lucid if running in an Lucid GNU
+ Emacs.
+ * compile-all.el: (files-to-compile): Add pcl-cvs-lucid.
+ * dist-makefile (ELFILES, ELCFILES): Dito.
+
+ * pcl-cvs Id 55: cvs-diff-backup swaps old and new version.
+ * pcl-cvs.el (cvs-diff-backup-extractor): Old version should be
+ first.
+ * pcl-cvs.el (cvs-mode-diff-backup): Call cvs-backup-diffable
+ correctly.
+
+ * pcl-cvs Id 64: elib substitute
+ * dist-makefile (install): Warn about Elib.
+ * pcl-cvs.texinfo: Talk about Elib.
+
+ * pcl-cvs Id 50: Committing the *commit* buffer twice.
+ * pcl-cvs.el (cvs-edit-done): Report an error if cvs-commit-list
+ is empty, and empty it when the commit is done.
+
+ * pcl-cvs Id 56: '-d <CVSROOT>' support.
+ * pcl-cvs.el (cvs-cvsroot): New variable.
+ * pcl-cvs.el (cvs-do-update, all callers of cvs-execute-list): Use
+ it everywhere CVS is called, to override CVSROOT.
+ * pcl-cvs.texinfo (Customization): Document it.
+
+Thu Apr 1 00:34:55 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * pcl-cvs.el (cvs-retrieve-revision-to-tmpfile): Exit status nil
+ from call-process means everything was successful in some Emacs
+ versions.
+
+ * pcl-cvs.el (cvs-mode-map): Bind "q" to bury-buffer.
+ * pcl-cvs.texinfo: Document it.
+
+Thu Mar 11 00:05:03 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * Release 1.03-Emerge (not released).
+
+ * Makefile (pcl-cvs-$(VER)): Don't includ elib-dll-debug.el in the
+ distribution. (It's included as elib/dll-debug.el).
+
+ * pcl-cvs.el (cvs-mode): Document the "e" key (cvs-mode-emerge).
+
+Tue Mar 9 00:02:57 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * pcl-cvs.texinfo (Emerge): New node.
+
+ * pcl-cvs.el (cvs-kill-buffer-visiting): New function.
+
+ * pcl-cvs.el (cvs-mode-emerge): Handle Conflict and Merged files.
+
+ * pcl-cvs.el (cvs-retrieve-revision-to-tmpfile): Handle any revision.
+
+ * pcl-cvs.el (cvs-fileinfo-*): Store base-revision instead of
+ backup-file.
+
+ * pcl-cvs.el (cvs-backup-diffable): The file is only diffable if
+ the backup file is readable.
+
+ * pcl-cvs.el (cvs-mode-map): Bind "e" to cvs-mode-emerge instead
+ of cvs-mode-find-file (which is anyhow bound to "f").
+
+Mon Mar 8 23:06:52 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * pcl-cvs.el (cvs-mode-emerge): New function. Currently only
+ handles emerge of Modified files.
+
+ * pcl-cvs.el (cvs-retrieve-revision-to-tmpfile): New function.
+
+Sun Jan 24 20:07:18 1993 Per Cederqvist (ceder@lysator.liu.se)
+
+ * elib-dll-debug.el: Moved to elib.
+
+Mon Jan 18 00:35:59 1993 Per Cederqvist (ceder@mauritz)
+
+ * pcl-cvs.el (cvs-do-update): Added a probably unnecessary sit-for.
+
+ * Release 1.03-Elib-0.05.1 (not released).
+
+ * Elib 0.05 compatibility:
+ * elib-dll-debug.el, pcl-cvs-buffer.el, test-dll.el: Fix the
+ require strings.
+ * pcl-cvs.el (cvs-pp): Insert the string.
+
+ * Release 1.03-Elib-0.05 (not released).
+
+ * elib: New directory, containing the parts of elib that are
+ required for pcl-cvs. Changes to the files in that directory
+ that are present in Elib are documented in the ChangeLog of
+ Elib, not here.
+ * Makefile (pcl-cvs-$(VER)): Copy the new dir to the distribution.
+ * dist-makefile (ELFILES, ELCFILES): Don't include the Elib files.
+
+Fri Jan 8 02:43:49 1993 Per Cederqvist (ceder@konrad)
+
+ * pcl-cvs.el (cvs-mode-map): Bind "e" to cvs-mode-find-file, like
+ in dired.
+
+Sun Jan 3 23:25:13 1993 Per Cederqvist (ceder@konrad)
+
+ * elib-dll.el, elib-node.el, cookie.el: Moved to the elib package.
+ Pcl-cvs now requires elib.
+
+Tue Dec 29 22:06:57 1992 Per Cederqvist (ceder@konrad)
+
+ * pcl-cvs.el: Tracked the latest (last?) rename of all functions
+ in cookie.el.
+
+Thu Sep 24 00:29:16 1992 Per Cederqvist (ceder@robert)
+
+ * pcl-cvs.texinfo (Archives): This version is not distributed with
+ CVS 1.3, so don't claim that it is.
+
+Fri Aug 21 15:17:08 1992 Per Cederqvist (ceder@maskros)
+
+ * pcl-cvs.el (cvs-parse-stderr): Fixed two "(set head" that should
+ be "(setq head".
+
+Thu Aug 20 05:53:58 1992 Per Cederqvist (ceder@robin)
+
+ * cookie.el: Changes to this file is documented in the ChangeLog
+ of elib in the future.
+
+Tue Aug 18 03:30:28 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el: Don't use cookie-last-tin (which no longer exists).
+
+ * cookie.el: Use prefix cookie:: for internal functions.
+
+ * cookie.el: (cookie:enter-after, cookie:enter-before,
+ cookie:nth-cookie): Implemented.
+ * cookie.el: No longer define (impl).
+
+ * cookie.el: More renames:
+ cookie:next-cookie -> cookie:goto-next-tin
+ cookie:previous-cookie -> cookie:goto-previous-tin
+ tin-next -> cookie:next-tin
+ tin-previous -> cookie:previous-tin
+ tin-nth -> cookie:nth-tin
+ tin-delete -> cookie:delete-tin
+ cookie:collect -> cookie:collect-cookies
+ cookie:tin-collect -> cookie:collect-tins
+ (new) -> cookie:tin-collect-cookies
+ (new) -> cookie:tin-collect-tins
+ cookie:refresh -> cookie:refresh-all
+ tin-invalidate-tins -> cookie:invalidate-tins
+
+Mon Aug 17 01:39:49 1992 Per Cederqvist (ceder@robin)
+
+ * cookie.el (cookie:set-buffer-bind-dll-let*): New macro. Used in
+ many places instead of cookie:set-buffer-bind-dll.
+ * cookie.el (cookie:set-buffer-bind-dll): Renamed the macro
+ cookie:set-buffer to this.
+
+ * pcl-cvs.el (cvs-use-temp-buffer): Set default-directory.
+
+Sun Aug 16 20:51:30 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-add-sub): Fixed call to cvs-add-file-update-buffer.
+
+Sat Aug 8 20:28:21 1992 Per Cederqvist (ceder@robin)
+
+ * Release 1.03-Cookie-II (not released).
+
+ * pcl-cvs.el (cvs-mode-diff-cvs): Don't care about the exit status
+ from ``cvs diff''.
+
+ * pcl-cvs.el (cvs-mode): Document cvs-mode-undo-local-changes.
+ * pcl-cvs.el (cvs-diffable): New function.
+
+ * pcl-cvs.el: Use the new cookie package.
+ * pcl-cvs.el (cvs-cookie-handle): New variable.
+ * pcl-cvs.el (cvs-do-update): User the new cookie:create
+ interface, and cookie:clear if the buffer already existed. Make
+ the buffer read-only.
+ * pcl-cvs.el (cvs-mode-next-line, cvs-mode-previous-line): New
+ functions (used instead of cookie:next-cookie and
+ cookie:previous-cookie).
+
+ * cookie.el: Major redesign. The handle that is passed to all
+ cookie functions is now a new datatype, and not the buffer that
+ the cookies resides in. This way it is possible to have more than
+ one set of cookies in a buffer. Things that used to be
+ buffer-local variables are now fields in the handle data type.
+ cookie-last-tin is no longer available.
+ * cookie.el (cookie:create): The buffer is not cleared, nor set to
+ be read-only.
+ * cookie.el (cookie:next-cookie, cookie:previous-cookie): Since
+ the first argument is now a handle and not a buffer, these can no
+ longer be called interactively. You have to write a small wrapper
+ about them.
+ * cookie.el (cookie:buffer): New function.
+
+Tue Aug 4 03:02:25 1992 Per Cederqvist (ceder@robert)
+
+ * pcl-cvs.texinfo (Bugs): Renamed "Reporting bugs and ideas" to
+ "Bugs" and added a table of known bugs/FAQ:s.
+
+Mon Aug 3 00:19:39 1992 Per Cederqvist (ceder@robert)
+
+ * pcl-cvs.el, pcl-cvs.texinfo: Big Renaming Time!
+ The commands that operate in the *cvs* buffer:
+ cvs-add-change-log-entry-other-window -> cvs-mode-add-change-log-entry-other-window
+ cvs-mark-all-files -> cvs-mode-mark-all-files
+ cvs-revert-updated-buffers -> cvs-mode-revert-updated-buffers
+ cvs-undo-local-changes -> cvs-mode-undo-local-changes
+ cvs-unmark-up -> cvs-mode-unmark-up
+ cvs-acknowledge -> cvs-mode-acknowledge
+ cvs-unmark-all-files -> cvs-mode-unmark-all-files
+ cvs-add -> cvs-mode-add
+ cvs-diff-backup -> cvs-mode-diff-backup
+ cvs-commit -> cvs-mode-commit
+ cvs-diff-cvs -> cvs-mode-diff-cvs
+ cvs-find-file -> cvs-mode-find-file
+ cvs-update-no-prompt -> cvs-mode-update-no-prompt
+ cvs-ignore -> cvs-mode-ignore
+ cvs-log -> cvs-mode-log
+ cvs-mark -> cvs-mode-mark
+ cvs-find-file-other-window -> cvs-mode-find-file-other-window
+ cvs-remove-file -> cvs-mode-remove-file
+ cvs-status -> cvs-mode-status
+ cvs-remove-handled -> cvs-mode-remove-handled
+ cvs-unmark -> cvs-mode-unmark
+
+ * pcl-cvs.el (cvs-cvs-diff-flags): Variable deleted.
+ * pcl-cvs.el (cvs-diff-cvs): Use cvs-diff-flags instead.
+ * pcl-cvs.texinfo (Customization): Update the doc.
+
+ * pcl-cvs.el (cvs-diff-cvs): Handle exit status 0 (no diffs), 1
+ (diffs) and other (error).
+ * pcl-cvs.el (cvs-execute-list): Add support for this kind of
+ thing.
+
+ * Revert buffers for committed files:
+ * pcl-cvs.el (cvs-auto-revert-after-commit): New variable.
+ * pcl-cvs.texinfo (Committing changes, Customization): Document
+ it.
+ * pcl-cvs.el (cvs-after-commit-function): New function.
+
+ * pcl-cvs.el (cvs-execute-list): Return the exit status or nil.
+ * pcl-cvs.el (cvs-edit-done, cvs-diff-cvs, cvs-remove-file,
+ cvs-undo-local-changes, cvs-add, cvs-status, cvs-log): Use the
+ exit status to generate an error message.
+
+
+ * pcl-cvs.el (cvs-do-update): It should be "cvs -n update -l", not
+ "cvs -l update -n". Put the -n and/or -l in the message that is
+ displayed in the *cvs* buffer during the update.
+
+Sat Aug 1 00:55:49 1992 Per Cederqvist (ceder@robert)
+
+ * cookie.el (cookie-sort): New function.
+
+ * cookie.el (cookie-clear): Rewritten. No longer clears all local
+ variables.
+
+Tue Jul 28 17:21:17 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-parse-stderr): Try to handle the output from RCS
+ when it is compiled without DIFF3_BIN and a conflict occurs.
+
+ * pcl-cvs.texinfo (Getting Started): Fixed typo.
+
+ * pcl-cvs-startup.el (cvs-update-other-window): Make the autoload
+ be interactive.
+
+Mon Jul 27 19:36:40 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-revert-updated-buffers, cvs-revert-fileinfo):
+ New functions.
+ * pcl-cvs.texinfo (Reverting your buffers): Document it.
+
+ * pcl-cvs.el (cvs-fileinfo->full-path): New function.
+ * pcl-cvs.el (cvs-full-path): Use it.
+
+ * cookie.el (cookie-map, cookie-map-reverse): Better doc-
+ string. Removed the unused local variable 'result'.
+
+ * compile-all.el: Renamed elib-files to files-to-compare.
+ * compile-all.el (compile-pcl-cvs): Bind load-path in a let
+ statement instead of globally.
+
+Thu Jul 23 19:02:41 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-do-update): Check that CVSROOT is set.
+ * pcl-cvs.el (cvs-diff-cvs): Check that cvs-cvs-diff-flags is a
+ list.
+ * pcl-cvs.el (cvs-diff-backup): Check that cvs-diff-flags is a
+ list.
+
+Tue Jul 21 11:27:39 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-parse-error): Make the *cvs* buffer writeable
+ before trying to write the email message. Require sendmail before
+ trying to switch to mail-mode.
+
+ * pcl-cvs.el (cvs-do-update): Check that cvs-program exists.
+
+ * pcl-cvs.el (cvs-skip-line): Fixed bracketing error.
+
+Mon Jul 20 10:31:51 1992 Per Cederqvist (ceder@robin)
+
+ * Release 1.03.
+
+ * pcl-cvs.el, cookie.el: Indentation fixes.
+
+ * Makefile (pcl-cvs-$(VER)): Include NEWS in the distribution.
+
+ * pcl-cvs.el (cvs-rm-program): Deleted.
+ * pcl-cvs.el (cvs-rmdir-program, cvs-lock-file): New variables.
+
+ * Handle lock files in a nicer way:
+ * pcl-cvs.el (cvs-update-filter, cvs-delete-lock,
+ cvs-lock-file-p): New functions.
+ * pcl-cvs.el (cvs-do-update, cvs-sentinel): Redirect stdout to the
+ temporary file, not stderr. Use cvs-update-filter.
+ * pcl-cvs.el (cvs-parse-update): New arguments.
+ * pcl-cvs.el (cvs-parse-buffer): Renamed to cvs-parse-update.
+ * pcl-cvs.el (cvs-stderr-file): Renamed to cvs-stdout-file.
+ * pcl-cvs.texinfo (Miscellaneous commands, Updating the
+ directory): Document cvs-delete-lock.
+
+ * pcl-cvs.el (cvs-mode): Don't reset buffer-read-only.
+
+ * pcl-cvs.el (cvs-find-file-other-window): Don't save-some-buffers.
+
+Thu Jul 16 00:19:58 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el, test-cookie-el: Use the new names from cookie.el.
+
+ * cookie.el: Big Renaming Time!
+ External functions:
+ cookie-next -> tin-next
+ cookie-previous -> tin-previous
+ cookie-nth -> tin-nth
+ cookie-delete -> tin-delete
+ cookie-filter-tins -> tin-filter
+ cookie-get-selection -> tin-get-selection
+ cookie-start-marker -> tin-start-marker
+ cookie-end-marker -> tin-end-marker
+ cookie-invalidate-tins -> tin-invalidate-tins
+ cookie-collect-tins -> tin-collect
+ cookie-collect-cookies -> cookie-collect
+ Internal functions:
+ cookie-create-tin -> cookie-create-wrapper
+ cookie-tin-start-marker -> cookie-wrapper-start-marker
+ cookie-tin-cookie-safe -> cookie-wrapper-cookie-safe
+ cookie-tin-cookie -> cookie-wrapper-cookie
+ set-cookie-tin-start-marker -> cookie-wrapper-set-start-marker
+ set-cookie-tin-cookie -> cookie-wrapper-set-cookie
+ cookie-tin-p -> cookie-wrapper-p
+ cookie-create-tin-and-insert -> cookie-create-wrapper-and-insert
+
+ * pcl-cvs.el (cvs-find-file, cvs-find-file-other-window): Signal
+ an appropriate error message if the *cvs* buffer is empty.
+
+ * cookie.el (cookie-create): Make the buffer read-only.
+ * cookie.el (cookie-create-tin-and-insert, cookie-refresh,
+ cookie-delete-tin-internal, cookie-refresh-tin): Bind
+ buffer-read-only to nil while changing the contents of
+ the buffer.
+
+ * pcl-cvs.el (cvs-byte-compile-files): New function.
+ * pcl-cvs.texinfo (Miscellaneous commands): Document it.
+
+ * pcl-cvs.el (cvs-diff-ignore-marks): New variable.
+ * pcl-cvs.el (cvs-diff-cvs, cvs-diff-backup): Don't consider
+ marked files to be selected if a prefix argument is given XOR the
+ variable cvs-diff-ignore-marks is non-nil.
+ * pcl-cvs.el (cvs-get-marked): New optional argument `ignore-marks'.
+ * pcl-cvs.texinfo (Customization, Viewing differences): Document
+ this behaviour.
+
+ * pcl-cvs.el (cvs-undo-local-changes): New function.
+ * pcl-cvs.texinfo (Undoing changes): Document
+ cvs-undo-local-changes.
+ * pcl-cvs.el (cvs-mode-map): cvs-unmark-all-files moved from "U"
+ to "ESC DEL". cvs-undo-local-changes bound to "U".
+ * pcl-cvs.texinfo (Marking files): Document ESC DEL.
+
+ * pcl-cvs.el (cvs-skip-line): New arguments. All callers updated.
+ Now calls cvs-parse-error if a parse error occurs.
+ * pcl-cvs.el (cvs-parse-error): New function that creates a bug
+ report.
+ * pcl-cvs.el (cvs-parse-stderr, cvs-parse-stdout): New arguments.
+ The only caller (cvs-parse-buffer) updated. Call cvs-parse-error
+ in case of parse error.
+
+ * pcl-cvs.el (pcl-cvs-version): New variable.
+
+ * cookie.el (cookie-create): Kill all local variables in the buffer.
+
+Fri Jul 10 11:17:40 1992 Per Cederqvist (ceder@robin)
+
+ * Release 1.03beta1.
+
+Thu Jul 9 03:12:00 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-update-running): New variable.
+ * pcl-cvs.el (cvs-do-update): Use it instead of the previous local
+ variable cvs-process (that no longer exists). Make sure that only
+ one `cvs update' runs at any given moment.
+ * pcl-cvs.el (cvs-sentinel): Reset cvs-update-running when the
+ update process exits.
+
+ * pcl-cvs.el (cvs-update): Switch to the *cvs* buffer.
+ * pcl-cvs.el (cvs-update-other-window): New function.
+ * pcl-cvs-startup.el (cvs-update-other-window): Added a autoload
+ for it.
+ * pcl-cvs.el (cvs-do-update): Don't pop up any buffer in a window
+ - let cvs-update or cvs-update-other-window handle that. Also
+ don't kill the *cvs* buffer, but rather insert a "Running cvs..."
+ message into it.
+ * pcl-cvs.el (cvs-parse-buffer): Don't change the window
+ configuration.
+
+ * pcl-cvs.el (cvs-create-fileinfo, cvs-pp, cvs-fileninfo->type):
+ New type for a fileinfo: MESSAGE.
+
+ * pcl-cvs.el (cvs-cvs-buffer): Deleted the variable. Use
+ cvs-buffer-name instead. (I no longer have any plans to allow more
+ than one cvs update to run at the same time - things only get
+ confusing). Changed all places where cvs-cvs-buffer was used.
+
+ * pcl-cvs.el: Take care of update programs (the -u option in the
+ modules file):
+ * pcl-cvs.el (cvs-update-prog-output-skip-regexp): New variable.
+ * pcl-cvs.el (cvs-parse-stdout): Skip output from the update
+ program (using cvs-update-prog-output-skip-regexp).
+ * pcl-cvs.texinfo (Future enhancements): Document that the
+ solution is not as good as it should be.
+ * pcl-cvs.texinfo (Customization): Document the variable.
+
+Wed Jul 8 20:29:44 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-do-update): Check that this-dir really exists
+ and is a directory, and that this-dir/CVS exists and is a
+ directory.
+
+Tue Jul 7 01:02:24 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.texinfo (Customization): Document TMPDIR.
+
+ * This chunk of modifications should make it possible to run
+ pcl-cvs on hosts that do not line-buffer stdout (such as
+ DECstation). They work by diverting stdout and stderr from
+ `cvs update' and later sorting them together.
+ * pcl-cvs.el (cvs-parse-stderr): Don't fail to parse conflict
+ data.
+ * pcl-cvs.el (cvs-remove-stdout-shadows, cvs-shadow-entry-p): New
+ functions.
+ * pcl-cvs.el (cvs-parse-buffer): Use it.
+ * pcl-cvs.el (cvs-remove-empty-directories): New function.
+ * pcl-cvs.el (cvs-remove-handled, cvs-parse-buffer): Use it.
+ * pcl-cvs.el (cvs-get-current-dir): New argument ROOT-DIR. All
+ calls to cvs-get-current-dir updated.
+ * pcl-cvs.el (cvs-do-update): Allocate a tmp file. Use cvs-shell
+ (typically /bin/sh) to redirect stderr from CVS to the tmp file.
+ * pcl-cvs.el (cvs-sentinel): Handle the tmp file. Remove it when
+ it is parsed.
+ * pcl-cvs.el (cvs-parse-buffer): New argument STDERR-BUFFER. All
+ calls to cvs-parse-buffer updated. Rewritten to handle the
+ separation of stderr and stdout.
+ * pcl-cvs.el (cvs-shell, cvs-stderr-file): New variables.
+ * pcl-cvs.el (cvs-compare-fileinfos, cvs-parse-stderr,
+ cvs-parse-stdout): New functions.
+
+ * pcl-cvs.el (cvs-parse-buffer): Some modifications for output
+ from RCS 5.6.
+
+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.
+
--- /dev/null
+This text is copied from the TeXinfo manual for pcl-cvs.
+
+Installation of the pcl-cvs program
+===================================
+
+ 1. Possibly edit the file `Makefile' to reflect the situation at your
+ site. We say "possibly" because the version of pcl-cvs included
+ with CVS uses a configuration mechanism integrated with the overall
+ mechanisms used by the CVS build and install procedures. Thus the
+ file `Makefile' will be generated automatically from the file
+ `Makefile.in', and it should not be necessary to edit it further.
+
+ If you do have to edit the `Makefile', 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(s) to
+ `infodir'.
+
+ 2. Configure pcl-cvs.el
+
+ There are a couple of pathnames that you have to check to make
+ sure that they match your 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. Release 1.05 and later of pcl-cvs requires parts of the Elib
+ library, version 1.0 or later. Elib is available via anonymous
+ ftp from prep.ai.mit.edu in `pub/gnu/elib-1.0.tar.gz', and from a
+ lot of other sites that mirror prep. Get Elib, and install it,
+ before proceeding.
+
+ *NOTE:* The version of pcl-cvs included with CVS includes a copy
+ of Elib in the sub-directory `elib' under the `contrib/elib'
+ directory.
+
+ 4. Type `make install' in the source directory. This will
+ byte-compile all `.el' files and copy the `*.elc' files into the
+ directory you specified in step 1.
+
+ If you want to install the `*.el' files too, you can type `make
+ install-el' to do so.
+
+ If you only want to create the compiled elisp files, but don't
+ want to install them, you can type `make' without parameters.
+
+ 5. Edit the file `default.el' in your emacs lisp directory (usually
+ `/usr/gnu/lib/emacs/site-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(s) `pcl-cvs.info*' 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. `prep.ai.mit.edu' as
+ `pub/gnu/texinfo-3.7.tar.gz' (there might be a newer version there
+ when you read this).
+
+ 2. Install the info file(s) `pcl-cvs.info*' into your standard `info'
+ directory. You should be able to do this by typing `make
+ install-info'.
+
+ 3. Edit the file `dir' in the `info' directory and enter one line to
+ contain a pointer to the info file(s) `pcl-cvs.info*'. 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.
+
+
+--
+#ident "@(#)cvs/contrib/pcl-cvs:$Name: $Id$"
--- /dev/null
+# Makefile for pcl-cvs, an Emacs interface to CVS.
+# NOTE: pcl-cvs requires Elib to run. See ../../contrib/elib/.
+
+#
+#ident "@(#)original: dist-makefile,v 1.19 1993/05/31 22:43:45 ceder Exp "
+#
+#ident "@(#)elisp/pcl-cvs:$Name: $:$Id: Makefile.in,v 1.1.1.1 1996/05/06 22:20:47 tholo Exp $"
+#
+# Makefile for pcl-cvs release 1.05-CVS-$Name: $.
+# Copyright (C) 1992, 1993 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.
+
+SHELL = /bin/sh
+
+#### Start of system configuration section. ####
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# Where to put the system-wide supplementary files
+libdir = $(prefix)/lib
+
+# Where to put the Info files
+infodir = $(prefix)/info
+
+# Where to put the manual pages.
+mandir = $(prefix)/man
+
+# Used to batch-byte-compile files.
+EMACS = emacs
+# compile with noninteractive environment
+BATCHFLAGS = -batch
+
+# This is the directory in which the ELCFILES will be installed.
+lispdir = $(libdir)/emacs/site-lisp
+
+#### End of system configuration section. ####
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+# Just in case...
+SHELL = /bin/sh
+@SET_MAKE@
+
+DISTFILES = \
+ .cvsignore ChangeLog INSTALL Makefile.in NEWS README \
+ ${ELFILES} \
+ pcl-cvs.texinfo texinfo.tex
+
+
+# OBJDIR_DISTFILES used to include the byte-compiled elisp files, but
+# this seems wrong because the person building the dist cannot have
+# made the appropriate site-specific modifications to pcl-cvs.el.
+# Therefore, I've taken the .elc files out of OBJDIR_DISTFILES for
+# now, pending the Right Solution to this problem (which probably
+# involves moving the site-specific modification section of pcl-cvs.el
+# to a separate file and having autoconf generate as much of the file
+# as possible). -Karl
+#
+# OBJDIR_DISTFILES = $(ELCFILES) pcl-cvs.aux pcl-cvs.ps
+OBJDIR_DISTFILES = pcl-cvs.aux pcl-cvs.ps
+
+
+# files that contain key macro definitions. almost everything
+# depends on them because the byte-compiler inlines macro
+# expansions. everything also depends on the byte compiler
+# options file since this might do odd things like turn off
+# certain compiler optimizations.
+CORE =
+
+ELFILES = pcl-cvs.el pcl-cvs-lucid.el pcl-cvs-startup.el
+ELCFILES = pcl-cvs.elc pcl-cvs-lucid.elc
+INFOFILES = pcl-cvs.info*
+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
+
+# Use cp if you don't have install.
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+MAKEINFO = makeinfo
+
+SET_TEXINPUTS = TEXINPUTS=.:$(srcdir):$$TEXINPUTS
+
+# Don Knuth's TeX formatter
+TEX = tex
+
+# auxiliary program for sorting Texinfo indices
+TEXINDEX = texindex
+
+DVIPS = dvips
+DVIPSFLAGS =
+
+# CYGNUS LOCAL: install does not depend on info
+all: $(ELCFILES) # info
+.PHONY: all
+
+.SUFFIXES: .el .elc
+# We copy the .el file to the build dir--is there a cleaner way to get
+# emacs to compile the .el file from srcdir and put the .elc in the build dir?
+# (that is also why we have separate rules for pcl-cvs.elc and
+# pcl-cvs-lucid.elc rather than just using a .el.elc rule).
+pcl-cvs.elc: pcl-cvs.el
+ @echo "You can probably ignore free variable and unknown function warnings..."
+ if test -f pcl-cvs.el; then \
+ : OK, we are building in srcdir ; \
+ else \
+ ln $(srcdir)/pcl-cvs.el . ; \
+ fi
+ $(EMACS) $(BATCHFLAGS) -f batch-byte-compile pcl-cvs.el
+pcl-cvs-lucid.elc: pcl-cvs-lucid.el
+ @echo "You can probably ignore free variable and unknown function warnings..."
+ if test -f pcl-cvs-lucid.el; then \
+ : OK, we are building in srcdir ; \
+ else \
+ ln $(srcdir)/pcl-cvs-lucid.el . ; \
+ fi
+ $(EMACS) $(BATCHFLAGS) -f batch-byte-compile pcl-cvs-lucid.el
+
+check installcheck:
+ @echo "$@ not supported in this makefile..."
+.PHONY: check installcheck
+
+# CYGNUS LOCAL: install does not depend on install-info
+install: install-elc # install-info install-el
+
+install-el: $(ELFILES)
+ 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: info
+ test -f pcl-cvs.info || cd $(srcdir); \
+ for i in *.info* ; do \
+ $(INSTALL_DATA) $$i $(infodir)/$$i ; \
+ done
+
+.PHONY: install install-el install-elc install-info
+
+# mkinstalldirs isn't supported for CVS yet....
+installdirs: $(top_srcdir)/mkinstalldirs
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(lispdir) $(infodir)
+.PHONY: installdirs
+
+uninstall:
+ @echo "$@ not yet supported in this makefile..."
+.PHONY: uninstall
+
+info: pcl-cvs.info
+.PHONY: info
+
+pcl-cvs.info: pcl-cvs.texinfo
+ $(MAKEINFO) ${srcdir}/pcl-cvs.texinfo -o pcl-cvs.info
+
+dvi: pcl-cvs.dvi
+.PHONY: dvi
+
+# this mess seems to be necessary to make the index right...
+pcl-cvs.dvi pcl-cvs.aux: pcl-cvs.texinfo
+ $(SET_TEXINPUTS) $(TEX) $(srcdir)/pcl-cvs.texinfo
+ $(SET_TEXINPUTS) $(TEX) $(srcdir)/pcl-cvs.texinfo
+ -$(TEXINDEX) pcl-cvs.cp pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky \
+ pcl-cvs.pg
+ $(SET_TEXINPUTS) $(TEX) $(srcdir)/pcl-cvs.texinfo
+
+pcl-cvs.ps: pcl-cvs.dvi
+ $(DVIPS) $(DVIPSFLAGS) pcl-cvs.dvi -o pcl-cvs.ps
+
+mostlyclean clean:
+ rm -f *~ core $(ELCFILES) $(INFOFILES) $(TEXTMPS)
+.PHONY: mostlyclean clean
+
+distclean: clean
+ rm -f Makefile tags TAGS
+.PHONY: distclean
+
+realclean maintainer-clean: distclean
+ rm -f pcl-cvs.info* pcl-cvs.ps
+.PHONY: realclean maintainer-clean
+
+# you can't use ctags for lisp...
+tags TAGS:
+ etags *.el
+.PHONY: tags
+
+ls:
+ @echo $(DISTFILES)
+.PHONY: ls
+
+dist-dir: ${OBJDIR_DISTFILES} ${DISTFILES} pcl-cvs.info
+ mkdir ${DISTDIR}
+ for i in ${DISTFILES}; do \
+ ln $(srcdir)/$${i} ${DISTDIR}; \
+ done
+ ln ${OBJDIR_DISTFILES} ${DISTDIR}
+ if [ -f pcl-cvs.info-1 ]; \
+ then ln -f pcl-cvs.info-* ${DISTDIR}; \
+ else : Pacify Ultrix sh; \
+ fi
+.PHONY: dist-dir
+
+subdir = tools/pcl-cvs
+Makefile: ../../config.status Makefile.in
+ cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+# CYGNUS LOCAL: don't depend on auto-re-config
+#../config.status: ../configure
+# cd .. ; $(SHELL) config.status --recheck
+
+# CYGNUS LOCAL: don't depend on auto-re-config
+#../configure: ../configure.in
+# cd $(top_srcdir) ; autoconf
--- /dev/null
+This is the NEWS file for pcl-cvs, an Emacs elisp front-end to CVS.
+
+User-visible changes in the un-official CVS release of pcl-cvs
+from the official 1.05 release to 1.05-CVS-$Name: $:
+
+* Support for using ChangeLog files, including hooks to automatically
+ guess CVS log entries from ChangeLog contents.
+
+* Support for client/server CVS (versions 1.5 through 1.7 and newer).
+
+* New commands for tagging files and directory trees (still needs to
+ be made to run in the background).
+
+* Better support for recognizing and handling unknown directories.
+
+* An attempt at new ediff and emerge interfaces (still needs work!),
+ including attempts to make vendor-branch merging work.
+
+* In a possibly misguided attempt to make it easier to see the effects
+ of changes that affect several files, diff output is now stored in a
+ uniqe buffer for each file.
+
+* Some commands now have default flags (cvs-*-flags).
+
+* Proper quoting of command line arguments displayed in *cvs-tmp*.
+
+* More hacking with getting CVSROOT right, though probably all
+ pointless, since CVS should do the right thing all the time.
+
+* Elib is back, at least in the CVS distribution.
+
+* Lots of minor bug fixes, tweaks, cleanup, re-indentation, etc.
+
+* Some minor tweaks, fixes, re-indentation, etc., in the
+ documentation.
+
+
+User-visible changes in pcl-cvs from 1.04 to 1.05:
+
+* Elib is no longer distributed with pcl-cvs. You must get Elib
+ separately, for instance from ftp.lysator.liu.se in pub/emacs.
+
+* The Lucid Emacs support works again.
+
+* A new function, cvs-change-cvsroot, can be used to interactively
+ switch between CVS repositories.
+
+* The mode line in the *cvs* buffer now indicates when a "cvs update"
+ is running.
+
+* The .cvsignore file is automatically sorted alphabetically (to
+ reduce the risk of conflicts when two people add different files
+ simultaneously). This behaviour can be turned off with
+ cvs-sort-ignore-file.
+
+* A trailing newline is always added in commit log messages. This
+ behaviour can be turned off with
+ cvs-commit-buffer-require-final-newline.
+
+* This version of pcl-cvs should work together with RCVS. I have not
+ tested this myself, though.
+
+* Plus some bug fixes. (Note that the version of cookie.el that is
+ distributed with pcl-cvs 1.04 contains errors that affects pcl-cvs.
+ You should get Elib 0.07).
+
+
+User-visible changes in pcl-cvs from 1.03 to 1.04:
+
+* Support for Emerge. Hitting "e" on a file that is Modified, Merged
+ or in Conflict will start Emerge, an interactive file merger written
+ in Emacs Lisp. This requires Emerge version 4. Emerge is not
+ included in this package. If you can't find it anywhere else, you
+ can get in from ftp.lysator.liu.se in pub/emacs. This package makes
+ it a lot easier to resolve conflicts.
+
+* Emacs will now automatically revert your buffers when the CVS
+ commands pcl-cvs issues causes the file to change. This automatic
+ revert never occurs if the buffer contents did not agree with the
+ file prior to the command.
+
+* If you are running Lucid GNU Emacs, you will get some fonts and
+ mouse support. This was contributed from people at Lucid.
+
+* The variable cvs-cvsroot can be used to select the location if the
+ repository. You no longer need to exit Emacs, setenv CVSROOT, and
+ start a new Emacs if you work with multiple repositories.
+
+* The "q" key can be used to hide the *cvs* buffer.
+
+* The name of the commands in the *cvs* have changed. If it was called
+ cvs-foo, it will now be called cvs-mode-foo. See the ChangeLog
+ entry from Tue Aug 4 03:02:25 1992 for a complete list of changes.
+
+* The variable cvs-cvs-diff-flags is no longer used. Instead,
+ cvs-diff-flags is always used.
+
+* Plus a lot of bug fixes.
+
+
+User-visible changes in pcl-cvs from 1.02 to 1.03:
+
+* Output from CVS to stdout and stderr is separated and parsed
+ independently. In that way pcl-cvs should work regardless of
+ whether stdout is buffered or line-buffered. Pcl-cvs should now
+ work with CVS 1.3 without modifications on hosts such as
+ DECstations.
+
+* Pcl-cvs now fully supports RCS version 5.6 as well as 5.5.
+
+* New functions:
+
+ + cvs-undo-local-changes ("U") - Undo all your modifications
+ to a file and get the newest
+ version from the repository.
+ + cvs-update-other-window - Similar to cvs-update.
+ + cvs-byte-compile-files - Byte compile the selected files.
+
+* cvs-update now displays the *cvs* buffer, which initially contains a
+ small message ("Running `cvs update' in /foo/bar/gazonk/...") until
+ the update is ready. The *cvs* buffer no longer pops up when the
+ update is ready. It often failed to pop up, due to race conditions
+ that are very hard to solve (and I doubt that they were at all
+ solvable).
+
+* cvs-unmark-all-files is moved from "U" to "ESC DEL" to be
+ "compatible" with dired.
+
+* cvs-diff ("d") and cvs-diff-backup ("b") can be configured to work
+ on only the file the cursor is positioned on, and ignore any marked
+ files. A prefix argument toggles this.
+
+* Only one `cvs update' can be run at a time. (It was previously
+ possible to start more than one simultaneously, but pcl-cvs could
+ not really handle more than one.)
+
+* Some rudimentary support for programs that CVS runs at update (due
+ to the -u switch in the modules file).
+
+* Pcl-cvs now automatically generates a bug report if it can't parse
+ the output from CVS.
+
+* The *cvs* buffer is read-only.
+
+* Pcl-cvs now creates temporary files in $TMPDIR if that environment
+ variable is set (otherwise it uses /tmp).
+
+---End of file NEWS---
+#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: NEWS,v 1.1.1.1 1996/05/06 22:20:47 tholo Exp $"
--- /dev/null
+This is the readme file for pcl-cvs, release 1.05-CVS-$Name: $.
+
+Pcl-cvs is a front-end to CVS versions 1.5 through 1.7. It integrates
+the most frequently used CVS commands into an emacs interface.
+
+There may be some configuration that needs to be done in pcl-cvs.el to
+get it to work. See the instructions in the file INSTALL.
+
+Full documentation is in Texinfo format in the file pcl-cvs.texinfo. To
+browse this document online, or in the emacs info mode, you will need to
+process this file with the makeinfo program, which can also be found on
+prep.ai.mit.edu in pub/gnu.
+
+If you have been using a previous version of pcl-cvs (for instance the
+official 1.05 release, or any previous releases) you should read through
+the file NEWS to see what has changed.
+
+This release has been tested under, Emacs 19.28 and Emacs 19.30.
+
+Per Cederqvist
+(updated by Jim Blandy, Greg A. Woods, Karl Fogel)
+
+--
+#OrigId "@(#) Id: README,v 1.14 1993/05/31 22:43:36 ceder Exp "
+#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: README,v 1.1.1.1 1996/05/06 22:20:47 tholo Exp $"
--- /dev/null
+;;; Mouse and font support for PCL-CVS 1.3 running in Lucid GNU Emacs
+;; @(#) Id: pcl-cvs-lucid.el,v 1.2 1993/05/31 19:37:34 ceder Exp
+;; Copyright (C) 1992-1993 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs 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.
+
+
+;; This simply adds a menu of the common CVS commands to the menubar and to
+;; the right mouse button. Clicking right moves point, and then pops up a
+;; menu from which commands can be executed.
+;;
+;; This could stand to be a lot more clever: for example, the "Commit Changes"
+;; command should only be active on files for which there is something to
+;; commit. Also, some indication of which files the command applies to
+;; (especially in the presence of multiple marked files) would be nice.
+;;
+;; Middle-click runs find-file.
+
+
+;(require 'pcl-cvs)
+(load "pcl-cvs.el")
+
+(defvar cvs-menu
+ '("CVS"
+ ["Find File" cvs-mode-find-file t]
+ ["Find File Other Window" cvs-mode-find-file-other-window t]
+ ["Interactively Merge (emerge)" cvs-mode-emerge t]
+ ["Diff against Repository" cvs-mode-diff-cvs t]
+ ["Diff against Backup Version" cvs-mode-diff-backup t]
+ "----"
+ ["Commit Changes to Repository" cvs-mode-commit t]
+ ["Revert File from Repository" cvs-mode-undo-local-changes t]
+ ["Add File to Repository" cvs-mode-add t]
+ ["Remove File from Repository" cvs-mode-remove-file t]
+ ["Ignore File" cvs-mode-ignore t]
+ ["Hide File" cvs-mode-acknowledge t]
+ ["Hide Handled Files" cvs-mode-remove-handled t]
+ "----"
+ ["Add ChangeLog Entry" cvs-mode-add-change-log-entry-other-window t]
+ ["Show CVS Log" cvs-mode-log t]
+ ["Show CVS Status" cvs-mode-status t]
+ "----"
+ ["Mark File" cvs-mode-mark t]
+ ["Unmark File" cvs-mode-unmark t]
+ ["Mark All Files" cvs-mode-mark-all-files t]
+ ["Unmark All Files" cvs-mode-unmark-all-files t]
+ "----"
+ ["Quit" bury-buffer t]
+ ))
+
+(defun cvs-menu (e)
+ (interactive "e")
+ (mouse-set-point e)
+ (beginning-of-line)
+ (or (looking-at "^[* ] ") (error "No CVS file line here"))
+ (popup-menu cvs-menu))
+
+(defun cvs-mouse-find-file (e)
+ (interactive "e")
+ (mouse-set-point e)
+ (beginning-of-line)
+ (or (looking-at "^[* ] ") (error "No CVS file line here"))
+ (cvs-mode-find-file (point)))
+
+(define-key cvs-mode-map 'button3 'cvs-menu)
+(define-key cvs-mode-map 'button2 'cvs-mouse-find-file)
+
+(make-face 'cvs-header-face)
+(make-face 'cvs-filename-face)
+(make-face 'cvs-status-face)
+
+(or (face-differs-from-default-p 'cvs-header-face)
+ (copy-face 'italic 'cvs-header-face))
+
+(or (face-differs-from-default-p 'cvs-filename-face)
+ (copy-face 'bold 'cvs-filename-face))
+
+(or (face-differs-from-default-p 'cvs-status-face)
+ (copy-face 'bold-italic 'cvs-status-face))
+
+
+(defun pcl-mode-motion-highlight-line (event)
+ (if (save-excursion
+ (let* ((window (event-window event))
+ (buffer (and window (window-buffer window)))
+ (point (and buffer (event-point event))))
+ (and point
+ (progn
+ (set-buffer buffer)
+ (goto-char point)
+ (beginning-of-line)
+ (looking-at "^[* ] ")))))
+ (mode-motion-highlight-line event)))
+
+(defconst pcl-cvs-font-lock-keywords
+ '(("^In directory \\(.+\\)$" 1 cvs-header-face)
+ ("^[* ] \\w+ +\\(ci\\)" 1 cvs-status-face)
+ ("^[* ] \\(Conflict\\|Merged\\)" 1 cvs-status-face)
+ ("^[* ] \\w+ +\\(ci +\\)?\\(.+\\)$" 2 cvs-filename-face)
+ )
+ "Patterns to highlight in the *cvs* buffer.")
+
+(defun pcl-cvs-fontify ()
+ ;;
+ ;; set up line highlighting
+ (require 'mode-motion)
+ (setq mode-motion-hook 'pcl-mode-motion-highlight-line)
+ ;;
+ ;; set up menubar
+ (if (and current-menubar (not (assoc "CVS" current-menubar)))
+ (progn
+ (set-buffer-menubar (copy-sequence current-menubar))
+ (add-menu nil "CVS" (cdr cvs-menu))))
+ ;;
+ ;; fontify mousable lines
+ (set (make-local-variable 'font-lock-keywords) pcl-cvs-font-lock-keywords)
+ (font-lock-mode 1)
+ )
+
+(add-hook 'cvs-mode-hook 'pcl-cvs-fontify)
--- /dev/null
+;;;#ident "@(#)OrigId: pcl-cvs-startup.el,v 1.4 1993/05/31 18:40:33 ceder Exp "
+;;;
+;;;#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs-startup.el,v 1.1.1.1 1996/05/06 22:20:48 tholo 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)
+
+(autoload 'cvs-update-other-window "pcl-cvs"
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer, display it in the other window, and run
+cvs-mode on it.
+
+If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+ t)
--- /dev/null
+;;;
+;;;#ident "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp "
+;;;
+;;;#ident "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.1.1.1 1996/05/06 22:20:47 tholo Exp $"
+;;;
+;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later.
+;;; Release 1.05-CVS-$Name: $.
+;;; Copyright (C) 1991, 1992, 1993 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.
+
+;;; This package requires ELIB-1.0 to run. Elib is included in the
+;;; CVS distribution in the contrib/elib/ subdirectory, but you can
+;;; also download it at the following URL:
+;;;
+;;; ftp://ftp.lysator.liu.se/pub/emacs/elib-1.0.tar.gz
+;;;
+
+;;; There is an TeXinfo file that describes this package. You should read it
+;;; to get the most from this package.
+
+;;; Mail questions and bug reports regarding this version (as included in
+;;; CVS-1.7 or newer) to the pcl-cvs support team at <pcl-cvs@cyclic.com>.
+
+;;; Don't try to use this with CVS 1.2 or earlier. It won't work. Get CVS 1.7
+;;; or newer. Use the version of RCS best suited for the version of CVS you're
+;;; using.
+
+(require 'cookie) ; from ELIB-1.0
+(require 'add-log) ; for all the ChangeLog goodies
+
+;;; -------------------------------------------------------
+;;; START OF THINGS TO CHECK WHEN INSTALLING
+
+;; also use $GNU here, since may folks might install CVS as a GNU package
+;;
+(defvar local-path (cond
+ ((getenv "LOCAL")
+ (getenv "LOCAL"))
+ ((getenv "GNU")
+ (getenv "GNU"))
+ (t
+ "/usr/local"))
+ "*Path prefix for most locally installed things.")
+
+;; this isn't likely to be right all the time....
+;;
+(defvar local-gnu-path (cond
+ ((getenv "GNU")
+ (getenv "GNU"))
+ (t
+ "/usr/local")) ; or "/usr/gnu"?
+ "*Path prefix for locally installed GNU software.")
+
+(defvar cvs-program (concat local-path "/bin/cvs")
+ "*Full path to the cvs executable.")
+
+;; SunOS-4.1.1_U1 has "diff.c 1.12 88/08/04 SMI; from UCB 4.6 86/04/03"
+;;
+(defvar cvs-diff-program (concat local-gnu-path "/bin/diff")
+ "*Full path to the best diff program you've got.
+NOTE: there are some nasty bugs in the context diff variants of some vendor
+versions, such as the one in SunOS-4.1.1_U1")
+
+(defvar cvs-rmdir-program "/bin/rmdir"
+ "*Full path to the rmdir program. Typically /bin/rmdir.")
+
+(defvar cvs-shell "/bin/sh"
+ "*Full path to a shell that can do redirection on stdout.")
+
+;;; Options to control various features:
+
+(defvar cvs-changelog-full-paragraphs t
+ "If non-nil, include full ChangeLog paragraphs in the CVS log.
+This may be set in the ``local variables'' section of a ChangeLog, to
+indicate the policy for that ChangeLog.
+
+A ChangeLog paragraph is a bunch of log text containing no blank lines;
+a paragraph usually describes a set of changes with a single purpose,
+but perhaps spanning several functions in several files. Changes in
+different paragraphs are unrelated.
+
+You could argue that the CVS log entry for a file should contain the
+full ChangeLog paragraph mentioning the change to the file, even though
+it may mention other files, because that gives you the full context you
+need to understand the change. This is the behaviour you get when this
+variable is set to t.
+
+On the other hand, you could argue that the CVS log entry for a change
+should contain only the text for the changes which occurred in that
+file, because the CVS log is per-file. This is the behaviour you get
+when this variable is set to nil.")
+
+(defvar cvs-cvsroot-required nil
+ "*Specifies whether CVS needs to be told where the repository is.
+
+In CVS 1.3, if your CVSROOT environment variable is not set, and you
+do not set the `cvs-cvsroot' lisp variable, CVS will have no idea
+where to find the repository, and refuse to run. CVS 1.4 and later
+store the repository path with the working directories, so most
+operations don't need to be told where the repository is.
+
+If you work with multiple repositories with CVS 1.4, it's probably
+advisable to leave your CVSROOT environment variable unset, set this
+variable to nil, and let CVS figure out where the repository is for
+itself.")
+
+(defvar cvs-cvsroot nil
+ "*Specifies where the (current) cvs master repository is.
+Overrides the $CVSROOT variable by sending \" -d dir\" to all cvs commands.
+This switch is useful if you have multiple CVS repositories, and are not using
+a modern version of CVS that stores the current repository in CVS/Root.")
+
+;; 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
+;;; --------------------------------------------------------
+
+(defconst pcl-cvs-version "1.05-CVS-$Name: $"
+ "A string denoting the current release version of pcl-cvs.")
+
+;; 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
+ "*Non-nil means don't display a Copyright message in the ``*cvs*'' buffer.")
+
+(defconst cvs-startup-message
+ (if cvs-inhibit-copyright-message
+ "PCL-CVS release 1.05-CVS-$Name: $"
+ "PCL-CVS release 1.05 from CVS release $Name: $.
+Copyright (C) 1992, 1993 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.")
+
+(defconst pcl-cvs-bugs-address "pcl-cvs-auto-bugs@cyclic.com"
+ "The destination address used for the default bug report form.")
+
+(defvar cvs-stdout-file nil
+ "Name of the file that holds the output that CVS sends to stdout.
+This variable is buffer local.")
+
+(defvar cvs-lock-file nil
+ "Full path to a lock file that CVS is waiting for (or was waiting for).")
+
+(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-mode-remove-handled should be called automatically.
+If this is set to any non-nil value, entries that do not need to be checked in
+will be removed from the *cvs* buffer after every cvs-mode-commit command.")
+
+(defvar cvs-auto-remove-handled-directories nil
+ "*Non-nil if cvs-mode-remove-handled and cvs-update should automatically
+remove empty directories.
+If this is set to any non-nil value, directories that do not contain any files
+to be checked in will be removed from the *cvs* buffer.")
+
+(defvar cvs-sort-ignore-file t
+ "*Non-nil if cvs-mode-ignore should sort the .cvsignore automatically.")
+
+(defvar cvs-auto-revert-after-commit t
+ "*Non-nil if committed buffers should be automatically reverted.")
+
+(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-commit-buffer-require-final-newline t
+ "*t says silently put a newline at the end of commit log messages.
+Non-nil but not t says ask user whether to add a newline in each such case.
+nil means don't add newlines.")
+
+(defvar cvs-temp-buffer-name "*cvs-tmp*"
+ "*Name of the cvs temporary buffer.
+Output from cvs is placed here by synchronous commands.")
+
+(defvar cvs-diff-ignore-marks nil
+ "*Non-nil if cvs-diff and cvs-mode-diff-backup should ignore any marked files.
+Normally they run diff on the files that are marked (with cvs-mode-mark),
+or the file under the cursor if no files are marked. If this variable
+is set to a non-nil value they will always run diff on the file on the
+current line.")
+
+;;; (setq cvs-status-flags '("-v"))
+(defvar cvs-status-flags '("-v")
+ "*List of flags to pass to ``cvs status''. Default is \"-v\".")
+
+;;; (setq cvs-log-flags nil)
+(defvar cvs-log-flags nil
+ "*List of flags to pass to ``cvs log''. Default is none.")
+
+;;; (setq cvs-tag-flags nil)
+(defvar cvs-tag-flags nil
+ "*List of extra flags to pass to ``cvs tag''. Default is none.")
+
+;;; (setq cvs-rtag-flags nil)
+(defvar cvs-rtag-flags nil
+ "*List of extra flags to pass to ``cvs rtag''. Default is none.")
+
+;;; (setq cvs-diff-flags '("-u"))
+(defvar cvs-diff-flags '("-u")
+ "*List of flags to use as flags to pass to ``diff'' and ``cvs diff''.
+Used by cvs-mode-diff-cvs and cvs-mode-diff-backup. Default is \"-u\".
+
+Set this to \"-u\" to get a Unidiff format, or \"-c\" to get context diffs.")
+
+;;; (setq cvs-update-optional-flags nil)
+(defvar cvs-update-optional-flags nil
+ "*List of strings to use as optional flags to pass to ``cvs update''. Used
+by cvs-do-update, called by cvs-update, cvs-update-other-window,
+cvs-mode-update-no-prompt, and cvs-examine. Default is none.
+
+For example set this to \"-j VENDOR_PREV_RELEASE -j VENDOR_TOP_RELEASE\" to
+perform an update after a new vendor release has been imported.
+
+To restrict the update to the current working directory, set this to \"-l\".")
+
+(defvar cvs-update-prog-output-skip-regexp "$"
+ "*A regexp that matches the end of the output from all cvs update programs.
+That is, output from any programs that are run by CVS (by the flag -u in the
+`modules' file - see cvs(5)) when `cvs update' is performed should terminate
+with a line that this regexp matches. It is enough that some part of the line
+is matched.
+
+The default (a single $) fits programs without output.")
+
+;;; --------------------------------------------------------
+;;; The variables below are used internally by pcl-cvs. You should
+;;; never change them.
+
+(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.")
+
+(defvar cvs-update-running nil
+ "This is set to nil when no process is running, and to
+the process when a cvs update process is running.")
+
+(defvar cvs-cookie-handle nil
+ "Handle for the cookie structure that is displayed in the *cvs* buffer.")
+
+(defvar cvs-commit-list nil
+ "Used internally by pcl-cvs.")
+
+;;; 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
+
+;;; 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 is a vector with the following fields:
+
+[0] handled True if this file doesn't require further action.
+[1] marked t/nil
+[2] type One of
+ UPDATED - file copied from repository
+ PATCHED - file update with patch 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 (if pcl-cvs did it)
+ REM-CONFLICT-removed in repository, but altered
+ locally.
+ MOD-CONFLICT-removed locally, changed in repository.
+ REM-EXIST - removed locally, but still exists.
+ DIRCHANGE - A change of directory.
+ UNKNOWN - An unknown file.
+ UNKNOWN-DIR- An unknown directory.
+ MOVE-AWAY - A file that is in the way.
+ REPOS-MISSING- The directory has vanished from the
+ repository.
+ MESSAGE - This is a special fileinfo that is used
+ to display a text that should be in
+ full-log.
+[3] dir Directory the file resides in. Should not end with slash.
+[4] file-name The file name.
+[5] backup-file The name of a backup file created during a merge.
+ Only valid for MERGED and CONFLICT files.
+[6] base-revision The revision that the working file was based on.
+ Only valid for MERGED and CONFLICT files.
+[7] head-revision The revision that the newly merged changes came from
+ Only valid for MERGED and CONFLICT files.
+[8] backup-revision The revision of the cvs backup file (original working rev.)
+ Only valid for MERGED and CONFLICT files.
+[9] cvs-diff-buffer A buffer that contains a 'cvs diff file'.
+[10] vendor-diff-buffer A buffer that contains a 'diff base-file head-file'.
+[11] backup-diff-buffer A buffer that contains a 'diff file backup-file'.
+[12] full-log The output from cvs, unparsed.
+[13] mod-time Modification time of file used for *-diff-buffer."
+
+ (cons
+ 'CVS-FILEINFO
+ (vector nil nil type dir file-name nil nil nil nil nil nil nil full-log nil 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, PATCHED, MODIFIED, ADDED, REMOVED, CVS-REMOVED, MERGED,
+CONFLICT, REM-CONFLICT, MOD-CONFLICT, REM-EXIST, DIRCHANGE, UNKNOWN,
+UNKNOWN-DIR, MOVE-AWAY, REPOS-MISSING or MESSAGE."
+ (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->base-revision (cvs-fileinfo)
+ "Get the base revision from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 6))
+
+(defun cvs-fileinfo->head-revision (cvs-fileinfo)
+ "Get the head revision from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 7))
+
+(defun cvs-fileinfo->backup-revision (cvs-fileinfo)
+ "Get the backup revision from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 8))
+
+(defun cvs-fileinfo->cvs-diff-buffer (cvs-fileinfo)
+ "Get cvs-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 9))
+
+(defun cvs-fileinfo->vendor-diff-buffer (cvs-fileinfo)
+ "Get backup-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 10))
+
+(defun cvs-fileinfo->backup-diff-buffer (cvs-fileinfo)
+ "Get backup-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 11))
+
+(defun cvs-fileinfo->full-log (cvs-fileinfo)
+ "Get full-log from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 12))
+
+(defun cvs-fileinfo->mod-time (cvs-fileinfo)
+ "Get mod-time from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 13))
+
+;;; 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->base-revision (cvs-fileinfo newval)
+ "Set base-revision in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 6 newval))
+
+(defun cvs-set-fileinfo->head-revision (cvs-fileinfo newval)
+ "Set head-revision in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 7 newval))
+
+(defun cvs-set-fileinfo->backup-revision (cvs-fileinfo newval)
+ "Set backup-revision in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 8 newval))
+
+(defun cvs-set-fileinfo->cvs-diff-buffer (cvs-fileinfo newval)
+ "Set cvs-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 9 newval))
+
+(defun cvs-set-fileinfo->vendor-diff-buffer (cvs-fileinfo newval)
+ "Set vendor-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 10 newval))
+
+(defun cvs-set-fileinfo->backup-diff-buffer (cvs-fileinfo newval)
+ "Set backup-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 11 newval))
+
+(defun cvs-set-fileinfo->full-log (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 12 newval))
+
+(defun cvs-set-fileinfo->mod-time (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 13 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."
+
+ (let ((dir default-directory))
+ (display-buffer (get-buffer-create cvs-temp-buffer-name))
+ (set-buffer cvs-temp-buffer-name)
+ (setq buffer-read-only nil)
+ (setq default-directory dir)
+ (erase-buffer)))
+
+;;----------
+(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.
+WARNING: this doesn't work very well yet...."
+
+ ;; TODO: this should do everything cvs-update does...
+ ;; for example, for CONFLICT files, it should setup fileinfo appropriately
+
+ (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)
+ (switch-to-buffer cvs-buffer-name))
+
+;;----------
+(defun cvs-update-other-window (directory &optional local)
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer, display it in the other window, 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 other window (directory): "
+ nil default-directory nil)
+ current-prefix-arg))
+ (cvs-do-update directory local nil)
+ (switch-to-buffer-other-window cvs-buffer-name))
+
+;;----------
+(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-mode-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.
+Args: DIRECTORY LOCAL DONT-CHANGE-DISC.
+
+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)
+ ;; Ensure that it is safe to do an update. If not, ask user
+ ;; for confirmation.
+ (if (and (boundp 'cvs-cookie-handle) (collection-buffer cvs-cookie-handle))
+ (if (collection-collect-tin
+ cvs-cookie-handle
+ '(lambda (cookie) (eq (cvs-fileinfo->type cookie) 'CONFLICT)))
+ (if (not
+ (yes-or-no-p
+ "Only update if conflicts have been resolved. Continue? "))
+ (error "Update aborted by user request."))))
+ (if (not (file-exists-p cvs-program))
+ (error "%s: file not found (check setting of cvs-program)"
+ cvs-program))
+ (let* ((this-dir (file-name-as-directory (expand-file-name directory)))
+ (update-buffer (generate-new-buffer
+ (concat " " (file-name-nondirectory
+ (substring this-dir 0 -1))
+ "-update")))
+ (temp-name (make-temp-name
+ (concat (file-name-as-directory
+ (or (getenv "TMPDIR") "/tmp"))
+ "pcl-cvs.")))
+ (args nil))
+
+ ;; Check that this-dir exists and is a directory that is under CVS contr.
+
+ (if (not (file-directory-p this-dir))
+ (error "%s is not a directory." this-dir))
+ (if (not (file-directory-p (concat this-dir "CVS")))
+ (error "%s does not contain CVS controlled files." this-dir))
+ (if (file-readable-p (concat this-dir "CVS/Root"))
+ (save-excursion ; read CVS/Root into cvs-cvsroot
+ (find-file (concat this-dir "CVS/Root"))
+ (goto-char (point-min))
+ (setq cvs-cvsroot (buffer-substring (point)
+ (progn (end-of-line) (point))))
+ (if (not cvs-cvsroot)
+ (error "Invalid contents of %sCVS/Root" this-dir))
+ (kill-buffer (current-buffer)))
+ (if (and cvs-cvsroot-required
+ (not (or (getenv "CVSROOT") cvs-cvsroot)))
+ (error "Both cvs-cvsroot and environment variable CVSROOT are unset, and no CVS/Root.")))
+
+ ;; Check that at most one `cvs update' is run at any time.
+
+ (if (and cvs-update-running (process-status cvs-update-running)
+ (or (eq (process-status cvs-update-running) 'run)
+ (eq (process-status cvs-update-running) 'stop)))
+ (error "Can't run two `cvs update' simultaneously."))
+
+ (if (not (listp cvs-update-optional-flags))
+ (error "cvs-update-optional-flags should be set using cvs-set-update-optional-flags"))
+
+ ;; Generate "-d /master -n update -l".
+ (setq args (concat (if cvs-cvsroot (concat " -d " cvs-cvsroot))
+ (if dont-change-disc " -n ")
+ " update "
+ (if local " -l ")
+ (if cvs-update-optional-flags
+ (mapconcat 'identity
+ (copy-sequence cvs-update-optional-flags)
+ " "))))
+
+ ;; Set up the buffer that receives the stderr output from "cvs update".
+ (set-buffer update-buffer)
+ (setq default-directory this-dir)
+ (make-local-variable 'cvs-stdout-file)
+ (setq cvs-stdout-file temp-name)
+
+ (setq cvs-update-running
+ (let ((process-connection-type nil)) ; Use a pipe, not a pty.
+ (start-process "cvs" update-buffer cvs-shell "-c"
+ (concat cvs-program " " args " > " temp-name))))
+
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status cvs-update-running))))
+ (set-buffer-modified-p (buffer-modified-p)) ; Update the mode line.
+ (set-process-sentinel cvs-update-running 'cvs-sentinel)
+ (set-process-filter cvs-update-running 'cvs-update-filter)
+ (set-marker (process-mark cvs-update-running) (point-min))
+
+ (save-excursion
+ (set-buffer (get-buffer-create cvs-buffer-name))
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (cvs-mode))
+
+ (setq cvs-cookie-handle
+ (collection-create
+ cvs-buffer-name 'cvs-pp
+ cvs-startup-message ;See comment above cvs-startup-message.
+ "---------- End -----"))
+
+ (cookie-enter-first
+ cvs-cookie-handle
+ (cvs-create-fileinfo
+ 'MESSAGE nil nil (concat "\n Running `cvs " args "' in " this-dir
+ "...\n")))
+
+ (save-excursion
+ (set-buffer cvs-buffer-name)
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status cvs-update-running))))
+ (set-buffer-modified-p (buffer-modified-p)) ; Update the mode line.
+ (setq buffer-read-only t))
+
+ ;; Work around a bug in emacs 18.57 and earlier.
+ (setq cvs-buffers-to-delete
+ (cvs-delete-unused-temporary-buffers cvs-buffers-to-delete)))
+
+ ;; The following line is said to improve display updates on some
+ ;; emacses. It shouldn't be needed, but it does no harm.
+ (sit-for 0))
+
+;;----------
+(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 front-end to CVS.
+
+To get to the \"*cvs*\" buffer you should use ``\\[execute-extended-command] cvs-update''.
+
+Full documentation is in the Texinfo file. Here are the most useful commands:
+
+\\[cvs-mode-previous-line] Move up. \\[cvs-mode-next-line] Move down.
+\\[cvs-mode-commit] Commit file. \\[cvs-mode-update-no-prompt] Re-update directory.
+\\[cvs-mode-mark] Mark file/dir. \\[cvs-mode-unmark] Unmark file/dir.
+\\[cvs-mode-mark-all-files] Mark all files. \\[cvs-mode-unmark-all-files] Unmark all files.
+\\[cvs-mode-find-file] Edit file/run Dired. \\[cvs-mode-find-file-other-window] Find file or run Dired in other window.
+\\[cvs-mode-ignore] Add file to ./.cvsignore. \\[cvs-mode-add-change-log-entry-other-window] Write ChangeLog in other window.
+\\[cvs-mode-add] Add to repository. \\[cvs-mode-remove-file] Remove file.
+\\[cvs-mode-diff-cvs] Diff with base revision. \\[cvs-mode-diff-backup] Diff backup file.
+\\[cvs-mode-ediff] Ediff base rev & backup. \\[cvs-mode-diff-vendor] Show merge from vendor branch.
+\\[cvs-mode-emerge] Emerge base rev & backup. \\[cvs-mode-diff-backup] Diff backup file.
+\\[cvs-mode-acknowledge] Delete line from buffer. \\[cvs-mode-remove-handled] Remove processed entries.
+\\[cvs-mode-log] Run ``cvs log''. \\[cvs-mode-status] Run ``cvs status''.
+\\[cvs-mode-tag] Run ``cvs tag''. \\[cvs-mode-rtag] Run ``cvs rtag''.
+\\[cvs-mode-changelog-commit] Like \\[cvs-mode-commit], but get default log text from ChangeLog.
+\\[cvs-mode-undo-local-changes] Revert the last checked in version - discard your changes to the file.
+
+Entry to this mode runs cvs-mode-hook.
+This description is updated for release 1.05-CVS-$Name: $ of pcl-cvs.
+
+All bindings:
+\\{cvs-mode-map}"
+
+ (interactive)
+ (setq major-mode 'cvs-mode)
+ (setq mode-name "CVS")
+ (setq mode-line-process nil)
+;; for older v18 emacs
+;;(buffer-flush-undo (current-buffer))
+ (buffer-disable-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))))
+ (let* ((out-file cvs-stdout-file)
+ (stdout-buffer (find-file-noselect out-file)))
+ (save-excursion
+ (set-buffer stdout-buffer)
+ (rename-buffer (concat " "
+ (file-name-nondirectory out-file)) t))
+ (cvs-parse-update stdout-buffer (process-buffer proc))
+ (setq cvs-buffers-to-delete
+ (cons (process-buffer proc)
+ (cons stdout-buffer
+ cvs-buffers-to-delete)))
+ (delete-file out-file)))
+ (set-buffer-modified-p (buffer-modified-p))
+ (setq cvs-update-running nil))
+ (if (equal obuf (process-buffer proc))
+ nil
+ (set-buffer (process-buffer proc))
+ (if (< opoint omax)
+ (goto-char opoint))
+ (set-buffer obuf))))))
+
+;;----------
+(defun cvs-update-filter (proc string)
+ "Filter function for pcl-cvs.
+This function gets the output that CVS sends to stderr. It inserts it
+into (process-buffer proc) but it also checks if CVS is waiting for a
+lock file. If so, it inserts a message cookie in the *cvs* buffer."
+
+ (let ((old-buffer (current-buffer))
+ (data (match-data)))
+ (unwind-protect
+ (progn
+ (set-buffer (process-buffer proc))
+ (save-excursion
+ ;; Insert the text, moving the process-marker.
+ (goto-char (process-mark proc))
+ (insert string)
+ (set-marker (process-mark proc) (point))
+ ;; Delete any old lock message
+ (if (tin-nth cvs-cookie-handle 1)
+ (tin-delete cvs-cookie-handle
+ (tin-nth cvs-cookie-handle 1)))
+ ;; Check if CVS is waiting for a lock.
+ (beginning-of-line 0) ;Move to beginning of last
+ ;complete line.
+ (cond
+ ((looking-at
+ "^cvs \\(update\\|server\\): \\[..:..:..\\] waiting for \\(.*\\)lock in \\(.*\\)$")
+ (setq cvs-lock-file (buffer-substring (match-beginning 3)
+ (match-end 3)))
+ (cookie-enter-last
+ cvs-cookie-handle
+ (cvs-create-fileinfo
+ 'MESSAGE nil nil
+ (concat "\tWaiting for "
+ (buffer-substring (match-beginning 2)
+ (match-end 2))
+ "lock in " cvs-lock-file
+ ".\n\t (type M-x cvs-delete-lock to delete it)")))))))
+ (store-match-data data)
+ (set-buffer old-buffer))))
+
+;;----------
+(defun cvs-delete-lock ()
+ "Delete the lock file that CVS is waiting for.
+Note that this can be dangerous. You should only do this
+if you are convinced that the process that created the lock is dead."
+
+ (interactive)
+ (cond
+ ((not (or (file-exists-p
+ (concat (file-name-as-directory cvs-lock-file) "#cvs.lock"))
+ (cvs-filter (function cvs-lock-file-p)
+ (directory-files cvs-lock-file))))
+ (error "No lock files found."))
+ ((yes-or-no-p (concat "Really delete locks in " cvs-lock-file "? "))
+ ;; Re-read the directory -- the locks might have disappeared.
+ (let ((locks (cvs-filter (function cvs-lock-file-p)
+ (directory-files cvs-lock-file))))
+ (while locks
+ (delete-file (concat (file-name-as-directory cvs-lock-file)
+ (car locks)))
+ (setq locks (cdr locks)))
+ (cvs-remove-directory
+ (concat (file-name-as-directory cvs-lock-file) "#cvs.lock"))))))
+
+;;----------
+(defun cvs-remove-directory (dir)
+ "Remove a directory."
+
+ (if (file-directory-p dir)
+ (call-process cvs-rmdir-program nil nil nil dir)
+ (error "Not a directory: %s" dir))
+ (if (file-exists-p dir)
+ (error "Could not remove directory %s" dir)))
+
+;;----------
+(defun cvs-lock-file-p (file)
+ "Return true if FILE looks like a CVS lock file."
+
+ (or
+ (string-match "^#cvs.tfl.[0-9]+$" file)
+ (string-match "^#cvs.rfl.[0-9]+$" file)
+ (string-match "^#cvs.wfl.[0-9]+$" file)))
+
+;;----------
+(defun cvs-quote-multiword-string (str)
+ "Return STR surrounded in single quotes if it contains whitespace."
+ (cond ((string-match "[ \t\n]" str)
+ (concat "'" str "'"))
+ (t
+ str)))
+
+;;----------
+;; this should be in subr.el or some similar place....
+(defun parse-string (str &optional regexp)
+ "Explode the string STR into a list of words ala strtok(3). Optional REGEXP
+defines regexp matching word separator, which defaults to \"[ \\t\\n]+\"."
+ (let (str-list ; new list
+ str-token ; "index" of next token
+ (str-start 0) ; "index" of current token
+ (str-sep (if regexp
+ regexp
+ "[ \t\n]+")))
+ (while (setq str-token (string-match str-sep str str-start))
+ (setq str-list
+ (nconc str-list
+ (list (substring str str-start str-token))))
+ (setq str-start (match-end 0)))
+ ;; tag on the remainder as the final item
+ (if (not (>= str-start (length str)))
+ (setq str-list
+ (nconc str-list
+ (list (substring str str-start)))))
+ str-list))
+
+;;----------
+(defun cvs-make-list (str)
+ "Return list of words made from the string STR."
+ (cond ((string-match "[ \t\n]+" str)
+ (let ((new-str (parse-string str "[ \t\n]+")))
+ ;; this is ugly, but assume if the first element is empty, there are
+ ;; no more elements.
+ (cond ((string= (car new-str) "")
+ nil)
+ (t
+ new-str))))
+ ((string= str "")
+ nil)
+ (t
+ (list str))))
+
+;;----------
+(defun cvs-skip-line (stdout stderr regexp &optional arg)
+ "Like forward-line, but check that the skipped line matches REGEXP.
+Args: STDOUT STDERR REGEXP &optional ARG.
+
+If it doesn't match REGEXP a bug report is generated and displayed.
+STDOUT and STDERR is only used to do that.
+
+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
+ (cvs-parse-error stdout
+ stderr
+ (if (eq (current-buffer) stdout)
+ 'STDOUT
+ 'STDERR)
+ (point)
+ regexp))))
+
+;;----------
+(defun cvs-get-current-dir (root-dir dirname)
+ "Return current working directory, suitable for cvs-parse-update.
+Args: ROOT-DIR DIRNAME.
+
+Concatenates ROOT-DIR and DIRNAME to form an absolute path."
+
+ (if (string= "." dirname)
+ (substring root-dir 0 -1)
+ (concat root-dir dirname)))
+
+;;----------
+(defun cvs-compare-fileinfos (a b)
+ "Compare fileinfo A with fileinfo B and return t if A is `less'."
+
+ (cond
+ ;; Sort acording to directories.
+ ((string< (cvs-fileinfo->dir a) (cvs-fileinfo->dir b)) t)
+ ((not (string= (cvs-fileinfo->dir a) (cvs-fileinfo->dir b))) nil)
+ ;; The DIRCHANGE entry is always first within the directory.
+ ((and (eq (cvs-fileinfo->type a) 'DIRCHANGE)
+ (not (eq (cvs-fileinfo->type b) 'DIRCHANGE))) t)
+ ((and (eq (cvs-fileinfo->type b) 'DIRCHANGE)
+ (not (eq (cvs-fileinfo->type a) 'DIRCHANGE))) nil)
+ ;; All files are sorted by file name.
+ ((string< (cvs-fileinfo->file-name a) (cvs-fileinfo->file-name b)))))
+
+;;----------
+(defun cvs-parse-error (stdout-buffer stderr-buffer err-buf pos &optional indicator)
+ "Handle a parse error when parsing the output from cvs.
+Args: STDOUT-BUFFER STDERR-BUFFER ERR-BUF POS &optional INDICATOR.
+
+ERR-BUF should be 'STDOUT or 'STDERR."
+
+ (setq pos (1- pos))
+ (set-buffer cvs-buffer-name)
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (insert "To: " pcl-cvs-bugs-address "\n")
+ (insert "Subject: pcl-cvs release" pcl-cvs-version " parse error.\n")
+ (insert (concat mail-header-separator "\n"))
+ (insert "This bug report is automatically generated by pcl-cvs\n")
+ (insert "because it doesn't understand some output from CVS. Below\n")
+ (insert "is detailed information about the error. Please send\n")
+ (insert "this, together with any information you think might be\n")
+ (insert "useful for me to fix the bug, to the address above. But\n")
+ (insert "please check the \"known problems\" section of the\n")
+ (insert "documentation first. Note that this buffer contains\n")
+ (insert "information that you might consider confidential. You\n")
+ (insert "are encouraged to read through it before sending it.\n")
+ (insert "\n")
+ (insert "Press C-c C-c to send this email.\n\n")
+ (insert "Please state the version of these programs you are using:\n\n")
+ (insert "RCS: \ndiff: \n\n")
+
+ (let* ((stdout (save-excursion (set-buffer stdout-buffer) (buffer-string)))
+ (stderr (save-excursion (set-buffer stderr-buffer) (buffer-string)))
+ (errstr (if (eq err-buf 'STDOUT) stdout stderr))
+ (errline-end (string-match "\n" errstr pos))
+ (errline (substring errstr pos errline-end)))
+ (insert (format "Offending line (%d chars): >" (- errline-end pos)))
+ (insert errline)
+ (insert "<\n")
+ (insert "Sent to " (symbol-name err-buf) " at pos " (format "%d\n" pos))
+ (if indicator
+ (insert "Optional args: \"" indicator "\".\n"))
+ (insert "\nEmacs-version: " (emacs-version) "\n")
+ (insert "Pcl-cvs Version: "
+ "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp\n")
+ (insert "CVS Version: "
+ "@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.el,v 1.1.1.1 1996/05/06 22:20:47 tholo Exp $\n\n")
+ (insert (format "--- Contents of stdout buffer (%d chars) ---\n"
+ (length stdout)))
+ (insert stdout)
+ (insert "--- End of stdout buffer ---\n")
+ (insert (format "--- Contents of stderr buffer (%d chars) ---\n"
+ (length stderr)))
+ (insert stderr)
+ (insert "--- End of stderr buffer ---\n")
+ (insert "\nEnd of bug report.\n")
+ (require 'sendmail)
+ (mail-mode)
+ (error "CVS parse error - please report this bug.")))
+
+;;----------
+(defun cvs-parse-update (stdout-buffer stderr-buffer)
+ "Parse the output from `cvs update'.
+
+Args: STDOUT-BUFFER STDERR-BUFFER.
+
+This functions parses the from `cvs update' (which should be
+separated in its stdout- and stderr-components) and prints a
+pretty representation of it in the *cvs* buffer.
+
+Signals an error if unexpected output was detected in the buffer."
+
+ (let* ((head (cons 'dummy nil))
+ (tail (cvs-parse-stderr stdout-buffer stderr-buffer
+ head default-directory))
+ (root-dir default-directory))
+ (cvs-parse-stdout stdout-buffer stderr-buffer tail root-dir)
+ (setq head (sort (cdr head) (function cvs-compare-fileinfos)))
+ (collection-clear cvs-cookie-handle)
+ (collection-append-cookies cvs-cookie-handle head)
+ (cvs-remove-stdout-shadows)
+ (if cvs-auto-remove-handled-directories
+ (cvs-remove-empty-directories))
+ (set-buffer cvs-buffer-name)
+ (cvs-mode)
+ (goto-char (point-min))
+ (tin-goto-previous cvs-cookie-handle (point-min) 1)
+ (setq default-directory root-dir)))
+
+;;----------
+(defun cvs-remove-stdout-shadows ()
+ "Remove entries in the *cvs* buffer that comes from both stdout and stderr.
+If there is two entries for a single file the second one should be
+deleted. (Remember that sort uses a stable sort algorithm, so one can
+be sure that the stderr entry is always first)."
+
+ (collection-filter-tins cvs-cookie-handle
+ (function
+ (lambda (tin)
+ (not (cvs-shadow-entry-p tin))))))
+
+;;----------
+(defun cvs-shadow-entry-p (tin)
+ "Return non-nil if TIN is a shadow entry.
+Args: TIN.
+
+A TIN is a shadow entry if the previous tin contains the same file."
+
+ (let* ((previous-tin (tin-previous cvs-cookie-handle tin))
+ (curr (tin-cookie cvs-cookie-handle tin))
+ (prev (and previous-tin
+ (tin-cookie cvs-cookie-handle previous-tin))))
+ (and
+ prev curr
+ (string= (cvs-fileinfo->file-name prev)
+ (cvs-fileinfo->file-name curr))
+ (string= (cvs-fileinfo->dir prev)
+ (cvs-fileinfo->dir curr))
+ (or
+ (and (eq (cvs-fileinfo->type prev) 'CONFLICT)
+ (eq (cvs-fileinfo->type curr) 'CONFLICT))
+ (and (eq (cvs-fileinfo->type prev) 'MERGED)
+ (eq (cvs-fileinfo->type curr) 'MODIFIED))
+ (and (eq (cvs-fileinfo->type prev) 'REM-EXIST)
+ (eq (cvs-fileinfo->type curr) 'REMOVED))))))
+
+;;----------
+(defun cvs-find-backup-file (filename &optional dirname)
+ "Look for a backup file for FILENAME, optionally in directory DIRNAME, and if
+there is one, return the name of the first file found as a string."
+
+ (if (eq dirname nil)
+ (setq dirname default-directory))
+ (car (directory-files dirname nil (concat "^\\" cvs-bakprefix filename
+ "\\."))))
+
+;;----------
+(defun cvs-find-backup-revision (filename)
+ "Take FILENAME as the name of a cvs backup file and return the revision of
+that file as a string."
+
+ (substring filename
+ (+ 1 (string-match "\\.\\([0-9.]+\\)$" filename))))
+
+;;----------
+(defun cvs-parse-stderr (stdout-buffer stderr-buffer head dir)
+ "Parse the output from CVS that is written to stderr.
+Args: STDOUT-BUFFER STDERR-BUFFER HEAD DIR
+
+STDOUT-BUFFER holds the output that cvs sent to stdout. It is only
+used to create a bug report in case there is a parse error.
+STDERR-BUFFER is the buffer that holds the output to parse.
+HEAD is a cons-cell, the head of the list that is built.
+DIR is the directory the `cvs update' was run in.
+
+This function returns the last cons-cell in the list that is built."
+
+ (save-window-excursion
+ (set-buffer stderr-buffer)
+ (goto-char (point-min))
+ (let ((current-dir dir)
+ (root-dir dir))
+
+ (while (< (point) (point-max))
+ (cond
+
+ ;; CVS is descending a subdirectory.
+
+ ((looking-at
+ "^cvs \\(server\\|update\\): Updating \\(.*\\)$")
+ (setq current-dir
+ (cvs-get-current-dir
+ root-dir
+ (buffer-substring (match-beginning 2) (match-end 2))))
+ (setcdr head (list (cvs-create-fileinfo
+ 'DIRCHANGE
+ current-dir
+ "." ; the old version had nil here???
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; File removed, since it is removed (by third party) in repository.
+
+ ((or (looking-at
+ "^cvs \\(update\\|server\\): warning: \\(.*\\) is not (any longer) pertinent")
+ (looking-at
+ "^cvs \\(update\\|server\\): \\(.*\\) is no longer in the repository"))
+
+ (setcdr head (list (cvs-create-fileinfo
+ 'CVS-REMOVED
+ current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 2)
+ (match-end 2)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; File removed by you, but recreated by cvs. Ignored. Will say
+ ;; "Updated" on the next line.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): warning: .* was lost$")
+ (forward-line 1))
+
+ ;; File unknown for some reason.
+ ;; FIXME: is it really a good idea to add this as unknown here?
+
+ ((looking-at
+ "cvs \\(update\\|server\\): nothing known about \\(.*\\)$")
+ (let ((filename (buffer-substring (match-beginning 2)
+ (match-end 2))))
+ (if (file-directory-p filename)
+ (setcdr head (list (cvs-create-fileinfo
+ 'UNKNOWN-DIR
+ current-dir
+ "."
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setcdr head (list (cvs-create-fileinfo
+ 'UNKNOWN
+ current-dir
+ (file-name-nondirectory filename)
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; A file that has been created by you, but added to the cvs
+ ;; repository by another.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): move away \\(.*\\); it is in the way$")
+ (setcdr head (list (cvs-create-fileinfo
+ 'MOVE-AWAY
+ current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 2)
+ (match-end 2)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; Cvs waits for a lock. Ignore.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): \\[..:..:..\\] waiting for .*lock in ")
+ (forward-line 1))
+
+ ;; File removed in repository, but edited by you.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): conflict: \\(.*\\) is modified but no longer in the repository$")
+ (setcdr head (list
+ (cvs-create-fileinfo
+ 'REM-CONFLICT
+ current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 2)
+ (match-end 2)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; File removed in repository, but edited by someone else.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): conflict: removed \\(.*\\) was modified by second party")
+ (setcdr head
+ (list
+ (cvs-create-fileinfo
+ 'MOD-CONFLICT
+ current-dir
+ (buffer-substring (match-beginning 1)
+ (match-end 1))
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; File removed in repository, but not in local directory.
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): \\(.*\\) should be removed and is still there")
+ (setcdr head
+ (list
+ (cvs-create-fileinfo
+ 'REM-EXIST
+ current-dir
+ (buffer-substring (match-beginning 2)
+ (match-end 2))
+ (buffer-substring (match-beginning 0)
+ (match-end 0)))))
+ (setq head (cdr head))
+ (forward-line 1))
+
+ ;; Error searching for repository
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): in directory ")
+ (let ((start (point)))
+ (forward-line 1)
+ (cvs-skip-line stdout-buffer stderr-buffer
+ (regexp-quote "cvs [update aborted]: there is no repository "))
+ (setcdr head (list (cvs-create-fileinfo
+ 'REPOS-MISSING
+ current-dir
+ nil
+ (buffer-substring start (point)))))
+ (setq head (cdr head))))
+
+ ;; Silly warning from attempted conflict resolution. Ignored.
+ ;; FIXME: Should it be?
+ ;; eg.: "cvs update: cannot find revision APC-web-update in file .cvsignore"
+ ;;
+ ((looking-at
+ "^cvs \\(update\\|server\\): cannot find revision \\(.*\\) in file \\(.*\\)$")
+ (forward-line 1)
+ (message "%s" (buffer-substring (match-beginning 0) (match-end 0))))
+
+ ;; CVS has decided to merge someone elses changes into this document.
+ ;; About to start an rcsmerge operation...
+ ;;
+ ((looking-at
+ "^RCS file: ")
+
+ ;; skip the "RCS file:" line...
+ (forward-line 1)
+
+ (let ((complex-start (point))
+ base-revision ; the first revision retrieved to merge from
+ head-revision ; the second revision retrieved to merge from
+ filename ; the name of the file being merged
+ backup-file ; the name of the backup of the working file
+ backup-revision) ; the revision of the original working file
+
+ (setq base-revision
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^retrieving revision \\(.*\\)$"
+ 1))
+ (setq head-revision
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^retrieving revision \\(.*\\)$"
+ 1))
+ (setq filename
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$"
+ 1))
+ (setq backup-file
+ (cvs-find-backup-file filename current-dir))
+ (setq backup-revision
+ (cvs-find-backup-revision backup-file))
+
+ ;; Was there a conflict during the merge?
+
+ (cond
+
+ ;;;; From CVS-1.3 & RCS-5.6.0.1 with GNU-Diffutils-2.5:
+ ;;;; "cvs update -j OLD-REV -j NEW-REV ."
+ ;;
+ ;; RCS file: /big/web-CVS/apc/cmd/Main/logout.sh,v
+ ;; retrieving revision 1.1.1.1
+ ;; retrieving revision 1.1.1.2
+ ;; Merging differences between 1.1.1.1 and 1.1.1.2 into logout.sh
+ ;; rcsmerge warning: overlaps during merge
+
+ ((looking-at
+ ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning").
+ "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\((overlaps\\|conflicts\\) during merge$")
+
+ ;; Yes, this is a conflict.
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\(overlaps\\|conflicts\\) during merge$")
+
+ ;; this line doesn't seem to appear in all cases -- perhaps only
+ ;; in "-j A -j B" usage, in which case this indicates ????
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^cvs \\(update\\|server\\): conflicts found in ")
+
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'CONFLICT current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+
+ ;; squirrel away info about the files that were retrieved for merging
+ (cvs-set-fileinfo->base-revision fileinfo base-revision)
+ (cvs-set-fileinfo->head-revision fileinfo head-revision)
+ (cvs-set-fileinfo->backup-revision fileinfo backup-revision)
+ (cvs-set-fileinfo->backup-file fileinfo backup-file)
+
+ (setcdr head (list fileinfo))
+ (setq head (cdr head))))
+
+ ;; Was it a conflict, and was RCS compiled without DIFF3_BIN, in
+ ;; which case this is a failed conflict resolution?
+
+ ((looking-at
+ ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning").
+ "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other problems during merge$")
+
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other problems during merge$")
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^cvs update: could not merge ")
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^cvs update: restoring .* from backup file ")
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'CONFLICT current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+ (setcdr head (list fileinfo))
+ (setq head (cdr head))))
+
+ ;; Not a conflict; it must be a succesful merge.
+
+ (t
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'MERGED current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+ (cvs-set-fileinfo->base-revision fileinfo base-revision)
+ (cvs-set-fileinfo->head-revision fileinfo head-revision)
+ (cvs-set-fileinfo->backup-revision fileinfo backup-revision)
+ (cvs-set-fileinfo->backup-file fileinfo backup-file)
+ (setcdr head (list fileinfo))
+ (setq head (cdr head)))))))
+
+ ;; Error messages from CVS (incomplete)
+
+ ((looking-at
+ "^cvs \\(update\\|server\\): \\(invalid option .*\\)$")
+ (error "Interface problem with CVS: %s"
+ (buffer-substring (match-beginning 2) (match-end 2))))
+
+ ;; network errors
+
+ ;; Kerberos connection attempted but failed. This is not
+ ;; really an error, as CVS will automatically fall back to
+ ;; rsh. Plus it tries kerberos, if available, even when rsh
+ ;; is what you really wanted.
+
+ ((looking-at
+ "^cvs update: kerberos connect:.*$")
+ (forward-line 1)
+ (message "Remote CVS: %s"
+ (buffer-substring (match-beginning 0) (match-end 0))))
+
+ ;; And when kerberos *does* fail, cvs prints out some stuff
+ ;; as it tries rsh. Ignore that stuff too.
+
+ ((looking-at
+ "^cvs update: trying to start server using rsh$")
+ (forward-line 1))
+
+ ((looking-at
+ "^\\([^:]*\\) Connection timed out")
+ (error "Remote CVS: %s"
+ (buffer-substring (match-beginning 0) (match-end 0))))
+
+ ((looking-at
+ "^Permission denied.")
+ (error "Remote CVS: %s"
+ (buffer-substring (match-beginning 0) (match-end 0))))
+
+ ((looking-at
+ "^cvs \\[update aborted\\]: premature end of file from server")
+ (error "Remote CVS: %s"
+ (buffer-substring (match-beginning 0) (match-end 0))))
+
+ ;; Empty line. Probably inserted by mistake by user (or developer :-)
+ ;; Ignore.
+
+ ((looking-at
+ "^$")
+ (forward-line 1))
+
+ ;; top-level parser (cond) default clause
+
+ (t
+ (cvs-skip-line stdout-buffer stderr-buffer
+ "^UN-MATCHABLE-OUTPUT"))))))
+
+ ;; cause this function to return the head of the parser output list
+ head)
+
+;;----------
+(defun cvs-parse-stdout (stdout-buffer stderr-buffer head root-dir)
+ "Parse the output from CVS that is written to stderr.
+Args: STDOUT-BUFFER STDERR-BUFFER HEAD ROOT-DIR
+
+STDOUT-BUFFER is the buffer that holds the output to parse.
+STDERR-BUFFER holds the output that cvs sent to stderr. It is only
+used to create a bug report in case there is a parse error.
+
+HEAD is a cons-cell, the head of the list that is built.
+ROOT-DIR is the directory the `cvs update' was run in.
+
+This function doesn't return anything particular."
+
+ (save-window-excursion
+ (set-buffer stdout-buffer)
+ (goto-char (point-min))
+ (while (< (point) (point-max))
+ (cond
+
+ ;; M: The file is modified by the user, and untouched in the repository.
+ ;; A: The file is "cvs add"ed, but not "cvs ci"ed.
+ ;; R: The file is "cvs remove"ed, but not "cvs ci"ed.
+ ;; C: Conflict (only useful if a join was done and stderr has info...)
+ ;; U: The file is copied from the repository.
+ ;; ?: Unknown file or directory.
+
+ ((looking-at
+ "^\\([MARCUP?]\\) \\(.*\\)$")
+ (let*
+ ((c (char-after (match-beginning 1)))
+ (full-path (concat (file-name-as-directory root-dir)
+ (buffer-substring (match-beginning 2)
+ (match-end 2))))
+ (isdir (file-directory-p full-path))
+ (fileinfo (cvs-create-fileinfo
+ (cond ((eq c ?M) 'MODIFIED)
+ ((eq c ?A) 'ADDED)
+ ((eq c ?R) 'REMOVED)
+ ((eq c ?C) 'CONFLICT)
+ ((eq c ?U) 'UPDATED)
+ ((eq c ?P) 'PATCHED)
+ ((eq c ??) (if isdir
+ 'UNKNOWN-DIR
+ 'UNKNOWN)))
+ (substring (file-name-directory full-path) 0 -1)
+ (file-name-nondirectory full-path)
+ (buffer-substring (match-beginning 0) (match-end 0)))))
+ ;; Updated and Patched files require no further action.
+ (if (memq c '(?U ?P))
+ (cvs-set-fileinfo->handled fileinfo t))
+
+ ;; Link this last on the list.
+ (setcdr head (list fileinfo))
+ (setq head (cdr head))
+ (forward-line 1)))
+
+ ;; Executing a program because of the -u option in modules.
+ ((looking-at
+ "^cvs \\(update\\|server\\): Executing")
+ ;; Skip by any output the program may generate to stdout.
+ ;; Note that pcl-cvs will get seriously confused if the
+ ;; program prints anything to stderr.
+ (re-search-forward cvs-update-prog-output-skip-regexp)
+ (forward-line 1))
+
+ (t
+ (cvs-parse-error stdout-buffer stderr-buffer 'STDOUT (point)
+ "cvs-parse-stdout"))))))
+
+;;----------
+(defun cvs-pp (fileinfo)
+ "Pretty print FILEINFO. Insert a printed representation in current buffer.
+For use by the cookie package."
+
+ (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")))
+ (insert
+ (cond
+ ((eq a 'UPDATED)
+ (format "%s Updated %s" s f))
+ ((eq a 'PATCHED)
+ (format "%s Patched %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 'UNKNOWN-DIR)
+ (format "%s Unknown dir %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 'REM-EXIST)
+ (format "%s Conflict: Removed by you, but still exists: %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 directory is missing! Remove this directory manually."))
+ ((eq a 'MESSAGE)
+ (cvs-fileinfo->full-log fileinfo))
+ (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-prefix-command 'cvs-mode-map-control-c-prefix)
+ (define-key cvs-mode-map "\C-?" 'cvs-mode-unmark-up)
+ (define-key cvs-mode-map "\C-k" 'cvs-mode-acknowledge)
+ (define-key cvs-mode-map "\C-n" 'cvs-mode-next-line)
+ (define-key cvs-mode-map "\C-p" 'cvs-mode-previous-line)
+ ;; ^C- keys are used to set various flags to control CVS features
+ (define-key cvs-mode-map "\C-c" 'cvs-mode-map-control-c-prefix)
+ (define-key cvs-mode-map "\C-c\C-c" 'cvs-change-cvsroot)
+ (define-key cvs-mode-map "\C-c\C-d" 'cvs-set-diff-flags)
+ (define-key cvs-mode-map "\C-c\C-l" 'cvs-set-log-flags)
+ (define-key cvs-mode-map "\C-c\C-s" 'cvs-set-status-flags)
+ (define-key cvs-mode-map "\C-c\C-u" 'cvs-set-update-optional-flags)
+ ;; M- keys are usually those that operate on modules
+ (define-key cvs-mode-map "\M-\C-?" 'cvs-mode-unmark-all-files)
+ (define-key cvs-mode-map "\M-C" 'cvs-mode-rcs2log) ; i.e. "Create a ChangeLog"
+ (define-key cvs-mode-map "\M-a" 'cvs-mode-admin)
+ (define-key cvs-mode-map "\M-c" 'cvs-mode-checkout)
+ (define-key cvs-mode-map "\M-o" 'cvs-mode-checkout-other-window)
+ (define-key cvs-mode-map "\M-p" 'cvs-mode-rdiff) ; i.e. "create a Patch"
+ (define-key cvs-mode-map "\M-r" 'cvs-mode-release)
+ (define-key cvs-mode-map "\M-t" 'cvs-mode-rtag)
+ ;; keys that operate on files
+ (define-key cvs-mode-map " " 'cvs-mode-next-line)
+ (define-key cvs-mode-map "?" 'describe-mode)
+ (define-key cvs-mode-map "A" 'cvs-mode-add-change-log-entry-other-window)
+ (define-key cvs-mode-map "B" 'cvs-mode-byte-compile-files)
+ (define-key cvs-mode-map "C" 'cvs-mode-changelog-commit)
+ (define-key cvs-mode-map "E" 'cvs-mode-emerge)
+ (define-key cvs-mode-map "G" 'cvs-update)
+ (define-key cvs-mode-map "M" 'cvs-mode-mark-all-files)
+ (define-key cvs-mode-map "Q" 'cvs-examine)
+ (define-key cvs-mode-map "R" 'cvs-mode-revert-updated-buffers)
+ (define-key cvs-mode-map "U" 'cvs-mode-undo-local-changes)
+ (define-key cvs-mode-map "a" 'cvs-mode-add)
+ (define-key cvs-mode-map "b" 'cvs-mode-diff-backup)
+ (define-key cvs-mode-map "c" 'cvs-mode-commit)
+ (define-key cvs-mode-map "d" 'cvs-mode-diff-cvs)
+ (define-key cvs-mode-map "e" 'cvs-mode-ediff)
+ (define-key cvs-mode-map "f" 'cvs-mode-find-file)
+ (define-key cvs-mode-map "g" 'cvs-mode-update-no-prompt)
+ (define-key cvs-mode-map "i" 'cvs-mode-ignore)
+ (define-key cvs-mode-map "l" 'cvs-mode-log)
+ (define-key cvs-mode-map "m" 'cvs-mode-mark)
+ (define-key cvs-mode-map "n" 'cvs-mode-next-line)
+ (define-key cvs-mode-map "o" 'cvs-mode-find-file-other-window)
+ (define-key cvs-mode-map "p" 'cvs-mode-previous-line)
+ (define-key cvs-mode-map "q" 'bury-buffer)
+ (define-key cvs-mode-map "r" 'cvs-mode-remove-file)
+ (define-key cvs-mode-map "s" 'cvs-mode-status)
+ (define-key cvs-mode-map "t" 'cvs-mode-tag)
+ (define-key cvs-mode-map "u" 'cvs-mode-unmark)
+ (define-key cvs-mode-map "v" 'cvs-mode-diff-vendor)
+ (define-key cvs-mode-map "x" 'cvs-mode-remove-handled))
+
+;;----------
+(defun cvs-get-marked (&optional ignore-marks ignore-contents)
+ "Return a list of all selected tins.
+Args: &optional IGNORE-MARKS IGNORE-CONTENTS.
+
+If there are any marked tins, and IGNORE-MARKS is nil, return them. Otherwise,
+if the cursor selects a directory, return all files in it, unless there are
+none, in which case just return the directory; or unless IGNORE-CONTENTS is not
+nil, in which case also just return the directory. 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?
+ ((and (not ignore-marks)
+ (collection-collect-tin cvs-cookie-handle 'cvs-fileinfo->marked)))
+ ;; Nope.
+ ((and (not ignore-contents)
+ (let ((sel (tin-locate cvs-cookie-handle (point))))
+ (cond
+ ;; If a directory is selected, all it members are returned.
+ ((and sel (eq (cvs-fileinfo->type (tin-cookie cvs-cookie-handle
+ sel))
+ 'DIRCHANGE))
+ (let ((retsel
+ (collection-collect-tin cvs-cookie-handle
+ 'cvs-dir-member-p
+ (cvs-fileinfo->dir (tin-cookie
+ cvs-cookie-handle sel)))))
+ (if retsel
+ retsel
+ (list sel))))
+ (t
+ (list sel))))))
+ (t
+ (list (tin-locate cvs-cookie-handle (point))))))
+
+;;----------
+(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 (tin)
+ "Return non-nil if TIN is a directory that is empty.
+Args: CVS-BUF TIN."
+
+ (and (eq (cvs-fileinfo->type (tin-cookie cvs-cookie-handle tin)) 'DIRCHANGE)
+ (or (not (tin-next cvs-cookie-handle tin))
+ (eq (cvs-fileinfo->type
+ (tin-cookie cvs-cookie-handle
+ (tin-next cvs-cookie-handle tin)))
+ 'DIRCHANGE))))
+
+;;----------
+(defun cvs-mode-revert-updated-buffers ()
+ "Revert any buffers that are UPDATED, PATCHED, MERGED or CONFLICT."
+
+ (interactive)
+ (cookie-map (function cvs-revert-fileinfo) cvs-cookie-handle))
+
+;;----------
+(defun cvs-revert-fileinfo (fileinfo)
+ "Revert the buffer that holds the file in FILEINFO if it has changed,
+and if the type is UPDATED, PATCHED, MERGED or CONFLICT."
+
+ (let* ((type (cvs-fileinfo->type fileinfo))
+ (file (cvs-fileinfo->full-path fileinfo))
+ (buffer (get-file-buffer file)))
+ ;; For a revert to happen...
+ (cond
+ ((and
+ ;; ...the type must be one that justifies a revert...
+ (or (eq type 'UPDATED)
+ (eq type 'PATCHED)
+ (eq type 'MERGED)
+ (eq type 'CONFLICT))
+ ;; ...and the user must be editing the file...
+ buffer)
+ (save-excursion
+ (set-buffer buffer)
+ (cond
+ ((buffer-modified-p)
+ (error "%s: edited since last cvs-update."
+ (buffer-file-name)))
+ ;; Go ahead and revert the file.
+ (t (revert-buffer 'dont-use-auto-save-file 'dont-ask))))))))
+
+;;----------
+(defun cvs-mode-remove-handled ()
+ "Remove all lines that are handled.
+Empty directories are removed."
+
+ (interactive)
+ ;; Pass one: remove files that are handled.
+ (collection-filter-cookies cvs-cookie-handle
+ (function
+ (lambda (fileinfo)
+ (not (cvs-fileinfo->handled fileinfo)))))
+ ;; Pass two: remove empty directories.
+ (if cvs-auto-remove-handled-directories
+ (cvs-remove-empty-directories)))
+
+;;----------
+(defun cvs-remove-empty-directories ()
+ "Remove empty directories in the *cvs* buffer."
+
+ (collection-filter-tins cvs-cookie-handle
+ (function
+ (lambda (tin)
+ (not (cvs-dir-empty-p tin))))))
+
+;;----------
+(defun cvs-mode-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 (tin-locate cvs-cookie-handle pos))
+ (sel (tin-cookie cvs-cookie-handle 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.
+ cvs-cookie-handle
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel t)
+ (tin-invalidate cvs-cookie-handle tin)
+ (tin-goto-next cvs-cookie-handle pos 1)))))
+
+;;----------
+(defun cvs-committable (tin)
+ "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 (tin-cookie cvs-cookie-handle 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-mode-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))))
+ (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)
+ (message "Press C-c C-c when you are done editing."))))
+
+;;----------
+(defun cvs-edit-done ()
+ "Commit the files to the repository."
+
+ (interactive)
+ (if (null cvs-commit-list)
+ (error "You have already committed the files"))
+ (if (and (> (point-max) 1)
+ (/= (char-after (1- (point-max))) ?\n)
+ (or (eq cvs-commit-buffer-require-final-newline t)
+ (and cvs-commit-buffer-require-final-newline
+ (yes-or-no-p
+ (format "Buffer %s does not end in newline. Add one? "
+ (buffer-name))))))
+ (save-excursion
+ (goto-char (point-max))
+ (insert ?\n)))
+ (save-some-buffers)
+ (let ((cc-list cvs-commit-list)
+ (cc-buffer (get-buffer cvs-buffer-name))
+ (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...")
+ (if (cvs-execute-list cc-list cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "commit" "-m" msg)
+ (list "commit" "-m" msg))
+ "Committing %s...")
+ (error "Something went wrong. Check the %s buffer carefully."
+ cvs-temp-buffer-name))
+ ;; FIXME: don't do any of this if the commit fails.
+ (let ((ccl cc-list))
+ (while ccl
+ (cvs-after-commit-function (tin-cookie cvs-cookie-handle (car ccl)))
+ (setq ccl (cdr ccl))))
+ (apply 'tin-invalidate cvs-cookie-handle cc-list)
+ (set-buffer msg-buffer)
+ (setq cvs-commit-list nil)
+ (set-buffer cc-buffer)
+ (if cvs-auto-remove-handled
+ (cvs-mode-remove-handled)))
+
+ (message "Committing... Done."))
+
+;;----------
+(defun cvs-after-commit-function (fileinfo)
+ "Do everything that needs to be done when FILEINFO has been committed.
+The fileinfo->handle is set, and if the buffer is present it is reverted."
+
+ (cvs-set-fileinfo->handled fileinfo t)
+ (if cvs-auto-revert-after-commit
+ (let* ((file (cvs-fileinfo->full-path fileinfo))
+ (buffer (get-file-buffer file)))
+ ;; For a revert to happen...
+ (if buffer
+ ;; ...the user must be editing the file...
+ (save-excursion
+ (set-buffer buffer)
+ (if (not (buffer-modified-p))
+ ;; ...but it must be unmodified.
+ (revert-buffer 'dont-use-auto-save-file 'dont-ask)))))))
+
+;;----------
+(defun cvs-execute-list (tin-list program constant-args &optional message-fmt)
+ "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).
+
+Any output from PROGRAM will be inserted in the current buffer.
+
+This function return nil if all went well, or the numerical exit status or a
+signal name as a string. Note that PROGRAM might be called several times. This
+will return non-nil if something goes wrong, but there is no way to know which
+process that failed.
+
+If MESSAGE-FMT is not nil, then message is called to display progress with
+MESSAGE-FMT as the string. MESSAGE-FMT should contain one %s for the arg-list
+being passed to PROGRAM."
+
+ ;; FIXME: something seems wrong with the error checking here....
+
+ (let ((exitstatus nil))
+ (while tin-list
+ (let ((current-dir (cvs-fileinfo->dir (tin-cookie cvs-cookie-handle
+ (car tin-list))))
+ arg-list
+ arg-str)
+
+ ;; Collect all marked files in this directory.
+
+ (while (and tin-list
+ (string= current-dir
+ (cvs-fileinfo->dir (tin-cookie cvs-cookie-handle
+ (car tin-list)))))
+ (setq arg-list
+ (cons (cvs-fileinfo->file-name
+ (tin-cookie cvs-cookie-handle (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.
+
+ (if message-fmt
+ (message message-fmt
+ (mapconcat 'cvs-quote-multiword-string
+ arg-list
+ " ")))
+ (setq default-directory (file-name-as-directory current-dir))
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (let ((res (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))))
+ ;; Remember the first, or highest, exitstatus.
+ (if (and (not (and (integerp res) (zerop res)))
+ (or (null exitstatus)
+ (and (integerp exitstatus) (= 1 exitstatus))))
+ (setq exitstatus res)))
+ (goto-char (point-max))
+ (if message-fmt
+ (message message-fmt
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (copy-sequence arg-list) '("Done."))
+ " ")))
+ exitstatus))))
+
+;;----------
+;;;; +++ not currently used!
+(defun cvs-execute-single-file-list (tin-list extractor program constant-args
+ &optional cleanup message-fmt)
+ "Run PROGRAM on all elements on TIN-LIST.
+Args: TIN-LIST EXTRACTOR PROGRAM CONSTANT-ARGS &optional CLEANUP.
+
+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.
+
+If CLEANUP is not nil, the filenames returned by EXTRACTOR are deleted.
+
+If MESSAGE-FMT is not nil, then message is called to display progress with
+MESSAGE-FMT as the string. MESSAGE-FMT should contain one %s for the arg-list
+being passed to PROGRAM."
+
+ (while tin-list
+ (let ((current-dir (file-name-as-directory
+ (cvs-fileinfo->dir
+ (tin-cookie cvs-cookie-handle
+ (car tin-list)))))
+ (arg-list
+ (funcall extractor
+ (tin-cookie cvs-cookie-handle (car tin-list)))))
+
+ ;; Execute the command unless extractor returned t.
+
+ (if (eq arg-list t)
+ nil
+ (setq default-directory current-dir)
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (if message-fmt
+ (message message-fmt (mapconcat 'cvs-quote-multiword-string
+ arg-list
+ " ")))
+ (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))
+ (goto-char (point-max))
+ (if message-fmt
+ (message message-fmt (mapconcat 'cvs-quote-multiword-string
+ (nconc arg-list '("Done."))
+ " ")))
+ (if cleanup
+ (while arg-list
+;;;; (kill-buffer ?????)
+ (delete-file (car arg-list))
+ (setq arg-list (cdr arg-list))))))
+ (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-edit-mode-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c" 'cvs-edit-mode-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c\C-c" 'cvs-edit-done))
+
+;;----------
+(defun cvs-diffable (tins)
+ "Return a list of all tins on TINS that it makes sense to run
+``cvs diff'' on."
+
+ ;; +++ There is an unnecessary (nreverse) here. Get the list the
+ ;; other way around instead!
+ (let ((result nil))
+ (while tins
+ (let ((type (cvs-fileinfo->type
+ (tin-cookie cvs-cookie-handle (car tins)))))
+ (if (or (eq type 'MODIFIED)
+ (eq type 'UPDATED)
+ (eq type 'PATCHED)
+ (eq type 'MERGED)
+ (eq type 'CONFLICT)
+ (eq type 'REMOVED) ;+++Does this line make sense?
+ (eq type 'ADDED)) ;+++Does this line make sense?
+ (setq result (cons (car tins) result)))
+ (setq tins (cdr tins))))
+ (nreverse result)))
+
+;;----------
+(defun cvs-mode-diff-cvs (&optional ignore-marks)
+ "Diff the selected files against the head revisions in the repository.
+
+If the variable cvs-diff-ignore-marks is non-nil any marked files will not be
+considered to be selected. An optional prefix argument will invert the
+influence from cvs-diff-ignore-marks.
+
+The flags in the variable cvs-diff-flags will be passed to ``cvs diff''.
+
+The resulting diffs are placed in the cvs-fileinfo->cvs-diff-buffer."
+
+ (interactive "P")
+ (if (not (listp cvs-diff-flags))
+ (error "cvs-diff-flags should be set using cvs-set-diff-flags."))
+ (save-some-buffers)
+ (message "cvsdiffing...")
+ (let ((marked-file-list (cvs-diffable
+ (cvs-get-marked
+ (or (and ignore-marks (not cvs-diff-ignore-marks))
+ (and (not ignore-marks) cvs-diff-ignore-marks))))))
+ (while marked-file-list
+ (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))
+ (local-def-directory (file-name-as-directory
+ (cvs-fileinfo->dir
+ (tin-cookie cvs-cookie-handle
+ (car marked-file-list))))))
+ (message "cvsdiffing %s..."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+
+ ;; FIXME: this seems messy to test and set buffer name at this point....
+ (if (not (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff))
+ (cvs-set-fileinfo->cvs-diff-buffer fileinfo-to-diff
+ (concat "*cvs-diff-"
+ (cvs-fileinfo->file-name
+ fileinfo-to-diff)
+ "-in-"
+ local-def-directory
+ "*")))
+ (display-buffer (get-buffer-create
+ (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff)))
+ (set-buffer (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff))
+ (setq buffer-read-only nil)
+ (setq default-directory local-def-directory)
+ (erase-buffer)
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== cvs %s\n\n"
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "diff")
+ '("diff"))
+ (copy-sequence cvs-diff-flags)
+ (list (cvs-fileinfo->file-name
+ fileinfo-to-diff)))
+ " ")))
+ (if (apply 'call-process cvs-program nil t t
+ (nconc (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "diff")
+ '("diff"))
+ (copy-sequence cvs-diff-flags)
+ (list (cvs-fileinfo->file-name fileinfo-to-diff))))
+ (message "cvsdiffing %s... Done."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+ (message "cvsdiffing %s... No differences found."
+ (cvs-fileinfo->file-name fileinfo-to-diff)))
+ (goto-char (point-max))
+ (setq marked-file-list (cdr marked-file-list)))))
+ (message "cvsdiffing... Done."))
+
+;;----------
+(defun cvs-mode-diff-backup (&optional ignore-marks)
+ "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.
+
+If the variable cvs-diff-ignore-marks is non-nil any marked files will
+not be considered to be selected. An optional prefix argument will
+invert the influence from cvs-diff-ignore-marks.
+
+The flags in cvs-diff-flags will be passed to ``diff''.
+
+The resulting diffs are placed in the cvs-fileinfo->backup-diff-buffer."
+
+ (interactive "P")
+ (if (not (listp cvs-diff-flags))
+ (error "cvs-diff-flags should be set using cvs-set-diff-flags."))
+ (save-some-buffers)
+ (let ((marked-file-list (cvs-filter
+ (function cvs-backup-diffable)
+ (cvs-get-marked
+ (or
+ (and ignore-marks (not cvs-diff-ignore-marks))
+ (and (not ignore-marks) cvs-diff-ignore-marks))))))
+ (if (null marked-file-list)
+ (error "No ``Conflict'' or ``Merged'' file selected!"))
+ (message "backup diff...")
+ (while marked-file-list
+ (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))
+ (local-def-directory (file-name-as-directory
+ (cvs-fileinfo->dir
+ (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))))
+ (backup-temp-files (cvs-diff-backup-extractor
+ (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))))
+ (message "backup diff %s..."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+
+ ;; FIXME: this seems messy to test and set buffer name at this point....
+ (if (not (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff))
+ (cvs-set-fileinfo->backup-diff-buffer fileinfo-to-diff
+ (concat "*cvs-diff-"
+ (cvs-fileinfo->backup-file
+ fileinfo-to-diff)
+ "-to-"
+ (cvs-fileinfo->file-name
+ fileinfo-to-diff)
+ "-in"
+ local-def-directory
+ "*")))
+ (display-buffer (get-buffer-create
+ (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff)))
+ (set-buffer (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff))
+ (setq buffer-read-only nil)
+ (setq default-directory local-def-directory)
+ (erase-buffer)
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ cvs-diff-program
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (copy-sequence cvs-diff-flags)
+ backup-temp-files)
+ " ")))
+ (apply 'call-process cvs-diff-program nil t t
+ (nconc (copy-sequence cvs-diff-flags) backup-temp-files))
+ (goto-char (point-max))
+ (message "backup diff %s... Done."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+ (setq marked-file-list (cdr marked-file-list)))))
+ (message "backup diff... Done."))
+
+;;----------
+(defun cvs-mode-diff-vendor (&optional ignore-marks)
+ "Diff the revisions merged into the current file. I.e. show what changes
+were merged in.
+
+This command can be used on files that are marked with \"Merged\"
+or \"Conflict\" in the *cvs* buffer.
+
+If the variable cvs-diff-ignore-marks is non-nil any marked files will
+not be considered to be selected. An optional prefix argument will
+invert the influence from cvs-diff-ignore-marks.
+
+The flags in cvs-diff-flags will be passed to ``diff''.
+
+The resulting diffs are placed in the cvs-fileinfo->vendor-diff-buffer."
+
+ (interactive "P")
+ (if (not (listp cvs-diff-flags))
+ (error "cvs-diff-flags should be set using cvs-set-diff-flags."))
+ (save-some-buffers)
+ (let ((marked-file-list (cvs-filter
+ (function cvs-vendor-diffable)
+ (cvs-get-marked
+ (or
+ (and ignore-marks (not cvs-diff-ignore-marks))
+ (and (not ignore-marks) cvs-diff-ignore-marks))))))
+ (if (null marked-file-list)
+ (error "No ``Conflict'' or ``Merged'' file selected!"))
+ (message "vendor diff...")
+ (while marked-file-list
+ (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))
+ (local-def-directory (file-name-as-directory
+ (cvs-fileinfo->dir
+ (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))))
+ (vendor-temp-files (cvs-diff-vendor-extractor
+ (tin-cookie cvs-cookie-handle
+ (car marked-file-list)))))
+ (message "vendor diff %s..."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+ (if (not (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff))
+ (cvs-set-fileinfo->vendor-diff-buffer fileinfo-to-diff
+ (concat "*cvs-diff-"
+ (cvs-fileinfo->file-name
+ fileinfo-to-diff)
+ "-of-"
+ (cvs-fileinfo->base-revision
+ fileinfo-to-diff)
+ "-to-"
+ (cvs-fileinfo->head-revision
+ fileinfo-to-diff)
+ "-in-"
+ local-def-directory
+ "*")))
+ (display-buffer (get-buffer-create
+ (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff)))
+ (set-buffer (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff))
+ (setq buffer-read-only nil)
+ (setq default-directory local-def-directory)
+ (erase-buffer)
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ cvs-diff-program
+ (mapconcat 'cvs-quote-multiword-string
+ (nconc (copy-sequence cvs-diff-flags)
+ vendor-temp-files)
+ " ")))
+ (apply 'call-process cvs-diff-program nil t t
+ (nconc (copy-sequence cvs-diff-flags) vendor-temp-files))
+ (goto-char (point-max))
+ (message "vendor diff %s... Done."
+ (cvs-fileinfo->file-name fileinfo-to-diff))
+ (while vendor-temp-files
+ (cvs-kill-buffer-visiting (car vendor-temp-files))
+ (delete-file (car vendor-temp-files))
+ (setq vendor-temp-files (cdr vendor-temp-files)))
+ (setq marked-file-list (cdr marked-file-list)))))
+ (message "vendor diff... Done."))
+
+;;----------
+(defun cvs-backup-diffable (tin)
+ "Check if the TIN is backup-diffable.
+It must have a backup file to be diffable."
+
+ (file-readable-p
+ (cvs-fileinfo->backup-file (tin-cookie cvs-cookie-handle tin))))
+
+;;----------
+(defun cvs-vendor-diffable (tin)
+ "Check if the TIN is vendor-diffable.
+It must have head and base revision info to be diffable."
+
+ (and
+ (cvs-fileinfo->base-revision (tin-cookie cvs-cookie-handle tin))
+ (cvs-fileinfo->head-revision (tin-cookie cvs-cookie-handle tin))))
+
+;;----------
+(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 (not (file-readable-p (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->backup-file fileinfo)
+ (cvs-fileinfo->file-name fileinfo)))
+
+;;----------
+(defun cvs-diff-vendor-extractor (fileinfo)
+ "Retrieve and return the filenames of the vendor branch revisions as a list.
+Signal an error if there is no info for the vendor revisions."
+
+ (list (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->base-revision
+ fileinfo))
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->head-revision
+ fileinfo))))
+
+;;----------
+(defun cvs-mode-find-file-other-window (pos)
+ "Select a buffer containing the file in another window.
+Args: POS."
+
+ (interactive "d")
+ (let ((tin (tin-locate cvs-cookie-handle pos)))
+ (if tin
+ (let ((type (cvs-fileinfo->type (tin-cookie cvs-cookie-handle 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
+ (tin-cookie cvs-cookie-handle tin))))
+ (dired-other-window default-directory)
+ (set-buffer obuf)
+ (setq default-directory odir)))
+ (t
+ (find-file-other-window (cvs-full-path tin)))))
+ (error "There is no file to find."))))
+
+;;----------
+(defun cvs-fileinfo->full-path (fileinfo)
+ "Return the full path for the file that is described in FILEINFO."
+
+ (concat
+ (file-name-as-directory
+ (cvs-fileinfo->dir fileinfo))
+ (cvs-fileinfo->file-name fileinfo)))
+
+;;----------
+(defun cvs-full-path (tin)
+ "Return the full path for the file that is described in TIN."
+
+ (cvs-fileinfo->full-path (tin-cookie cvs-cookie-handle tin)))
+
+;;----------
+(defun cvs-mode-find-file (pos)
+ "Select a buffer containing the file in another window.
+Args: POS."
+
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (tin (tin-locate cvs-cookie-handle pos)))
+ (if tin
+ (let* ((fileinfo (tin-cookie cvs-cookie-handle 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 tin)))))
+ (error "There is no file to find."))))
+
+;;----------
+(defun cvs-mode-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))))
+ cvs-cookie-handle))
+
+;;----------
+(defun cvs-mode-unmark (pos)
+ "Unmark a fileinfo.
+Args: POS."
+
+ (interactive "d")
+ (let* ((tin (tin-locate cvs-cookie-handle pos))
+ (sel (tin-cookie cvs-cookie-handle 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))))
+ cvs-cookie-handle
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel nil)
+ (tin-invalidate cvs-cookie-handle tin)
+ (tin-goto-next cvs-cookie-handle pos 1)))))
+
+;;----------
+(defun cvs-mode-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))
+ cvs-cookie-handle))
+
+;;----------
+(defun cvs-do-removal (tins)
+ "Remove files.
+Args: TINS.
+
+TINS is a list of tins that the user wants to delete. The files are deleted.
+If the type of the tin is 'UNKNOWN or 'UNKNOWN-DIR 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 (tin-cookie cvs-cookie-handle tin))
+ (filepath (cvs-full-path tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (if (or (eq type 'REMOVED)
+ (eq type 'CVS-REMOVED))
+ nil
+ ;; if it doesn't exist, as a file or directory, ignore it
+ (cond ((file-directory-p filepath)
+ (call-process cvs-rmdir-program nil nil nil filepath))
+ ((file-exists-p filepath)
+ (delete-file filepath)))
+ (if (or (eq type 'UNKNOWN)
+ (eq type 'UNKNOWN-DIR)
+ (eq type 'MOVE-AWAY))
+ (tin-delete cvs-cookie-handle tin)
+ (setq files-to-remove (cons tin files-to-remove))
+ (cvs-set-fileinfo->type fileinfo 'REMOVED)
+ (cvs-set-fileinfo->handled fileinfo nil)
+ (tin-invalidate cvs-cookie-handle tin))))
+ (setq tins (cdr tins)))
+ files-to-remove))
+ (t nil)))
+
+;;----------
+(defun cvs-mode-remove-file ()
+ "Remove all marked files."
+
+ (interactive)
+ (let ((files-to-remove (cvs-do-removal (cvs-get-marked))))
+ (if (null files-to-remove)
+ nil
+ (cvs-use-temp-buffer)
+ (message "removing from repository...")
+ (if (cvs-execute-list files-to-remove cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "remove")
+ '("remove"))
+ "removing %s from repository...")
+ (error "CVS exited with non-zero exit status.")
+ (message "removing from repository... Done.")))))
+
+;;----------
+(defun cvs-mode-undo-local-changes ()
+ "Undo local changes to all marked files.
+The file is removed and `cvs update FILE' is run."
+
+ (interactive)
+ (let ((tins-to-undo (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (mapcar 'cvs-insert-full-path tins-to-undo)
+ (cond
+ ((and tins-to-undo (yes-or-no-p (format "Undo changes to %d files? "
+ (length tins-to-undo))))
+ (let (files-to-update)
+ (while tins-to-undo
+ (let* ((tin (car tins-to-undo))
+ (fileinfo (tin-cookie cvs-cookie-handle tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond
+ ((or
+ (eq type 'UPDATED)
+ (eq type 'PATCHED)
+ (eq type 'MODIFIED)
+ (eq type 'MERGED)
+ (eq type 'CONFLICT)
+ (eq type 'CVS-REMOVED)
+ (eq type 'REM-CONFLICT)
+ (eq type 'MOVE-AWAY)
+ (eq type 'REMOVED))
+ (if (not (eq type 'REMOVED))
+ (delete-file (cvs-full-path tin)))
+ (setq files-to-update (cons tin files-to-update))
+ (cvs-set-fileinfo->type fileinfo 'UPDATED)
+ (cvs-set-fileinfo->handled fileinfo t)
+ (tin-invalidate cvs-cookie-handle tin))
+
+ ((eq type 'MOD-CONFLICT)
+ (error "Use cvs-mode-add instead on %s."
+ (cvs-fileinfo->file-name fileinfo)))
+
+ ((eq type 'REM-CONFLICT)
+ (error "Can't deal with a file you have removed and recreated."))
+
+ ((eq type 'DIRCHANGE)
+ (error "Undo on directories not supported (yet)."))
+
+ ((eq type 'ADDED)
+ (error "There is no old revision to get for %s"
+ (cvs-fileinfo->file-name fileinfo)))
+ (t (error "cvs-mode-undo-local-changes: can't handle an %s"
+ type)))
+
+ (setq tins-to-undo (cdr tins-to-undo))))
+ (cvs-use-temp-buffer)
+ (message "Re-getting files from repository...")
+ (if (cvs-execute-list files-to-update cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "update")
+ '("update"))
+ "Re-getting %s from repository...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Re-getting files from repository... Done.")))))))
+
+;;----------
+(defun cvs-mode-acknowledge ()
+ "Remove all marked files from the buffer."
+
+ (interactive)
+ (mapcar (function (lambda (tin)
+ (tin-delete cvs-cookie-handle tin)))
+ (cvs-get-marked)))
+
+;;----------
+(defun cvs-mode-unmark-up (pos)
+ "Unmark the file on the previous line.
+Takes one argument POS, a buffer position."
+
+ (interactive "d")
+ (let ((tin (tin-goto-previous cvs-cookie-handle pos 1)))
+ (cond
+ (tin
+ (cvs-set-fileinfo->marked (tin-cookie cvs-cookie-handle tin)
+ nil)
+ (tin-invalidate cvs-cookie-handle tin)))))
+
+;;----------
+(defun cvs-mode-previous-line (arg)
+ "Go to the previous line.
+If a prefix argument is given, move by that many lines."
+
+ (interactive "p")
+ (tin-goto-previous cvs-cookie-handle (point) arg))
+
+;;----------
+(defun cvs-mode-next-line (arg)
+ "Go to the next line.
+If a prefix argument is given, move by that many lines."
+
+ (interactive "p")
+ (tin-goto-next cvs-cookie-handle (point) arg))
+
+;;----------
+(defun cvs-add-file-update-buffer (tin)
+ "Sub-function to cvs-mode-add. Internal use only. Update the display. Return
+non-nil if `cvs add' should be called on this file.
+Args: TIN.
+
+Returns 'DIR, 'ADD, 'ADD-DIR, or 'RESURRECT."
+
+ (let ((fileinfo (tin-cookie cvs-cookie-handle tin)))
+ (cond
+ ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN-DIR)
+ (cvs-set-fileinfo->full-log fileinfo "new directory added with cvs-mode-add")
+ 'ADD-DIR)
+ ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN)
+ (cvs-set-fileinfo->type fileinfo 'ADDED)
+ (cvs-set-fileinfo->full-log fileinfo "new file added with cvs-mode-add")
+ (tin-invalidate cvs-cookie-handle tin)
+ 'ADD)
+ ((eq (cvs-fileinfo->type fileinfo) 'REMOVED)
+ (cvs-set-fileinfo->type fileinfo 'UPDATED)
+ (cvs-set-fileinfo->full-log fileinfo "file resurrected with cvs-mode-add")
+ (cvs-set-fileinfo->handled fileinfo t)
+ (tin-invalidate cvs-cookie-handle 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 list of lists.
+The first list is unknown tins that shall be `cvs add -m msg'ed.
+The second list is unknown directory tins that shall be `cvs add -m msg'ed.
+The third list is removed files that shall be `cvs add'ed (resurrected)."
+
+ (let (add add-dir resurrect)
+ (while candidates
+ (let ((type (cvs-add-file-update-buffer (car candidates))))
+ (cond ((eq type 'ADD)
+ (setq add (cons (car candidates) add)))
+ ((eq type 'ADD-DIR)
+ (setq add-dir (cons (car candidates) add-dir)))
+ ((eq type 'RESURRECT)
+ (setq resurrect (cons (car candidates) resurrect)))))
+ (setq candidates (cdr candidates)))
+ (list add add-dir resurrect)))
+
+;;----------
+(defun cvs-mode-add ()
+ "Add marked files to the cvs repository."
+
+ (interactive)
+ (let* ((buf (current-buffer))
+ (marked (cvs-get-marked))
+ (result (cvs-add-sub buf marked))
+ (added (car result))
+ (newdirs (car (cdr result)))
+ (resurrect (car (cdr (cdr result))))
+ (msg (if (or added newdirs)
+ (read-from-minibuffer "Enter description: "))))
+
+ (if (or resurrect (or added newdirs))
+ (cvs-use-temp-buffer))
+
+ (cond (resurrect
+ (message "Resurrecting files from repository...")
+ (if (cvs-execute-list resurrect
+ cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "add")
+ '("add"))
+ "Resurrecting %s from repository...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Resurrecting files from repository... Done."))))
+
+ (cond (added
+ (message "Adding new files to repository...")
+ (if (cvs-execute-list added
+ cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "add" "-m" msg)
+ (list "add" "-m" msg))
+ "Adding %s to repository...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Adding new files to repository... Done."))))
+
+ (cond (newdirs
+ (message "Adding new directories to repository...")
+ (if (cvs-execute-list newdirs
+ cvs-program
+ (if cvs-cvsroot
+ (list "-d" cvs-cvsroot "add" "-m" msg)
+ (list "add" "-m" msg))
+ "Adding %s to repository...")
+ (error "CVS exited with non-zero exit status.")
+ (while newdirs
+ (let* ((tin (car newdirs))
+ (fileinfo (tin-cookie cvs-cookie-handle tin))
+ (newdir (cvs-fileinfo->file-name fileinfo)))
+ (cvs-set-fileinfo->dir fileinfo
+ (concat (cvs-fileinfo->dir fileinfo)
+ "/"
+ newdir))
+ (cvs-set-fileinfo->type fileinfo 'DIRCHANGE)
+ (cvs-set-fileinfo->file-name fileinfo ".")
+ (tin-invalidate cvs-cookie-handle tin)
+ (setq newdirs (cdr newdirs))))
+ ;; FIXME: this should really run cvs-update-no-prompt on the
+ ;; subdir and insert everthing in the current list.
+ (message "You must re-update to visit the new directories."))))))
+
+;;----------
+(defun cvs-mode-ignore ()
+ "Arrange so that CVS ignores the selected files and directories.
+This command ignores files/dirs that are flagged as `Unknown'."
+
+ (interactive)
+ (mapcar (function (lambda (tin)
+ (let* ((fileinfo (tin-cookie cvs-cookie-handle tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond ((or (eq type 'UNKNOWN)
+ (eq type 'UNKNOWN-DIR))
+ (cvs-append-to-ignore fileinfo)
+ (tin-delete cvs-cookie-handle tin))))))
+ (cvs-get-marked)))
+
+;;----------
+(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")
+ (if cvs-sort-ignore-file
+ (sort-lines nil (point-min) (point-max)))
+ (save-buffer)))
+
+;;----------
+(defun cvs-mode-status ()
+ "Show cvs status for all marked files."
+
+ (interactive)
+ (save-some-buffers)
+ (if (not (listp cvs-status-flags))
+ (error "cvs-status-flags should be set using cvs-set-status-flags."))
+ (let ((marked (cvs-get-marked nil t)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs status ...")
+ (if (cvs-execute-list marked
+ cvs-program
+ (append (if cvs-cvsroot (list "-d" cvs-cvsroot))
+ (list "-Q" "status")
+ cvs-status-flags)
+ "Running cvs -Q status %s...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Running cvs -Q status ... Done."))))
+
+;;----------
+(defun cvs-mode-log ()
+ "Display the cvs log of all selected files."
+
+ (interactive)
+ (if (not (listp cvs-log-flags))
+ (error "cvs-log-flags should be set using cvs-set-log-flags."))
+ (let ((marked (cvs-get-marked nil t)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs log ...")
+ (if (cvs-execute-list marked
+ cvs-program
+ (append (if cvs-cvsroot (list "-d" cvs-cvsroot))
+ (list "log")
+ cvs-log-flags)
+ "Running cvs log %s...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Running cvs log ... Done."))))
+
+;;----------
+(defun cvs-mode-tag ()
+ "Run 'cvs tag' on all selected files."
+
+ (interactive)
+ (if (not (listp cvs-tag-flags))
+ (error "cvs-tag-flags should be set using cvs-set-tag-flags."))
+ (let ((marked (cvs-get-marked nil t))
+ (tag-args (cvs-make-list (read-string "Tag name (and flags): "))))
+ (cvs-use-temp-buffer)
+ (message "Running cvs tag ...")
+ (if (cvs-execute-list marked
+ cvs-program
+ (append (if cvs-cvsroot (list "-d" cvs-cvsroot))
+ (list "tag")
+ cvs-tag-flags
+ tag-args)
+ "Running cvs tag %s...")
+ (error "CVS exited with non-zero exit status.")
+ (message "Running cvs tag ... Done."))))
+
+;;----------
+(defun cvs-mode-rtag ()
+ "Run 'cvs rtag' on all selected files."
+
+ (interactive)
+ (if (not (listp cvs-rtag-flags))
+ (error "cvs-rtag-flags should be set using cvs-set-rtag-flags."))
+ (let ((marked (cvs-get-marked nil t))
+ ;; FIXME: should give selection from the modules file
+ (module-name (read-string "Module name: "))
+ ;; FIXME: should also ask for an existing tag *or* date
+ (rtag-args (cvs-make-list (read-string "Tag name (and flags): "))))
+ (cvs-use-temp-buffer)
+ (message "Running cvs rtag ...")
+ (if (cvs-execute-list marked
+ cvs-program
+ (append (if cvs-cvsroot (list "-d" cvs-cvsroot))
+ (list "rtag")
+ cvs-rtag-flags
+ rtag-args
+ (list module-name))
+ "Running cvs rtag %s...")
+ (error "CVS rtag exited with non-zero exit status.")
+ (message "Running cvs rtag ... Done."))))
+
+;;----------
+(defun cvs-mode-byte-compile-files ()
+ "Run byte-compile-file on all selected files that end in '.el'."
+
+ (interactive)
+ (let ((marked (cvs-get-marked)))
+ (while marked
+ (let ((filename (cvs-full-path (car marked))))
+ (if (string-match "\\.el$" filename)
+ (byte-compile-file filename)))
+ (setq marked (cdr marked)))))
+
+;;----------
+(defun cvs-insert-full-path (tin)
+ "Insert full path to the file described in TIN in the current buffer."
+
+ (insert (format "%s\n" (cvs-full-path tin))))
+
+;;----------
+(defun cvs-mode-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)
+ (obfname buffer-file-name)
+ (tin (tin-locate cvs-cookie-handle pos))
+ (fileinfo (tin-cookie cvs-cookie-handle tin))
+ (fname (cvs-fileinfo->file-name fileinfo))
+ (dname (file-name-as-directory (cvs-fileinfo->dir fileinfo))))
+ (setq change-log-default-name nil) ; this rarely correct in 19.28
+ (setq buffer-file-name (cond (fname
+ fname)
+ (t
+ nil)))
+ (setq default-directory (cond (dname
+ dname)
+ (t
+ odir)))
+ (add-change-log-entry-other-window)
+ (set-buffer cvs-buf)
+ (setq default-directory odir)
+ (setq buffer-file-name obfname)))
+
+;;----------
+(defun print-cvs-tin (foo)
+ "Debug utility."
+
+ (let ((cookie (tin-cookie cvs-cookie-handle foo))
+ (stream (get-buffer-create "pcl-cvs-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)))
+
+;;----------
+;; NOTE: the variable cvs-emerge-tmp-head-file will be "free" when compiling
+(defun cvs-mode-emerge (pos)
+ "Emerge appropriate revisions of the selected file.
+Args: POS."
+
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (tin (tin-locate cvs-cookie-handle pos)))
+ (if (boundp 'cvs-emerge-tmp-head-file)
+ (error "There can only be one emerge session active at a time."))
+ (if tin
+ (let* ((fileinfo (tin-cookie cvs-cookie-handle tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond
+ ((eq type 'MODIFIED) ; merge repository head rev. with working file
+ (require 'emerge)
+ (setq cvs-emerge-tmp-head-file ; trick to prevent multiple runs
+ (cvs-retrieve-revision-to-tmpfile fileinfo))
+ (unwind-protect
+ (if (not (emerge-files
+ t ; arg
+ (cvs-fileinfo->full-path fileinfo) ; file-A
+ ;; this is an un-avoidable compiler reference to a free variable
+ cvs-emerge-tmp-head-file ; file-B
+ (cvs-fileinfo->full-path fileinfo) ; file-out
+ nil ; start-hooks
+ '(lambda () ; quit-hooks
+ (delete-file cvs-emerge-tmp-head-file)
+ (makunbound 'cvs-emerge-tmp-head-file))))
+ (error "Emerge session failed"))))
+
+ ;; re-do the same merge rcsmerge supposedly just did....
+ ((or (eq type 'MERGED)
+ (eq type 'CONFLICT)) ; merge backup-working=A, head=B, base=ancestor
+ (require 'emerge)
+ (setq cvs-emerge-tmp-head-file ; trick to prevent multiple runs
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->head-revision
+ fileinfo)))
+ (let ((cvs-emerge-tmp-backup-working-file
+ (cvs-fileinfo->backup-file fileinfo))
+ (cvs-emerge-tmp-ancestor-file
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->base-revision
+ fileinfo))))
+ (unwind-protect
+ (if (not (emerge-files-with-ancestor
+ t ; arg
+ cvs-emerge-tmp-backup-working-file ; file-A
+ ;; this is an un-avoidable compiler reference to a free variable
+ cvs-emerge-tmp-head-file ; file-B
+ cvs-emerge-tmp-ancestor-file ; file-ancestor
+ (cvs-fileinfo->full-path fileinfo) ; file-out
+ nil ; start-hooks
+ '(lambda () ; quit-hooks
+ (delete-file cvs-emerge-tmp-backup-file)
+ (delete-file cvs-emerge-tmp-ancestor-file)
+ (delete-file cvs-emerge-tmp-head-file)
+ (makunbound 'cvs-emerge-tmp-head-file))))
+ (error "Emerge session failed")))))
+ (t
+ (error "Can only e-merge \"Modified\", \"Merged\" or \"Conflict\" files"))))
+ (error "There is no file to e-merge."))))
+
+;;----------
+;; NOTE: the variable ediff-version may be "free" when compiling
+(defun cvs-mode-ediff (pos)
+ "Ediff appropriate revisions of the selected file.
+Args: POS."
+
+ (interactive "d")
+ (if (boundp 'cvs-ediff-tmp-head-file)
+ (error "There can only be one ediff session active at a time."))
+ (require 'ediff)
+ (if (and (boundp 'ediff-version)
+ (>= (string-to-number ediff-version) 2.0)) ; FIXME real number?
+ (run-ediff-from-cvs-buffer pos)
+ (cvs-old-ediff-interface pos)))
+
+(defun cvs-old-ediff-interface (pos)
+ "Emerge like interface for older ediffs.
+Args: POS"
+
+ (let* ((cvs-buf (current-buffer))
+ (tin (tin-locate cvs-cookie-handle pos)))
+ (if tin
+ (let* ((fileinfo (tin-cookie cvs-cookie-handle tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond
+ ((eq type 'MODIFIED) ; diff repository head rev. with working file
+ ;; should this be inside the unwind-protect, and should the
+ ;; makeunbound be an unwindform?
+ (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs
+ (cvs-retrieve-revision-to-tmpfile fileinfo))
+ (unwind-protect
+ (if (not (ediff-files ; check correct ordering of args
+ (cvs-fileinfo->full-path fileinfo) ; file-A
+ ;; this is an un-avoidable compiler reference to a free variable
+ cvs-ediff-tmp-head-file ; file-B
+ '(lambda () ; startup-hooks
+ (make-local-hook 'ediff-cleanup-hooks)
+ (add-hook 'ediff-cleanup-hooks
+ '(lambda ()
+ (ediff-janitor)
+ (delete-file cvs-ediff-tmp-head-file)
+ (makunbound 'cvs-ediff-tmp-head-file))
+ nil t))))
+ (error "Ediff session failed"))))
+
+ ;; look at the merge rcsmerge supposedly just did....
+ ((or (eq type 'MERGED)
+ (eq type 'CONFLICT)) ; diff backup-working=A, head=B, base=ancestor
+ (if (not (boundp 'ediff-version))
+ (error "ediff version way too old for 3-way diff"))
+ (if (<= (string-to-number ediff-version) 1.9) ; FIXME real number?
+ (error "ediff version %s too old for 3-way diff" ediff-version))
+ (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->head-revision
+ fileinfo)))
+ (let ((cvs-ediff-tmp-backup-working-file
+ (cvs-fileinfo->backup-file fileinfo))
+ (cvs-ediff-tmp-ancestor-file
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (cvs-fileinfo->base-revision
+ fileinfo))))
+ (unwind-protect
+ (if (not (ediff-files3 ; check correct ordering of args
+ cvs-ediff-tmp-backup-working-file ; file-A
+ ;; this is an un-avoidable compiler reference to a free variable
+ cvs-ediff-tmp-head-file ; file-B
+ cvs-ediff-tmp-ancestor-file ; file-ancestor
+ '(lambda () ; start-hooks
+ (make-local-hook 'ediff-cleanup-hooks)
+ (add-hook 'ediff-cleanup-hooks
+ '(lambda ()
+ (ediff-janitor)
+ (delete-file cvs-ediff-tmp-backup-file)
+ (delete-file cvs-ediff-tmp-ancestor-file)
+ (delete-file cvs-ediff-tmp-head-file)
+ (makunbound 'cvs-ediff-tmp-head-file))
+ nil t))))
+ (error "Ediff session failed")))))
+
+ ((not (or (eq type 'UNKNOWN)
+ (eq type 'UNKNOWN-DIR))) ; i.e. UPDATED or PATCHED ????
+ ;; this should really diff the current working file with the previous
+ ;; rev. on the current branch (i.e. not the head, since that's what
+ ;; the current file should be)
+ (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs
+ (cvs-retrieve-revision-to-tmpfile fileinfo
+ (read-string "Rev #/tag to diff against: "
+ (cvs-fileinfo->head-revision
+ fileinfo))))
+ (unwind-protect
+ (if (not (ediff-files ; check correct ordering of args
+ (cvs-fileinfo->full-path fileinfo) ; file-A
+ ;; this is an un-avoidable compiler reference to a free variable
+ cvs-ediff-tmp-head-file ; file-B
+ '(lambda () ; startup-hooks
+ (make-local-hook 'ediff-cleanup-hooks)
+ (add-hook 'ediff-cleanup-hooks
+ '(lambda ()
+ (ediff-janitor)
+ (delete-file cvs-ediff-tmp-head-file)
+ (makunbound 'cvs-ediff-tmp-head-file))
+ nil t))))
+ (error "Ediff session failed"))))
+ (t
+ (error "Can not ediff \"Unknown\" files"))))
+ (error "There is no file to ediff."))))
+
+;;----------
+(defun cvs-retrieve-revision-to-tmpfile (fileinfo &optional revision)
+ "Retrieve the latest revision of the file in FILEINFO to a temporary file.
+If second optional argument REVISION is given, retrieve that revision instead."
+
+ (let
+ ((temp-name (make-temp-name
+ (concat (file-name-as-directory
+ (or (getenv "TMPDIR") "/tmp"))
+ "pcl-cvs." revision))))
+ (cvs-kill-buffer-visiting temp-name)
+ (if (and revision
+ (stringp revision)
+ (not (string= revision "")))
+ (message "Retrieving revision %s..." revision)
+ (message "Retrieving latest revision..."))
+ (let ((res (call-process cvs-shell nil nil nil "-c"
+ (concat cvs-program " update -p "
+ (if (and revision
+ (stringp revision)
+ (not (string= revision "")))
+ (concat "-r " revision " ")
+ "")
+ (cvs-fileinfo->full-path fileinfo)
+ " > " temp-name))))
+ (if (and res (not (and (integerp res) (zerop res))))
+ (error "Something went wrong retrieving revision %s: %s"
+ revision res))
+
+ (if revision
+ (message "Retrieving revision %s... Done." revision)
+ (message "Retrieving latest revision... Done."))
+ (save-excursion
+ (set-buffer (find-file-noselect temp-name))
+ (rename-buffer (concat " " (file-name-nondirectory temp-name)) t))
+ temp-name)))
+
+;;----------
+(defun cvs-kill-buffer-visiting (filename)
+ "If there is any buffer visiting FILENAME, kill it (without confirmation)."
+
+ (let ((l (buffer-list)))
+ (while l
+ (if (string= (buffer-file-name (car l)) filename)
+ (kill-buffer (car l)))
+ (setq l (cdr l)))))
+
+;;----------
+(defun cvs-change-cvsroot ()
+ "Ask for a new cvsroot."
+
+ (interactive)
+ (cvs-set-cvsroot (read-file-name "New CVSROOT: " cvs-cvsroot)))
+
+;;----------
+(defun cvs-set-cvsroot (newroot)
+ "Change the cvsroot."
+
+ (if (or (file-directory-p (expand-file-name "CVSROOT" newroot))
+ (y-or-n-p (concat "Warning: no CVSROOT found inside repository."
+ " Change cvs-cvsroot anyhow?")))
+ (setq cvs-cvsroot newroot)))
+
+;;----------
+(defun cvs-set-diff-flags ()
+ "Ask for new setting of cvs-diff-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-diff-flags) " ")))
+ (setq cvs-diff-flags
+ (cvs-make-list (read-string "Diff flags: " old-value)))))
+
+;;----------
+(defun cvs-set-update-optional-flags ()
+ "Ask for new setting of cvs-update-optional-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-update-optional-flags) " ")))
+ (setq cvs-update-optional-flags
+ (cvs-make-list (read-string "Update optional flags: " old-value)))))
+
+;;----------
+(defun cvs-set-status-flags ()
+ "Ask for new setting of cvs-status-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-status-flags) " ")))
+ (setq cvs-status-flags
+ (cvs-make-list (read-string "Status flags: " old-value)))))
+
+;;----------
+(defun cvs-set-log-flags ()
+ "Ask for new setting of cvs-log-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-log-flags) " ")))
+ (setq cvs-log-flags
+ (cvs-make-list (read-string "Log flags: " old-value)))))
+
+;;----------
+(defun cvs-set-tag-flags ()
+ "Ask for new setting of cvs-tag-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-tag-flags) " ")))
+ (setq cvs-tag-flags
+ (cvs-make-list (read-string "Tag flags: " old-value)))))
+
+;;----------
+(defun cvs-set-rtag-flags ()
+ "Ask for new setting of cvs-rtag-flags."
+
+ (interactive)
+ (let ((old-value (mapconcat 'identity
+ (copy-sequence cvs-rtag-flags) " ")))
+ (setq cvs-rtag-flags
+ (cvs-make-list (read-string "Rtag flags: " old-value)))))
+
+;;----------
+(if (string-match "Lucid" emacs-version)
+ (progn
+ (autoload 'pcl-cvs-fontify "pcl-cvs-lucid")
+ (add-hook 'cvs-mode-hook 'pcl-cvs-fontify)))
+
+(defun cvs-changelog-name (directory)
+ "Return the name of the ChangeLog file that handles DIRECTORY.
+This is in DIRECTORY or one of its parents.
+Signal an error if we can't find an appropriate ChangeLog file."
+ (let ((dir (file-name-as-directory directory))
+ file)
+ (while (and dir
+ (not (file-exists-p
+ (setq file (expand-file-name "ChangeLog" dir)))))
+ (let ((last dir))
+ (setq dir (file-name-directory (directory-file-name dir)))
+ (if (equal last dir)
+ (setq dir nil))))
+ (or dir
+ (error "Can't find ChangeLog for %s" directory))
+ file))
+
+(defun cvs-narrow-changelog ()
+ "Narrow to the top page of the current buffer, a ChangeLog file.
+Actually, the narrowed region doesn't include the date line.
+A \"page\" in a ChangeLog file is the area between two dates."
+ (or (eq major-mode 'change-log-mode)
+ (error "cvs-narrow-changelog: current buffer isn't a ChangeLog"))
+
+ (goto-char (point-min))
+
+ ;; Skip date line and subsequent blank lines.
+ (forward-line 1)
+ (if (looking-at "[ \t\n]*\n")
+ (goto-char (match-end 0)))
+
+ (let ((start (point)))
+ (forward-page 1)
+ (narrow-to-region start (point))
+ (goto-char (point-min))))
+
+(defun cvs-changelog-paragraph ()
+ "Return the bounds of the ChangeLog paragraph containing point.
+If we are between paragraphs, return the previous paragraph."
+ (save-excursion
+ (beginning-of-line)
+ (if (looking-at "^[ \t]*$")
+ (skip-chars-backward " \t\n" (point-min)))
+ (list (progn
+ (if (re-search-backward "^[ \t]*\n" nil 'or-to-limit)
+ (goto-char (match-end 0)))
+ (point))
+ (if (re-search-forward "^[ \t\n]*$" nil t)
+ (match-beginning 0)
+ (point)))))
+
+(defun cvs-changelog-subparagraph ()
+ "Return the bounds of the ChangeLog subparagraph containing point.
+A subparagraph is a block of non-blank lines beginning with an asterisk.
+If we are between sub-paragraphs, return the previous subparagraph."
+ (save-excursion
+ (end-of-line)
+ (if (search-backward "*" nil t)
+ (list (progn (beginning-of-line) (point))
+ (progn
+ (forward-line 1)
+ (if (re-search-forward "^[ \t]*[\n*]" nil t)
+ (match-beginning 0)
+ (point-max))))
+ (list (point) (point)))))
+
+(defun cvs-changelog-entry ()
+ "Return the bounds of the ChangeLog entry containing point.
+The variable `cvs-changelog-full-paragraphs' decides whether an
+\"entry\" is a paragraph or a subparagraph; see its documentation string
+for more details."
+ (if cvs-changelog-full-paragraphs
+ (cvs-changelog-paragraph)
+ (cvs-changelog-subparagraph)))
+
+;; NOTE: the variable user-full-name may be "free" when compiling
+(defun cvs-changelog-ours-p ()
+ "See if ChangeLog entry at point is for the current user, today.
+Return non-nil iff it is."
+ ;; Code adapted from add-change-log-entry.
+ (looking-at (concat (regexp-quote (substring (current-time-string)
+ 0 10))
+ ".* "
+ (regexp-quote (substring (current-time-string) -4))
+ "[ \t]+"
+ (regexp-quote (if (and (boundp 'add-log-full-name)
+ add-log-full-name)
+ add-log-full-name
+ user-full-name))
+ " <"
+ (regexp-quote (if (and (boundp 'add-log-mailing-address)
+ add-log-mailing-address)
+ add-log-mailing-address
+ user-mail-address)))))
+
+(defun cvs-relative-path (base child)
+ "Return a directory path relative to BASE for CHILD.
+If CHILD doesn't seem to be in a subdirectory of BASE, just return
+the full path to CHILD."
+ (let ((base (file-name-as-directory (expand-file-name base)))
+ (child (expand-file-name child)))
+ (or (string= base (substring child 0 (length base)))
+ (error "cvs-relative-path: %s isn't in %s" child base))
+ (substring child (length base))))
+
+(defun cvs-changelog-entries (file)
+ "Return the ChangeLog entries for FILE, and the ChangeLog they came from.
+The return value looks like this:
+ (LOGBUFFER (ENTRYSTART . ENTRYEND) ...)
+where LOGBUFFER is the name of the ChangeLog buffer, and each
+\(ENTRYSTART . ENTRYEND\) pair is a buffer region."
+ (save-excursion
+ (set-buffer (find-file-noselect
+ (cvs-changelog-name
+ (file-name-directory
+ (expand-file-name file)))))
+ (or (eq major-mode 'change-log-mode)
+ (change-log-mode))
+ (goto-char (point-min))
+ (if (looking-at "[ \t\n]*\n")
+ (goto-char (match-end 0)))
+ (if (not (cvs-changelog-ours-p))
+ (list (current-buffer))
+ (save-restriction
+ (cvs-narrow-changelog)
+ (goto-char (point-min))
+
+ ;; Search for the name of FILE relative to the ChangeLog. If that
+ ;; doesn't occur anywhere, they're not using full relative
+ ;; filenames in the ChangeLog, so just look for FILE; we'll accept
+ ;; some false positives.
+ (let ((pattern (cvs-relative-path
+ (file-name-directory buffer-file-name) file)))
+ (if (or (string= pattern "")
+ (not (save-excursion
+ (search-forward pattern nil t))))
+ (setq pattern file))
+
+ (let (texts)
+ (while (search-forward pattern nil t)
+ (let ((entry (cvs-changelog-entry)))
+ (setq texts (cons entry texts))
+ (goto-char (elt entry 1))))
+
+ (cons (current-buffer) texts)))))))
+
+(defun cvs-changelog-insert-entries (buffer regions)
+ "Insert those regions in BUFFER specified in REGIONS.
+Sort REGIONS front-to-back first."
+ (let ((regions (sort regions 'car-less-than-car))
+ (last))
+ (while regions
+ (if (and last (< last (car (car regions))))
+ (newline))
+ (setq last (elt (car regions) 1))
+ (apply 'insert-buffer-substring buffer (car regions))
+ (setq regions (cdr regions)))))
+
+(defun cvs-union (set1 set2)
+ "Return the union of SET1 and SET2, according to `equal'."
+ (while set2
+ (or (member (car set2) set1)
+ (setq set1 (cons (car set2) set1)))
+ (setq set2 (cdr set2)))
+ set1)
+
+(defun cvs-insert-changelog-entries (files)
+ "Given a list of files FILES, insert the ChangeLog entries for them."
+ (let ((buffer-entries nil))
+
+ ;; Add each buffer to buffer-entries, and associate it with the list
+ ;; of entries we want from that file.
+ (while files
+ (let* ((entries (cvs-changelog-entries (car files)))
+ (pair (assq (car entries) buffer-entries)))
+ (if pair
+ (setcdr pair (cvs-union (cdr pair) (cdr entries)))
+ (setq buffer-entries (cons entries buffer-entries))))
+ (setq files (cdr files)))
+
+ ;; Now map over each buffer in buffer-entries, sort the entries for
+ ;; each buffer, and extract them as strings.
+ (while buffer-entries
+ (cvs-changelog-insert-entries (car (car buffer-entries))
+ (cdr (car buffer-entries)))
+ (if (and (cdr buffer-entries) (cdr (car buffer-entries)))
+ (newline))
+ (setq buffer-entries (cdr buffer-entries)))))
+
+(defun cvs-edit-delete-common-indentation ()
+ "Unindent the current buffer rigidly until at least one line is flush left."
+ (save-excursion
+ (let ((common 100000))
+ (goto-char (point-min))
+ (while (< (point) (point-max))
+ (if (not (looking-at "^[ \t]*$"))
+ (setq common (min common (current-indentation))))
+ (forward-line 1))
+ (indent-rigidly (point-min) (point-max) (- common)))))
+
+(defun cvs-mode-changelog-commit ()
+ "Check in all marked files, or the current file.
+Ask the user for a log message in a buffer.
+
+This is just like `\\[cvs-mode-commit]', except that it tries to provide
+appropriate default log messages by looking at the ChangeLog. The
+idea is to write your ChangeLog entries first, and then use this
+command to commit your changes.
+
+To select default log text, we:
+- find the ChangeLog entries for the files to be checked in,
+- verify that the top entry in the ChangeLog is on the current date
+ and by the current user; if not, we don't provide any default text,
+- search the ChangeLog entry for paragraphs containing the names of
+ the files we're checking in, and finally
+- use those paragraphs as the log text."
+
+ (interactive)
+
+ (let* ((cvs-buf (current-buffer))
+ (marked (cvs-filter (function cvs-committable)
+ (cvs-get-marked))))
+ (if (null marked)
+ (error "Nothing to commit!")
+ (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer))
+ (goto-char (point-min))
+
+ (erase-buffer)
+ (cvs-insert-changelog-entries
+ (mapcar (lambda (tin)
+ (let ((cookie (tin-cookie cvs-cookie-handle tin)))
+ (expand-file-name
+ (cvs-fileinfo->file-name cookie)
+ (cvs-fileinfo->dir cookie))))
+ marked))
+ (cvs-edit-delete-common-indentation)
+
+ (cvs-edit-mode)
+ (make-local-variable 'cvs-commit-list)
+ (setq cvs-commit-list marked)
+ (message "Press C-c C-c when you are done editing."))))
+
+(provide 'pcl-cvs)
+
+;;;; end of file pcl-cvs.el
--- /dev/null
+\input texinfo @c -*-texinfo-*-
+
+@comment OrigId: pcl-cvs.texinfo,v 1.45 1993/05/31 22:38:15 ceder Exp
+@comment @@(#)cvs/contrib/pcl-cvs:$Name: $:$Id: pcl-cvs.texinfo,v 1.1.1.1 1996/05/06 22:20:48 tholo 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.info
+@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.05-CVS-$Name: $
+@comment -release-
+@sp 3
+@center Per Cederqvist
+@sp 3
+@center last updated 20 Nov 1995
+@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, Installation, (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 versions 1.5 through 1.7 and newer, and possibly
+CVS-1.3 and CVS-1.4A2. This manual is updated to release
+1.05-CVS-$Name: $ of pcl-cvs.
+@end ifinfo
+@comment -release-
+
+@menu
+* 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.
+* Bugs:: Bugs (known and unknown).
+* COPYING:: GNU General Public License
+* 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
+* Undoing changes:: Undoing changes
+* 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.
+* Invoking Ediff:: Running @samp{ediff} from @samp{*cvs*} buffer.
+* Invoking Emerge:: Running @samp{emerge} from @samp{*cvs*} buffer.
+* Reverting your buffers:: Reverting your buffers
+* Miscellaneous commands:: Miscellaneous commands
+@end menu
+
+
+@node Installation, About pcl-cvs, Top, 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
+Possibly edit the file @file{Makefile} to reflect the situation at your
+site. We say "possibly" because the version of pcl-cvs included with
+CVS uses a configuration mechanism integrated with the overall
+mechanisms used by the CVS build and install procedures. Thus the file
+@code{Makefile} will be generated automatically from the file
+@code{Makefile.in}, and it should not be necessary to edit it further.
+
+If you do have to edit the @file{Makefile}, 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(s) to
+@code{infodir}.
+
+@item
+Configure pcl-cvs.el
+
+There are a couple of pathnames that you have to check to make sure that
+they match your system. They appear early in the file
+@samp{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
+Release 1.05 and later of pcl-cvs requires parts of the Elib library,
+version 1.0 or later. Elib is available via anonymous ftp from
+prep.ai.mit.edu in @file{pub/gnu/elib-1.0.tar.gz}, and from a lot of
+other sites that mirror prep. Get Elib, and install it, before
+proceeding.
+
+@strong{NOTE:} The version of pcl-cvs included with CVS includes a copy
+of Elib in the sub-directory @file{elib} under the
+@file{contrib/pcl-cvs} directory.
+
+@item
+Type @samp{make install} in the source directory. This will
+byte-compile all @file{.el} files and copy the @file{*.elc} files into
+the directory you specified in step 1.
+
+If you want to install the @file{*.el} files too, you can type
+@samp{make install-el} to do so.
+
+If you only want to create the compiled elisp files, but don't want to
+install them, you can type @samp{make} without parameters.
+
+@item
+Edit the file @file{default.el} in your emacs lisp directory (usually
+@file{/usr/gnu/lib/emacs/site-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(s) @file{pcl-cvs.info*} 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{prep.ai.mit.edu} as @file{pub/gnu/texinfo-3.7.tar.gz} (there
+might be a newer version there when you read this).@refill
+
+@item
+Install the info file(s) @file{pcl-cvs.info*} into your standard
+@file{info} directory. You should be able to do this by typing
+@samp{make install-info}.@refill
+
+@item
+Edit the file @file{dir} in the @file{info} directory and enter one line
+to contain a pointer to the info file(s) @file{pcl-cvs.info*}. 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 versions 1.5 through 1.7 and newer; and
+possibly verison 1.3 and 1.4A2. It integrates the most frequently used
+CVS commands into an emacs interface.
+
+@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{Bugs}).
+
+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
+
+@item
+Jamie Zawinski (@samp{jwz@@lucid.com}) contributed
+@file{pcl-cvs-lucid.el}.
+
+@item
+Leif Lonnblad contributed RCVS support. (Since superceded by the new
+remote CVS support.)
+
+@item
+Jim Blandy (@samp{jimb@@cyclic.com}) contributed hooks to automatically
+guess CVS log entries from ChangeLog contents; and initial support of
+the new Cygnus / Cyclic remote CVS; as well as various sundry bug fixes
+and cleanups.
+
+@item
+Jim Kingdon (@samp{kingdon@@cyclic.com}) contributed lots of fixes to
+the build and install procedure.
+
+@item
+Greg A. Woods (@samp{woods@@planix.com}) contributed code to implement
+the use of per-file diff buffers; and vendor join diffs with emerge and
+ediff; as well as various an sundry bug fixes and cleanups.
+@end itemize
+
+Apart from these, a lot of people have send me suggestions, ideas,
+requests, bug reports and encouragement. Thanks a lot! Without your
+there would be no new releases of pcl-cvs.
+
+
+@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
+
+The current release of pcl-cvs is included in CVS-1.7.
+
+The author's 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 cvs-update 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.05-CVS-$Name: $.
+@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 two 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 Patched (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 Patched
+The file was brought up to date with respect to a remote repository by
+way of fetching and applying a patch to the file in your source. This
+is done for any file that exists in a remote repository and in your
+source; of which you haven't changed locally but is not the most recent
+version available in the remote 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
+* Undoing changes:: Undoing changes
+* 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.
+* Invoking Ediff:: Running @samp{ediff} from @samp{*cvs*} buffer.
+* Invoking Emerge:: Running @samp{emerge} from @samp{*cvs*} buffer.
+* Reverting your buffers:: Reverting your buffers
+* Miscellaneous commands:: Miscellaneous commands
+@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-mode-update-no-prompt
+@findex cvs-delete-lock
+@cindex Getting the *cvs* buffer
+@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
+
+CVS uses lock files in the repository to ensure the integrity of the
+data files in the repository. They might be left behind i.e. if a
+workstation crashes in the middle of a CVS operation. CVS outputs a
+message when it is waiting for a lock file to go away. Pcl-cvs will
+show the same message in the *cvs* buffer, together with instructions
+for deleting the lock files. You should normally not have to delete
+them manually --- just wait a little while and the problem should fix
+itself. But if the lock files doesn't disappear you can delete them
+with @kbd{M-x cvs-delete-lock RET}.@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-mode-update-no-prompt}.@refill
+
+@item G
+This will run @samp{cvs update} and prompt for a new directory to
+update. This runs the command @samp{cvs-update}.@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 ESC DEL - unmark all files
+@kindex DEL - unmark previous file
+@findex cvs-mode-mark
+@findex cvs-mode-unmark
+@findex cvs-mode-mark-all-files
+@findex cvs-mode-unmark-all-files
+@findex cvs-mode-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-mode-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-mode-unmark}).@refill
+
+@item M
+Mark @emph{all} files in the buffer (@code{cvs-mode-mark-all-files}).
+
+@item @key{ESC} @key{DEL}
+Unmark @emph{all} files (@code{cvs-mode-unmark-all-files}).
+
+@item @key{DEL}
+Unmark the file on the previous line, and move point to that line
+(@code{cvs-mode-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-mode-commit
+@findex cvs-mode-changelog-commit
+@kindex c - commit files
+@kindex C - commit files with ChangeLog message
+@vindex cvs-erase-input-buffer (variable)
+@vindex cvs-auto-revert-after-commit (variable)
+@cindex Commit buffer
+@cindex Edit buffer
+@cindex Erasing commit message
+@cindex Reverting buffers after commit
+
+@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-mode-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-@code{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
+
+If you are editing the files in your emacs an automatic
+@samp{revert-buffer} will be performed. (If the file contains
+@samp{$@asis{Id}$} keywords @samp{cvs commit} will write a new file with
+the new values substituted. The auto-revert makes sure that you get
+them into your buffer). The revert will not occur if you have modified
+your buffer, or if @samp{cvs-auto-revert-after-commit} is set to
+@samp{nil}.@refill
+
+@item C
+This is just like @samp{cvs-mode-commit}, except that it tries to
+provide appropriate default log messages by looking at the
+@samp{ChangeLog}s in the current directory. The idea is to write your
+ChangeLog entries first, and then use this command to commit your
+changes. Pressing @kbd{C} causes @code{cvs-mode-changelog-commit} to be
+run.@refill
+
+To select default log text, pcl-cvs:
+@itemize @minus
+@item
+finds the ChangeLogs for the files to be checked in;
+@item
+verifies that the top entry in the ChangeLog is on the current date and
+by the current user; if not, no default text is provided;
+@item
+search the ChangeLog entry for paragraphs containing the names of the
+files we're checking in; and finally
+@item
+uses those paragraphs as the default log text in the
+@samp{*cvs-commit-message*} buffer.
+@end itemize
+
+You can then commit the @samp{ChangeLog} file once per day without any
+log message.@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-mode-find-file
+@findex cvs-mode-find-file-other-window
+@findex cvs-mode-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-mode-find-file}).@refill
+
+@item o
+Like @kbd{f}, but use another window
+(@code{cvs-mode-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-mode-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-mode-log
+@findex cvs-mode-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-mode-log}).
+
+@item s
+Run @samp{cvs status} on all selected files, and show the result in a
+temporary buffer (@code{cvs-mode-status}).
+@end table
+
+
+@node Adding and removing files, Undoing changes, 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-mode-add
+@findex cvs-mode-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-mode-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-mode-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-mode-commit},
+@pxref{Committing changes}) to commit the removal.@refill
+
+The command that is run is @code{cvs-mode-remove-file}.
+@end table
+
+
+@node Undoing changes, Removing handled entries, Adding and removing files, Commands
+@comment node-name, next, previous, up
+
+@section Undoing changes
+@cindex Undo changes
+@cindex Flush changes
+@kindex U - undo changes
+@findex cvs-mode-undo-local-changes
+
+@table @kbd
+@item U
+If you have modified a file, and for some reason decide that you don't
+want to keep the changes, you can undo them with this command. It works
+by removing your working copy of the file and then getting the latest
+version from the repository (@code{cvs-mode-undo-local-changes}.
+@end table
+
+
+@node Removing handled entries, Ignoring files, Undoing changes, 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-mode-remove-handled
+@findex cvs-mode-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-mode-remove-handled}. If
+@samp{cvs-auto-remove-handled} is set to non-@code{nil} this will
+automatically be performed after every commit.@refill
+
+@item C-k
+This command can be used for lines that @samp{cvs-mode-remove-handled} would
+not delete, but that you want to delete (@code{cvs-mode-acknowledge}).
+@end table
+
+
+@node Ignoring files, Viewing differences, Removing handled entries, Commands
+@comment node-name, next, previous, up
+
+@section Ignoring files
+@kindex i - ignoring files
+@findex cvs-mode-ignore
+
+@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-mode-ignore}.
+@end table
+
+
+@node Viewing differences, Invoking Ediff, Ignoring files, Commands
+@comment node-name, next, previous, up
+
+@section Viewing differences
+@cindex Diff
+@cindex Ediff
+@cindex Invoking ediff
+@cindex Conflicts, how to resolve them
+@cindex Viewing differences
+@kindex d - run @samp{cvs diff}
+@kindex b - diff backup file
+@findex cvs-mode-diff-cvs
+@findex cvs-mode-diff-backup
+@vindex cvs-diff-ignore-marks (variable)
+
+@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}. If @var{cvs-diff-ignore-marks} is set to a
+non-@code{nil} value or if a prefix argument is given (but not both) any
+marked files will not be considered to be selected.
+(@code{cvs-mode-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.@refill
+
+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}.@refill
+
+If @var{cvs-diff-ignore-marks} is set to a non-@code{nil} value or if a
+prefix argument is given (but not both) any marked files will not be
+considered to be selected. (@code{cvs-mode-diff-backup}).@refill
+@end table
+
+
+@node Invoking Ediff, Invoking Emerge, Viewing differences, Commands
+@comment node-name, next, previous, up
+
+@section Running ediff
+@cindex Ediff
+@cindex Invoking ediff
+@cindex Viewing differences
+@cindex Conflicts, resolving
+@cindex Resolving conflicts
+@kindex e - invoke @samp{ediff}
+@findex cvs-mode-ediff
+@findex run-ediff-from-cvs-buffer
+@findex cvs-old-ediff-interface
+
+@table @kbd
+@item e
+This command works
+slightly different depending on the version of @samp{ediff} and the file
+status.@refill
+
+With modern versions of @samp{ediff}, this command invokes
+@samp{run-ediff-from-cvs-buffer} on one file.@refill
+
+@strong{Note:} When the file status is @samp{Merged} or @samp{Conflict},
+CVS has already performed a merge. The resulting file is not used in
+any way if you use this command. If you use the @kbd{q} command inside
+@samp{ediff} (to successfully terminate a merge) the file that CVS
+created will be overwritten.@refill
+
+Older versions of @samp{ediff} use an interface similar to
+@samp{emerge}. The function @samp{cvs-old-ediff-interface} is invoked
+if the version of @samp{ediff} you have doesn't support
+@samp{run-ediff-from-cvs-buffer}. These older versions do not support
+merging of revisions.@refill
+
+@table @asis
+@item @samp{Modified}
+Run @samp{ediff-files} with your working file as file A, and the latest
+revision in the repository as file B.
+
+@item @samp{Merged}
+@itemx @samp{Conflict}
+Run @samp{ediff-files3} with your working file (as it was prior to your
+invocation of @samp{cvs-update}) as file A, the latest revision in the
+repository as file B, and the revision that you based your local
+modifications on as ancestor.
+
+@item @samp{Updated}
+@itemx @samp{Patched}
+Run @samp{ediff-files} with your working file as file A, and a given
+revision in the repository as file B. You are prompted for the revision
+to ediff against, and you may specify either a tag name or a numerical
+revision number (@pxref{Getting info about files}).
+@end table
+
+@end table
+
+@node Invoking Emerge, Reverting your buffers, Invoking Ediff, Commands
+@comment node-name, next, previous, up
+
+@section Running emerge
+@cindex Emerge
+@cindex Ediff
+@cindex Viewing differences
+@cindex Invoking emerge
+@cindex Conflicts, resolving
+@cindex Resolving conflicts
+@kindex E - invoke @samp{emerge}
+@findex cvs-mode-emerge
+
+@table @kbd
+@item E
+Invoke @samp{emerge} on one file. This command works slightly different
+depending on the file status.
+
+@table @asis
+@item @samp{Modified}
+Run @samp{emerge-files} with your working file as file A, and the latest
+revision in the repository as file B.
+
+@item @samp{Merged}
+@itemx @samp{Conflict}
+Run @samp{emerge-files-with-ancestor} with your working file (as it was
+prior to your invocation of @samp{cvs-update}) as file A, the latest
+revision in the repository as file B, and the revision that you based
+your local modifications on as ancestor.
+@end table
+
+@strong{Note:} When the file status is @samp{Merged} or @samp{Conflict},
+CVS has already performed a merge. The resulting file is not used in
+any way if you use this command. If you use the @kbd{q} command inside
+@samp{emerge} (to successfully terminate the merge) the file that CVS
+created will be overwritten.
+
+@end table
+
+
+@node Reverting your buffers, Miscellaneous commands, Invoking Emerge, Commands
+@comment node-name, next, previous, up
+
+@section Reverting your buffers
+@findex cvs-mode-revert-updated-buffers
+@kindex R - revert buffers
+@cindex Syncing buffers
+@cindex Reverting buffers
+
+@table @kbd
+@item R
+If you are editing (or just viewing) a file in a buffer, and that file
+is changed by CVS during a @samp{cvs-update}, all you have to do is type
+@kbd{R} in the *cvs* buffer to read in the new versions of the
+files.@refill
+
+All files that are @samp{Updated}, @samp{Merged} or in @samp{Conflict}
+are reverted from the disk. Any other files are ignored. Only files
+that you were already editing are read.@refill
+
+An error is signalled if you have modified the buffer since it was last
+changed. (@code{cvs-mode-revert-updated-buffers}).@refill
+@end table
+
+
+@node Miscellaneous commands, , Reverting your buffers, Commands
+@comment node-name, next, previous, up
+
+@section Miscellaneous commands
+@findex cvs-byte-compile-files
+@cindex Recompiling elisp files
+@cindex Byte compilation
+@cindex Getting rid of lock files
+@cindex Lock files
+@kindex q - bury the *cvs* buffer
+@findex bury-buffer
+
+@table @kbd
+@item M-x cvs-byte-compile-files
+Byte compile all selected files that end in .el.
+
+@item M-x cvs-delete-lock
+This command can be used in any buffer, and deletes the lock files that
+the *cvs* buffer informs you about. You should normally never have to
+use this command since CVS tries very carefully to always remove the
+lock files itself.
+
+You can only use this command when a message in the *cvs* buffer tells
+you so. You should wait a while before using this command in case
+someone else is running a cvs command.
+
+@item q
+Bury the *cvs* buffer. (@code{bury-buffer}).
+
+@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-diff-flags (variable)
+@vindex cvs-diff-ignore-marks (variable)
+@vindex cvs-log-flags (variable)
+@vindex cvs-status-flags (variable)
+@vindex cvs-auto-remove-handled (variable)
+@vindex cvs-update-prog-output-skip-regexp (variable)
+@vindex cvs-cvsroot (variable)
+@vindex TMPDIR (environment variable)
+@vindex cvs-auto-revert-after-commit (variable)
+@vindex cvs-commit-buffer-require-final-newline (variable)
+@vindex cvs-sort-ignore-file (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
+@cindex -u option in modules file
+@cindex Modules file (-u option)
+@cindex Update program (-u option in modules file)
+@cindex Reverting buffers after commit
+@cindex Require final newline
+@cindex Automatically inserting newline
+@cindex Commit message, inserting newline
+@cindex Sorting the .cvsignore file
+@cindex .cvsignore file, sorting
+@cindex Automatically sorting .cvsignore
+
+If you have an idea about any customization that would be handy but
+isn't present in this list, please tell me! @xref{Bugs} for info on how
+to reach me.@refill
+
+@table @samp
+@item cvs-erase-input-buffer
+If set to anything else than @code{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-diff-flags
+A list of strings to pass as arguments to the @samp{cvs diff} and
+@samp{diff} programs. This is used by @samp{cvs-mode-diff-cvs} and
+@samp{cvs-mode-diff-backup} (key @kbd{b}, @pxref{Viewing differences}). If
+you prefer the Unidiff format you could add this line to your
+@file{.emacs} file:@refill
+
+@example
+(setq cvs-diff-flags '("-u"))
+@end example
+
+@item cvs-diff-ignore-marks
+If this variable is non-@code{nil} or if a prefix argument is given (but
+not both) to @samp{cvs-mode-diff-cvs} or @samp{cvs-mode-diff-backup}
+marked files are not considered selected.
+
+@item cvs-log-flags
+List of strings to send to @samp{cvs log}. Used by @samp{cvs-mode-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-mode-status}
+(key @kbd{s}, @pxref{Getting info about files}).
+
+@item cvs-auto-remove-handled
+If this variable is set to any non-@code{nil} value
+@samp{cvs-mode-remove-handled} will be called every time you check in
+files, after the check-in is ready. @xref{Removing handled
+entries}.@refill
+
+@item cvs-auto-revert-after-commit
+If this variable is set to any non-@samp{nil} value any buffers you have
+that visit a file that is committed will be automatically reverted.
+This variable is default @samp{t}. @xref{Committing changes}.@refill
+
+@item cvs-update-prog-output-skip-regexp
+The @samp{-u} flag in the @file{modules} file can be used to run a command
+whenever a @samp{cvs update} is performed (see cvs(5)). This regexp
+is used to search for the last line in that output. It is normally set
+to @samp{"$"}. That setting is only correct if the command outputs
+nothing. Note that pcl-cvs will get very confused if the command
+outputs @emph{anything} to @samp{stderr}.
+
+@item cvs-cvsroot
+This variable can be set to override @samp{CVSROOT}. It should be a
+string. If it is set then everytime a cvs command is run it will be
+called as @samp{cvs -d @var{cvs-cvsroot}@dots{}} This can be useful if
+your site has several repositories.
+
+@item TMPDIR
+Pcl-cvs uses this @emph{environment variable} to decide where to put the
+temporary files it needs. It defaults to @file{/tmp} if it is not set.
+
+@item cvs-commit-buffer-require-final-newline
+When you enter a log message in the @samp{*cvs-commit-message*} buffer
+pcl-cvs will normally automatically insert a trailing newline, unless
+there already is one. This behavior can be controlled via
+@samp{cvs-commit-buffer-require-final-newline}. If it is @samp{t} (the
+default behavior), a newline will always be appended. If it is
+@samp{nil}, newlines will never be appended. Any other value causes
+pcl-cvs to ask the user whenever there is no trailing newline in the
+commit message buffer.
+
+@item cvs-sort-ignore-file
+If this variable is set to any non-@samp{nil} value the
+@file{.cvsignore} will always be sorted whenever you use
+@samp{cvs-mode-ignore} to add a file to it. This option is on by
+default.
+
+@end table
+
+
+@node Future enhancements, Bugs, 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. Below is my current wish-list for future releases
+of pcl-cvs. Please, let me know which of these features you want most.
+They are listed below in approximately the order that I currently think
+I will implement them in.
+
+@itemize @bullet
+@item
+Rewritten parser code. There are many situations where pcl-cvs will
+fail to recognize the output from CVS. The situation could be greatly
+increased.
+
+@item
+@samp{cvs-status}. This will run @samp{cvs status} in a directory and
+produce a buffer that looks pretty much like the current *cvs* buffer.
+That buffer will include information for all version-controlled files.
+(There will be a simple keystroke to remove all "uninteresting" files,
+that is, files that are "Up-to-date"). In this new buffer you will be
+able to update a file, commit a file, et c. The big win with this is
+that you will be able to watch the differences between your current
+working file and the head revision in the repository before you update
+the file, and you can then choose to update it or let it wait for a
+while longer.
+
+@item
+Log mode. When this mode is finished you will be able to move around
+(using @kbd{n} and @kbd{p}) between the revisions of a file, mark two of
+them, and run a diff between them. You will be able to hide branches
+(similar to the way you can hide sub-paragraphs in outline-mode) and do
+merges between revisions. Other ideas about this are welcome.
+
+@item
+The current model for marks in the *cvs* buffer seems to be confusing.
+I am considering to use the VM model instead, where marks are normally
+inactive. To activate the mark, you issue a command like
+@samp{cvs-mode-next-command-uses-marks}. I might implement a flag so
+that you can use either version. Feedback on this before I start coding
+it is very welcome.
+
+@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. I planed to put these kind of commands on the prefix
+@kbd{C-c C-v}, but that turned out to be used by for instance c++-mode.
+If you have any suggestions for a better prefix key, please let me know.
+
+@item
+Increased robustness. For instance, you can not currently press
+@kbd{C-g} when you are entering the description of a file that you are
+adding without confusing pcl-cvs.
+
+@item
+Support for multiple active *cvs* buffers.
+
+@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
+An ability to send user-supplied options to all the cvs commands.
+
+@item
+Pcl-cvs is not at all clever about what it should do when @samp{cvs
+update} runs a program (due to the @samp{-u} option in the
+@file{modules} file --- see @samp{cvs(5)}). The current release uses a
+regexp to search for the end. At the very least that regexp should be
+configured for different modules. Tell me if you have any idea about
+what is the right thing to do. In a perfect world the program should
+also be allowed to print to @samp{stderr} without causing pcl-cvs to
+crash.
+@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{Bugs} for
+information about how to reach me.@refill
+
+So far, I have written most of pcl-cvs in my all-to-rare spare time. If
+you want pcl-cvs to be developed faster you can write a contract with
+Signum Support to do the extension. You can reach Signum Support by
+email to @samp{info@@signum.se} or via mail to Signum Support AB, Box
+2044, S-580 02 Linkoping, Sweden. Phone: +46 (0) 13 - 21 46 00. Fax:
++46 (0) 13 - 21 47 00.
+
+
+@node Bugs, COPYING, Future enhancements, Top
+@comment node-name, next, previous, up
+
+@chapter Bugs (known and unknown)
+@cindex Reporting bugs and ideas
+@cindex Bugs, how to report them
+@cindex Author, how to reach
+@cindex Email to the author
+@cindex Known bugs
+@cindex Bugs, known
+@cindex FAQ
+@cindex Problems, list of common
+
+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!
+
+Below is a partial list of currently known problems with pcl-cvs version
+1.05.
+
+@table @asis
+@item Commit causes Emacs to hang
+Emacs waits for the @samp{cvs commit} command to finish before you can
+do anything. If you start a background job from the loginfo file you
+must take care that it closes @samp{stdout} and @samp{stderr} if you do
+not want to wait for it. (You do that with @samp{background-command &>-
+2&>- &} if you are starting @samp{background-command} from a
+@samp{/bin/sh} shell script).
+
+Your emacs will also hang if there was a lock file in the repository.
+In this case you can type @kbd{C-g} to get control over your emacs
+again.
+
+@item Name clash in Emacs 19
+This is really a bug in Elib or the Emacs 19 distribution. Both Elib and
+Emacs 19.6 through at least 19.10 contains a file named
+@file{cookie.el}. One of the files will have to be renamed, and we are
+currently negotiating about which of the files to rename.
+
+@item Commands while cvs-update is running
+It is possible to type commands in the *cvs* buffer while the update is
+running, but error messages is all that you will get. The error
+messages should be better.
+
+@item Unexpected output from CVS
+Unexpected output from CVS confuses pcl-cvs. It will currently create a
+bug report that you can mail to me. It should do something more
+civilized.
+@end table
+
+
+@node COPYING, Function and Variable Index, Bugs, Top
+@comment node-name, next, previous, up
+
+@appendix GNU GENERAL PUBLIC LICENSE
+@c @include gpl.texinfo
+
+
+@node Function and Variable Index, Concept Index, COPYING, 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
--- /dev/null
+%% TeX macros to handle texinfo files
+
+% Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+%This texinfo.tex file is free software; you can redistribute it and/or
+%modify it under the terms of the GNU General Public License as
+%published by the Free Software Foundation; either version 2, or (at
+%your option) any later version.
+
+%This texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, write
+%to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+%USA.
+
+
+%In other words, you are welcome to use, share and improve this program.
+%You are forbidden to forbid anyone else to use, share and improve
+%what you give them. Help stamp out software-hoarding!
+
+% This automatically updates the version number based on RCS.
+\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}}
+\deftexinfoversion$fixed-revision: 2.137 $
+\message{Loading texinfo package [Version \texinfoversion]:}
+
+% Print the version number if in a .fmt file.
+\everyjob{\message{[Texinfo version \texinfoversion]}\message{}}
+
+% Save some parts of plain tex whose names we will redefine.
+
+\let\ptextilde=\~
+\let\ptexlbrace=\{
+\let\ptexrbrace=\}
+\let\ptexdots=\dots
+\let\ptexdot=\.
+\let\ptexstar=\*
+\let\ptexend=\end
+\let\ptexbullet=\bullet
+\let\ptexb=\b
+\let\ptexc=\c
+\let\ptexi=\i
+\let\ptext=\t
+\let\ptexl=\l
+\let\ptexL=\L
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ \gdef\tie{\leavevmode\penalty\@M\ }
+}
+\let\~ = \tie % And make it available as @~.
+
+\message{Basics,}
+\chardef\other=12
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Set up fixed words for English.
+\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi%
+\def\putwordInfo{Info}%
+\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi%
+\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi%
+\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi%
+\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi%
+\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi%
+\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi%
+\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi%
+\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi%
+\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi%
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+\hyphenation{ap-pen-dix}
+\hyphenation{mini-buf-fer mini-buf-fers}
+\hyphenation{eshell}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen \bindingoffset \bindingoffset=0pt
+\newdimen \normaloffset \normaloffset=\hoffset
+\newdimen\pagewidth \newdimen\pageheight
+\pagewidth=\hsize \pageheight=\vsize
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{\tracingcommands2 \tracingstats2
+ \tracingpages1 \tracingoutput1 \tracinglostchars1
+ \tracingmacros2 \tracingparagraphs1 \tracingrestores1
+ \showboxbreadth\maxdimen\showboxdepth\maxdimen
+}%
+
+%---------------------Begin change-----------------------
+%
+%%%% For @cropmarks command.
+% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\cornerlong \newdimen\cornerthick
+\newdimen \topandbottommargin
+\newdimen \outerhsize \newdimen \outervsize
+\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks
+\outerhsize=7in
+%\outervsize=9.5in
+% Alternative @smallbook page size is 9.25in
+\outervsize=9.25in
+\topandbottommargin=.75in
+%
+%---------------------End change-----------------------
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions itself, but you have to call it yourself.
+\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}}
+\def\onepageout#1{\hoffset=\normaloffset
+\ifodd\pageno \advance\hoffset by \bindingoffset
+\else \advance\hoffset by -\bindingoffset\fi
+{\escapechar=`\\\relax % makes sure backslash is used in output files.
+\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
+{\let\hsize=\pagewidth \makefootline}}}%
+\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+
+%%%% For @cropmarks command %%%%
+
+% Here is a modification of the main output routine for Near East Publications
+% This provides right-angle cropmarks at all four corners.
+% The contents of the page are centerlined into the cropmarks,
+% and any desired binding offset is added as an \hskip on either
+% site of the centerlined box. (P. A. MacKay, 12 November, 1986)
+%
+\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up
+{\escapechar=`\\\relax % makes sure backslash is used in output files.
+ \shipout
+ \vbox to \outervsize{\hsize=\outerhsize
+ \vbox{\line{\ewtop\hfill\ewtop}}
+ \nointerlineskip
+ \line{\vbox{\moveleft\cornerthick\nstop}
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}}
+ \vskip \topandbottommargin
+ \centerline{\ifodd\pageno\hskip\bindingoffset\fi
+ \vbox{
+ {\let\hsize=\pagewidth \makeheadline}
+ \pagebody{#1}
+ {\let\hsize=\pagewidth \makefootline}}
+ \ifodd\pageno\else\hskip\bindingoffset\fi}
+ \vskip \topandbottommargin plus1fill minus1fill
+ \boxmaxdepth\cornerthick
+ \line{\vbox{\moveleft\cornerthick\nsbot}
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}}
+ \nointerlineskip
+ \vbox{\line{\ewbot\hfill\ewbot}}
+ }}
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+%
+% Do @cropmarks to get crop marks
+\def\cropmarks{\let\onepageout=\croppageout }
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1 \unvbox#1
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+%
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg#1{%
+ \let\next = #1%
+ \begingroup
+ \obeylines
+ \futurelet\temp\parseargx
+}
+
+% If the next token is an obeyed space (from an @example environment or
+% the like), remove it and recurse. Otherwise, we're done.
+\def\parseargx{%
+ % \obeyedspace is defined far below, after the definition of \sepspaces.
+ \ifx\obeyedspace\temp
+ \expandafter\parseargdiscardspace
+ \else
+ \expandafter\parseargline
+ \fi
+}
+
+% Remove a single space (as the delimiter token to the macro call).
+{\obeyspaces %
+ \gdef\parseargdiscardspace {\futurelet\temp\parseargx}}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ %
+ % First remove any @c comment, then any @comment.
+ % Result of each macro is put in \toks0.
+ \argremovec #1\c\relax %
+ \expandafter\argremovecomment \the\toks0 \comment\relax %
+ %
+ % Call the caller's macro, saved as \next in \parsearg.
+ \expandafter\next\expandafter{\the\toks0}%
+ }%
+}
+
+% Since all \c{,omment} does is throw away the argument, we can let TeX
+% do that for us. The \relax here is matched by the \relax in the call
+% in \parseargline; it could be more or less anything, its purpose is
+% just to delimit the argument to the \c.
+\def\argremovec#1\c#2\relax{\toks0 = {#1}}
+\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}}
+
+% \argremovec{,omment} might leave us with trailing spaces, though; e.g.,
+% @end itemize @c foo
+% will have two active spaces as part of the argument with the
+% `itemize'. Here we remove all active spaces from #1, and assign the
+% result to \toks0.
+%
+% This loses if there are any *other* active characters besides spaces
+% in the argument -- _ ^ +, for example -- since they get expanded.
+% Fortunately, Texinfo does not define any such commands. (If it ever
+% does, the catcode of the characters in questionwill have to be changed
+% here.) But this means we cannot call \removeactivespaces as part of
+% \argremovec{,omment}, since @c uses \parsearg, and thus the argument
+% that \parsearg gets might well have any character at all in it.
+%
+\def\removeactivespaces#1{%
+ \begingroup
+ \ignoreactivespaces
+ \edef\temp{#1}%
+ \global\toks0 = \expandafter{\temp}%
+ \endgroup
+}
+
+% Change the active space to expand to nothing.
+%
+\begingroup
+ \obeyspaces
+ \gdef\ignoreactivespaces{\obeyspaces\let =\empty}
+\endgroup
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+%% These are used to keep @begin/@end levels from running away
+%% Call \inENV within environments (after a \begingroup)
+\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi}
+\def\ENVcheck{%
+\ifENV\errmessage{Still within an environment. Type Return to continue.}
+\endgroup\fi} % This is not perfect, but it should reduce lossage
+
+% @begin foo is the same as @foo, for now.
+\newhelp\EMsimple{Type <Return> to continue.}
+
+\outer\def\begin{\parsearg\beginxxx}
+
+\def\beginxxx #1{%
+\expandafter\ifx\csname #1\endcsname\relax
+{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else
+\csname #1\endcsname\fi}
+
+% @end foo executes the definition of \Efoo.
+%
+\def\end{\parsearg\endxxx}
+\def\endxxx #1{%
+ \removeactivespaces{#1}%
+ \edef\endthing{\the\toks0}%
+ %
+ \expandafter\ifx\csname E\endthing\endcsname\relax
+ \expandafter\ifx\csname \endthing\endcsname\relax
+ % There's no \foo, i.e., no ``environment'' foo.
+ \errhelp = \EMsimple
+ \errmessage{Undefined command `@end \endthing'}%
+ \else
+ \unmatchedenderror\endthing
+ \fi
+ \else
+ % Everything's ok; the right environment has been started.
+ \csname E\endthing\endcsname
+ \fi
+}
+
+% There is an environment #1, but it hasn't been started. Give an error.
+%
+\def\unmatchedenderror#1{%
+ \errhelp = \EMsimple
+ \errmessage{This `@end #1' doesn't have a matching `@#1'}%
+}
+
+% Define the control sequence \E#1 to give an unmatched @end error.
+%
+\def\defineunmatchedend#1{%
+ \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}%
+}
+
+
+% Single-spacing is done by various environments (specifically, in
+% \nonfillstart and \quotations).
+\newskip\singlespaceskip \singlespaceskip = 12.5pt
+\def\singlespace{%
+ % Why was this kern here? It messes up equalizing space above and below
+ % environments. --karl, 6may93
+ %{\advance \baselineskip by -\singlespaceskip
+ %\kern \baselineskip}%
+ \setleading \singlespaceskip
+}
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt \char '100}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+
+\def\mylbrace {{\tt \char '173}}
+\def\myrbrace {{\tt \char '175}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=3000 }
+
+% @enddots{} is an end-of-sentence ellipsis.
+\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000}
+
+% @! is an end-of-sentence bang.
+\gdef\!{!\spacefactor=3000 }
+
+% @? is an end-of-sentence query.
+\gdef\?{?\spacefactor=3000 }
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+\def\group{\begingroup
+ \ifnum\catcode13=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ %
+ % The \vtop we start below produces a box with normal height and large
+ % depth; thus, TeX puts \baselineskip glue before it, and (when the
+ % next line of text is done) \lineskip glue after it. (See p.82 of
+ % the TeXbook.) Thus, space below is not quite equal to space
+ % above. But it's pretty close.
+ \def\Egroup{%
+ \egroup % End the \vtop.
+ \endgroup % End the \group.
+ }%
+ %
+ \vtop\bgroup
+ % We have to put a strut on the last line in case the @group is in
+ % the midst of an example, rather than completely enclosing it.
+ % Otherwise, the interline space between the last line of the group
+ % and the first line afterwards is too small. But we can't put the
+ % strut in \Egroup, since there it would be on a line by itself.
+ % Hence this just inserts a strut at the beginning of each line.
+ \everypar = {\strut}%
+ %
+ % Since we have a strut on every line, we don't need any of TeX's
+ % normal interline spacing.
+ \offinterlineskip
+ %
+ % OK, but now we have to do something about blank
+ % lines in the input in @example-like environments, which normally
+ % just turn into \lisppar, which will insert no space now that we've
+ % turned off the interline space. Simplest is to make them be an
+ % empty paragraph.
+ \ifx\par\lisppar
+ \edef\par{\leavevmode \par}%
+ %
+ % Reset ^^M's definition to new definition of \par.
+ \obeylines
+ \fi
+ %
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+\def\need{\parsearg\needx}
+
+% Old definition--didn't work.
+%\def\needx #1{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000
+%\prevdepth=-1000pt
+%}}
+
+\def\needx#1{%
+ % Go into vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % Don't add any leading before our big empty box, but allow a page
+ % break, since the best break might be right here.
+ \allowbreak
+ \nointerlineskip
+ \vtop to #1\mil{\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+}
+
+% @br forces paragraph break
+
+\let\br = \par
+
+% @dots{} output some dots
+
+\def\dots{$\ldots$}
+
+% @page forces the start of a new page
+
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\def\exdent{\parsearg\exdentyyy}
+\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}}
+
+% This defn is used inside nofill environments such as @example.
+\def\nofillexdent{\parsearg\nofillexdentyyy}
+\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount
+\leftline{\hskip\leftskip{\rm#1}}}}
+
+%\hbox{{\rm#1}}\hfil\break}}
+
+% @include file insert text of that file as input.
+
+\def\include{\parsearg\includezzz}
+%Use \input\thisfile to avoid blank after \input, which may be an active
+%char (in which case the blank would become the \input argument).
+%The grouping keeps the value of \thisfile correct even when @include
+%is nested.
+\def\includezzz #1{\begingroup
+\def\thisfile{#1}\input\thisfile
+\endgroup}
+
+\def\thisfile{}
+
+% @center line outputs that line, centered
+
+\def\center{\parsearg\centerzzz}
+\def\centerzzz #1{{\advance\hsize by -\leftskip
+\advance\hsize by -\rightskip
+\centerline{#1}}}
+
+% @sp n outputs n lines of vertical space
+
+\def\sp{\parsearg\spxxx}
+\def\spxxx #1{\par \vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other%
+\parsearg \commentxxx}
+
+\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 }
+
+\let\c=\comment
+
+% Prevent errors for section commands.
+% Used in @ignore and in failing conditionals.
+\def\ignoresections{%
+\let\chapter=\relax
+\let\unnumbered=\relax
+\let\top=\relax
+\let\unnumberedsec=\relax
+\let\unnumberedsection=\relax
+\let\unnumberedsubsec=\relax
+\let\unnumberedsubsection=\relax
+\let\unnumberedsubsubsec=\relax
+\let\unnumberedsubsubsection=\relax
+\let\section=\relax
+\let\subsec=\relax
+\let\subsubsec=\relax
+\let\subsection=\relax
+\let\subsubsection=\relax
+\let\appendix=\relax
+\let\appendixsec=\relax
+\let\appendixsection=\relax
+\let\appendixsubsec=\relax
+\let\appendixsubsection=\relax
+\let\appendixsubsubsec=\relax
+\let\appendixsubsubsection=\relax
+\let\contents=\relax
+\let\smallbook=\relax
+\let\titlepage=\relax
+}
+
+% Used in nested conditionals, where we have to parse the Texinfo source
+% and so want to turn off most commands, in case they are used
+% incorrectly.
+%
+\def\ignoremorecommands{%
+ \let\defcv = \relax
+ \let\deffn = \relax
+ \let\deffnx = \relax
+ \let\defindex = \relax
+ \let\defivar = \relax
+ \let\defmac = \relax
+ \let\defmethod = \relax
+ \let\defop = \relax
+ \let\defopt = \relax
+ \let\defspec = \relax
+ \let\deftp = \relax
+ \let\deftypefn = \relax
+ \let\deftypefun = \relax
+ \let\deftypevar = \relax
+ \let\deftypevr = \relax
+ \let\defun = \relax
+ \let\defvar = \relax
+ \let\defvr = \relax
+ \let\ref = \relax
+ \let\xref = \relax
+ \let\printindex = \relax
+ \let\pxref = \relax
+ \let\settitle = \relax
+ \let\include = \relax
+ \let\lowersections = \relax
+ \let\down = \relax
+ \let\raisesections = \relax
+ \let\up = \relax
+ \let\set = \relax
+ \let\clear = \relax
+ \let\item = \relax
+ \let\message = \relax
+}
+
+% Ignore @ignore ... @end ignore.
+%
+\def\ignore{\doignore{ignore}}
+
+% Also ignore @ifinfo, @ifhtml, @html, @menu, and @direntry text.
+%
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\html{\doignore{html}}
+\def\menu{\doignore{menu}}
+\def\direntry{\doignore{direntry}}
+
+% Ignore text until a line `@end #1'.
+%
+\def\doignore#1{\begingroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define a command to swallow text until we reach `@end #1'.
+ \long\def\doignoretext##1\end #1{\enddoignore}%
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \catcode32 = 10
+ %
+ % And now expand that command.
+ \doignoretext
+}
+
+% What we do to finish off ignored text.
+%
+\def\enddoignore{\endgroup\ignorespaces}%
+
+\newif\ifwarnedobs\warnedobsfalse
+\def\obstexwarn{%
+ \ifwarnedobs\relax\else
+ % We need to warn folks that they may have trouble with TeX 3.0.
+ % This uses \immediate\write16 rather than \message to get newlines.
+ \immediate\write16{}
+ \immediate\write16{***WARNING*** for users of Unix TeX 3.0!}
+ \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).}
+ \immediate\write16{If you are running another version of TeX, relax.}
+ \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.}
+ \immediate\write16{ Then upgrade your TeX installation if you can.}
+ \immediate\write16{If you are stuck with version 3.0, run the}
+ \immediate\write16{ script ``tex3patch'' from the Texinfo distribution}
+ \immediate\write16{ to use a workaround.}
+ \immediate\write16{}
+ \warnedobstrue
+ \fi
+}
+
+% **In TeX 3.0, setting text in \nullfont hangs tex. For a
+% workaround (which requires the file ``dummy.tfm'' to be installed),
+% uncomment the following line:
+%%%%%\font\nullfont=dummy\let\obstexwarn=\relax
+
+% Ignore text, except that we keep track of conditional commands for
+% purposes of nesting, up to an `@end #1' command.
+%
+\def\nestedignore#1{%
+ \obstexwarn
+ % We must actually expand the ignored text to look for the @end
+ % command, so that nested ignore constructs work. Thus, we put the
+ % text into a \vbox and then do nothing with the result. To minimize
+ % the change of memory overflow, we follow the approach outlined on
+ % page 401 of the TeXbook: make the current font be a dummy font.
+ %
+ \setbox0 = \vbox\bgroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define `@end #1' to end the box, which will in turn undefine the
+ % @end command again.
+ \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}%
+ %
+ % We are going to be parsing Texinfo commands. Most cause no
+ % trouble when they are used incorrectly, but some commands do
+ % complicated argument parsing or otherwise get confused, so we
+ % undefine them.
+ %
+ % We can't do anything about stray @-signs, unfortunately;
+ % they'll produce `undefined control sequence' errors.
+ \ignoremorecommands
+ %
+ % Set the current font to be \nullfont, a TeX primitive, and define
+ % all the font commands to also use \nullfont. We don't use
+ % dummy.tfm, as suggested in the TeXbook, because not all sites
+ % might have that installed. Therefore, math mode will still
+ % produce output, but that should be an extremely small amount of
+ % stuff compared to the main input.
+ %
+ \nullfont
+ \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont
+ \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont
+ \let\tensf = \nullfont
+ % Similarly for index fonts (mostly for their use in
+ % smallexample)
+ \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont
+ \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont
+ \let\indsf = \nullfont
+ %
+ % Don't complain when characters are missing from the fonts.
+ \tracinglostchars = 0
+ %
+ % Don't bother to do space factor calculations.
+ \frenchspacing
+ %
+ % Don't report underfull hboxes.
+ \hbadness = 10000
+ %
+ % Do minimal line-breaking.
+ \pretolerance = 10000
+ %
+ % Do not execute instructions in @tex
+ \def\tex{\doignore{tex}}
+}
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+%
+\def\set{\parsearg\setxxx}
+\def\setxxx#1{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ \def\temp{#2}%
+ \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty
+ \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted.
+ \fi
+}
+\def\setzzz#1#2 \endsetzzz{\expandafter\xdef\csname SET#1\endcsname{#2}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\def\clear{\parsearg\clearxxx}
+\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax}
+
+% @value{foo} gets the text saved in variable foo.
+%
+\def\value#1{\expandafter
+ \ifx\csname SET#1\endcsname\relax
+ {\{No value for ``#1''\}}
+ \else \csname SET#1\endcsname \fi}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+\def\ifset{\parsearg\ifsetxxx}
+\def\ifsetxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifsetfail
+ \else
+ \expandafter\ifsetsucceed
+ \fi
+}
+\def\ifsetsucceed{\conditionalsucceed{ifset}}
+\def\ifsetfail{\nestedignore{ifset}}
+\defineunmatchedend{ifset}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+\def\ifclear{\parsearg\ifclearxxx}
+\def\ifclearxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifclearsucceed
+ \else
+ \expandafter\ifclearfail
+ \fi
+}
+\def\ifclearsucceed{\conditionalsucceed{ifclear}}
+\def\ifclearfail{\nestedignore{ifclear}}
+\defineunmatchedend{ifclear}
+
+% @iftex always succeeds; we read the text following, through @end
+% iftex). But `@end iftex' should be valid only after an @iftex.
+%
+\def\iftex{\conditionalsucceed{iftex}}
+\defineunmatchedend{iftex}
+
+% We can't just want to start a group at @iftex (for example) and end it
+% at @end iftex, since then @set commands inside the conditional have no
+% effect (they'd get reverted at the end of the group). So we must
+% define \Eiftex to redefine itself to be its previous value. (We can't
+% just define it to fail again with an ``unmatched end'' error, since
+% the @ifset might be nested.)
+%
+\def\conditionalsucceed#1{%
+ \edef\temp{%
+ % Remember the current value of \E#1.
+ \let\nece{prevE#1} = \nece{E#1}%
+ %
+ % At the `@end #1', redefine \E#1 to be its previous value.
+ \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}%
+ }%
+ \temp
+}
+
+% We need to expand lots of \csname's, but we don't want to expand the
+% control sequences after we've constructed them.
+%
+\def\nece#1{\expandafter\noexpand\csname#1\endcsname}
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math means output in math mode.
+% We don't use $'s directly in the definition of \math because control
+% sequences like \math are expanded when the toc file is written. Then,
+% we read the toc file back, the $'s will be normal characters (as they
+% should be, according to the definition of Texinfo). So we must use a
+% control sequence to switch into and out of math mode.
+%
+% This isn't quite enough for @math to work properly in indices, but it
+% seems unlikely it will ever be needed there.
+%
+\let\implicitmath = $
+\def\math#1{\implicitmath #1\implicitmath}
+
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{\implicitmath\ptexbullet\implicitmath}
+\def\minus{\implicitmath-\implicitmath}
+
+\def\node{\ENVcheck\parsearg\nodezzz}
+\def\nodezzz#1{\nodexxx [#1,]}
+\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
+\let\nwnode=\node
+\let\lastnode=\relax
+
+\def\donoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\setref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\unnumbnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\appendixnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\let\refill=\relax
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \readauxfile
+ \opencontents
+ \openindices
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \global\let\setfilename=\comment % Ignore extra @setfilename cmds.
+ \comment % Ignore the actual filename.
+}
+
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+\message{fonts,}
+
+% Font-change commands.
+
+% Texinfo supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf analogous to plain's \rm, etc.
+\newfam\sffam
+\def\sf{\fam=\sffam \tensf}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+%% Try out Computer Modern fonts at \magstephalf
+\let\mainmagstep=\magstephalf
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+\def\setfont#1#2{\font#1=\fontprefix#2}
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+
+\ifx\bigger\relax
+\let\mainmagstep=\magstep1
+\setfont\textrm{r12}
+\setfont\texttt{tt12}
+\else
+\setfont\textrm{r10 scaled \mainmagstep}
+\setfont\texttt{tt10 scaled \mainmagstep}
+\fi
+% Instead of cmb10, you many want to use cmbx10.
+% cmbx10 is a prettier font on its own, but cmb10
+% looks better when embedded in a line with cmr10.
+\setfont\textbf{b10 scaled \mainmagstep}
+\setfont\textit{ti10 scaled \mainmagstep}
+\setfont\textsl{sl10 scaled \mainmagstep}
+\setfont\textsf{ss10 scaled \mainmagstep}
+\setfont\textsc{csc10 scaled \mainmagstep}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+
+% A few fonts for @defun, etc.
+\setfont\defbf{bx10 scaled \magstep1} %was 1314
+\setfont\deftt{tt10 scaled \magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf}
+
+% Fonts for indices and small examples.
+% We actually use the slanted font rather than the italic,
+% because texinfo normally uses the slanted fonts for that.
+% Do not make many font distinctions in general in the index, since they
+% aren't very useful.
+\setfont\ninett{tt9}
+\setfont\indrm{r9}
+\setfont\indit{sl9}
+\let\indsl=\indit
+\let\indtt=\ninett
+\let\indsf=\indrm
+\let\indbf=\indrm
+\let\indsc=\indrm
+\font\indi=cmmi9
+\font\indsy=cmsy9
+
+% Fonts for headings
+\setfont\chaprm{bx12 scaled \magstep2}
+\setfont\chapit{ti12 scaled \magstep2}
+\setfont\chapsl{sl12 scaled \magstep2}
+\setfont\chaptt{tt12 scaled \magstep2}
+\setfont\chapsf{ss12 scaled \magstep2}
+\let\chapbf=\chaprm
+\setfont\chapsc{csc10 scaled\magstep3}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+
+\setfont\secrm{bx12 scaled \magstep1}
+\setfont\secit{ti12 scaled \magstep1}
+\setfont\secsl{sl12 scaled \magstep1}
+\setfont\sectt{tt12 scaled \magstep1}
+\setfont\secsf{ss12 scaled \magstep1}
+\setfont\secbf{bx12 scaled \magstep1}
+\setfont\secsc{csc10 scaled\magstep2}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+
+% \setfont\ssecrm{bx10 scaled \magstep1} % This size an font looked bad.
+% \setfont\ssecit{cmti10 scaled \magstep1} % The letters were too crowded.
+% \setfont\ssecsl{sl10 scaled \magstep1}
+% \setfont\ssectt{tt10 scaled \magstep1}
+% \setfont\ssecsf{ss10 scaled \magstep1}
+
+%\setfont\ssecrm{b10 scaled 1315} % Note the use of cmb rather than cmbx.
+%\setfont\ssecit{ti10 scaled 1315} % Also, the size is a little larger than
+%\setfont\ssecsl{sl10 scaled 1315} % being scaled magstep1.
+%\setfont\ssectt{tt10 scaled 1315}
+%\setfont\ssecsf{ss10 scaled 1315}
+
+%\let\ssecbf=\ssecrm
+
+\setfont\ssecrm{bx12 scaled \magstephalf}
+\setfont\ssecit{ti12 scaled \magstephalf}
+\setfont\ssecsl{sl12 scaled \magstephalf}
+\setfont\ssectt{tt12 scaled \magstephalf}
+\setfont\ssecsf{ss12 scaled \magstephalf}
+\setfont\ssecbf{bx12 scaled \magstephalf}
+\setfont\ssecsc{csc10 scaled \magstep1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled \magstep1
+% The smallcaps and symbol fonts should actually be scaled \magstep1.5,
+% but that is not a standard magnification.
+
+% Fonts for title page:
+\setfont\titlerm{bx12 scaled \magstep3}
+\let\authorrm = \secrm
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families. Since
+% texinfo doesn't allow for producing subscripts and superscripts, we
+% don't bother to reset \scriptfont and \scriptscriptfont (which would
+% also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+ \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy
+ \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf
+ \textfont\ttfam = \tentt \textfont\sffam = \tensf
+}
+
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE. We do this so that font changes will continue to work
+% in math mode, where it is the current \fam that is relevant in most
+% cases, not the current. Plain TeX does, for example,
+% \def\bf{\fam=\bffam \tenbf} By redefining \tenbf, we obviate the need
+% to redefine \bf itself.
+\def\textfonts{%
+ \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+ \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+ \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
+ \resetmathfonts}
+\def\chapfonts{%
+ \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+ \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+ \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
+ \resetmathfonts}
+\def\secfonts{%
+ \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+ \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+ \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
+ \resetmathfonts}
+\def\subsecfonts{%
+ \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+ \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+ \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
+ \resetmathfonts}
+\def\indexfonts{%
+ \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl
+ \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc
+ \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy
+ \resetmathfonts}
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\textfonts
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Fonts for short table of contents.
+\setfont\shortcontrm{r12}
+\setfont\shortcontbf{bx12}
+\setfont\shortcontsl{sl12}
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi}
+\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\var=\smartitalic
+\let\dfn=\smartitalic
+\let\emph=\smartitalic
+\let\cite=\smartitalic
+
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+\def\t#1{%
+ {\tt \nohyphenation \rawbackslash \frenchspacing #1}%
+ \null
+}
+\let\ttfont = \t
+%\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null}
+\def\samp #1{`\tclose{#1}'\null}
+\def\key #1{{\tt \nohyphenation \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+\let\file=\samp
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \rawbackslash
+ \frenchspacing
+ #1%
+ }%
+ \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in \code.
+% Otherwise, it is too hard to avoid overful hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate an a dash.
+% -- rms.
+{
+\catcode`\-=\active
+\catcode`\_=\active
+\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex}
+% The following is used by \doprintindex to insure that long function names
+% wrap around. It is necessary for - and _ to be active before the index is
+% read from the file, as \entry parses the arguments long before \code is
+% ever called. -- mycroft
+\global\def\indexbreaks{\catcode`\-=\active \let-\realdash \catcode`\_=\active \let_\realunder}
+}
+\def\realdash{-}
+\def\realunder{_}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{\normalunderscore\discretionary{}{}{}}
+\def\codex #1{\tclose{#1}\endgroup}
+
+%\let\exp=\tclose %Was temporary
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else\tclose{\look}\fi
+\else\tclose{\look}\fi}
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of
+% @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
+
+\def\l#1{{\li #1}\null} %
+
+\def\r#1{{\rm #1}} % roman font
+% Use of \lowercase was suggested.
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\def\titlefont#1{{\titlerm #1}}
+
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+\def\shorttitlepage{\parsearg\shorttitlepagezzz}
+\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\def\titlepage{\begingroup \parindent=0pt \textfonts
+ \let\subtitlerm=\tenrm
+% I deinstalled the following change because \cmr12 is undefined.
+% This change was not in the ChangeLog anyway. --rms.
+% \let\subtitlerm=\cmr12
+ \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}%
+ %
+ \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}%
+ %
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ %
+ % Now you can print the title using @title.
+ \def\title{\parsearg\titlezzz}%
+ \def\titlezzz##1{\leftline{\titlefont{##1}}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt}%
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Now you can put text using @subtitle.
+ \def\subtitle{\parsearg\subtitlezzz}%
+ \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}%
+ %
+ % @author should come last, but may come many times.
+ \def\author{\parsearg\authorzzz}%
+ \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi
+ {\authorfont \leftline{##1}}}%
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \oldpage
+ \let\page = \oldpage
+ \hbox{}}%
+% \def\page{\oldpage \hbox{}}
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ \HEADINGSon
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks \evenheadline % Token sequence for heading line of even pages
+\newtoks \oddheadline % Token sequence for heading line of odd pages
+\newtoks \evenfootline % Token sequence for footing line of even pages
+\newtoks \oddfootline % Token sequence for footing line of odd pages
+
+% Now make Tex use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\everyheading{\parsearg\everyheadingxxx}
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\everyfooting{\parsearg\everyfootingxxx}
+
+{\catcode`\@=0 %
+
+\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish}
+\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish}
+\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish}
+\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
+\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish}
+\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish}
+\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+%
+}% unbind the catcode of @.
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+
+% Subroutines used in generating headings
+% Produces Day Month Year style of output.
+\def\today{\number\day\space
+\ifcase\month\or
+January\or February\or March\or April\or May\or June\or
+July\or August\or September\or October\or November\or December\fi
+\space\number\year}
+
+% Use this if you want the Month Day, Year style of output.
+%\def\today{\ifcase\month\or
+%January\or February\or March\or April\or May\or June\or
+%July\or August\or September\or October\or November\or December\fi
+%\space\number\day, \number\year}
+
+% @settitle line... specifies the title of the document, for headings
+% It generates no output of its own
+
+\def\thistitle{No Title}
+\def\settitle{\parsearg\settitlezzz}
+\def\settitlezzz #1{\gdef\thistitle{#1}}
+
+\message{tables,}
+
+% @tabs -- simple alignment
+
+% These don't work. For one thing, \+ is defined as outer.
+% So these macros cannot even be defined.
+
+%\def\tabs{\parsearg\tabszzz}
+%\def\tabszzz #1{\settabs\+#1\cr}
+%\def\tabline{\parsearg\tablinezzz}
+%\def\tablinezzz #1{\+#1\cr}
+%\def\&{&}
+
+% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @vtable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
+\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz}
+
+\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
+\def\internalBkitemx{\itemxpar \parsearg\kitemzzz}
+
+\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}%
+ \itemzzz {#1}}
+
+\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}%
+ \itemzzz {#1}}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemfont{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % Be sure we are not still in the middle of a paragraph.
+ %{\parskip = 0in
+ %\par
+ %}%
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. Unfortunately
+ % we can't prevent a possible page break at the following
+ % \baselineskip glue.
+ \nobreak
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line. Since that
+ % text will be indented by \tableindent, we make the item text be in
+ % a zero-width box.
+ \noindent
+ \rlap{\hskip -\tableindent\box0}\ignorespaces%
+ \endgroup%
+ \itemxneedsnegativevskiptrue%
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a table}}
+\def\itemx{\errmessage{@itemx while not in a table}}
+\def\kitem{\errmessage{@kitem while not in a table}}
+\def\kitemx{\errmessage{@kitemx while not in a table}}
+\def\xitem{\errmessage{@xitem while not in a table}}
+\def\xitemx{\errmessage{@xitemx while not in a table}}
+
+%% Contains a kludge to get @end[description] to work
+\def\description{\tablez{\dontindex}{1}{}{}{}{}}
+
+\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex}
+{\obeylines\obeyspaces%
+\gdef\tablex #1^^M{%
+\tabley\dontindex#1 \endtabley}}
+
+\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex}
+{\obeylines\obeyspaces%
+\gdef\ftablex #1^^M{%
+\tabley\fnitemindex#1 \endtabley
+\def\Eftable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex}
+{\obeylines\obeyspaces%
+\gdef\vtablex #1^^M{%
+\tabley\vritemindex#1 \endtabley
+\def\Evtable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\dontindex #1{}
+\def\fnitemindex #1{\doind {fn}{\code{#1}}}%
+\def\vritemindex #1{\doind {vr}{\code{#1}}}%
+
+{\obeyspaces %
+\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup%
+\tablez{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+\def\tablez #1#2#3#4#5#6{%
+\aboveenvbreak %
+\begingroup %
+\def\Edescription{\Etable}% Neccessary kludge.
+\let\itemindex=#1%
+\ifnum 0#3>0 \advance \leftskip by #3\mil \fi %
+\ifnum 0#4>0 \tableindent=#4\mil \fi %
+\ifnum 0#5>0 \advance \rightskip by #5\mil \fi %
+\def\itemfont{#2}%
+\itemmax=\tableindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \tableindent %
+\exdentamount=\tableindent
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def\Etable{\endgraf\afterenvbreak\endgroup}%
+\let\item = \internalBitem %
+\let\itemx = \internalBitemx %
+\let\kitem = \internalBkitem %
+\let\kitemx = \internalBkitemx %
+\let\xitem = \internalBxitem %
+\let\xitemx = \internalBxitemx %
+}
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\def\itemize{\parsearg\itemizezzz}
+
+\def\itemizezzz #1{%
+ \begingroup % ended by the @end itemsize
+ \itemizey {#1}{\Eitemize}
+}
+
+\def\itemizey #1#2{%
+\aboveenvbreak %
+\itemmax=\itemindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \itemindent %
+\exdentamount=\itemindent
+\parindent = 0pt %
+\parskip = \smallskipamount %
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def#2{\endgraf\afterenvbreak\endgroup}%
+\def\itemcontents{#1}%
+\let\item=\itemizeitem}
+
+% Set sfcode to normal for the chars that usually have another value.
+% These are `.?!:;,'
+\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000
+ \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 }
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\def\enumerate{\parsearg\enumeratezzz}
+\def\enumeratezzz #1{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ \begingroup % ended by the @end enumerate
+ %
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call itemizey, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \itemizey{#1.}\Eenumerate\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+% Definition of @item while inside @itemize.
+
+\def\itemizeitem{%
+\advance\itemno by 1
+{\let\par=\endgraf \smallbreak}%
+\ifhmode \errmessage{\in hmode at itemizeitem}\fi
+{\parskip=0in \hskip 0pt
+\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}%
+\vadjust{\penalty 1200}}%
+\flushcr}
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94
+%
+% @multitable ... @endmultitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @percentofhsize .2 .3 .5
+% @item ...
+%
+% Numbers following @percentofhsize are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab, @multicolumn or @endmulticolumn do not need to be on their
+% own lines, but it will not hurt if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @endmultitable
+
+% Default dimensions may be reset by user.
+% @intableparskip will set vertical space between paragraphs in table.
+% @intableparindent will set paragraph indent in table.
+% @spacebetweencols will set horizontal space to be left between columns.
+% @spacebetweenlines will set vertical space to be left between lines.
+
+%%%%
+% Dimensions
+
+\newdimen\intableparskip
+\newdimen\intableparindent
+\newdimen\spacebetweencols
+\newdimen\spacebetweenlines
+\intableparskip=0pt
+\intableparindent=6pt
+\spacebetweencols=12pt
+\spacebetweenlines=12pt
+
+%%%%
+% Macros used to set up halign preamble:
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\percentofhsize\relax
+\def\xpercentofhsize{\percentofhsize}
+\newif\ifsetpercent
+
+\newcount\colcount
+\def\setuptable#1{\def\firstarg{#1}%
+\ifx\firstarg\xendsetuptable\let\go\relax%
+\else
+ \ifx\firstarg\xpercentofhsize\global\setpercenttrue%
+ \else
+ \ifsetpercent
+ \if#1.\else%
+ \global\advance\colcount by1 %
+ \expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}%
+ \fi
+ \else
+ \global\advance\colcount by1
+ \setbox0=\hbox{#1}%
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi%
+ \fi%
+ \let\go\setuptable%
+\fi\go}
+%%%%
+% multitable syntax
+\def\tab{&}
+
+%%%%
+% @multitable ... @endmultitable definitions:
+
+\def\multitable#1\item{\bgroup
+\let\item\cr
+\tolerance=9500
+\hbadness=9500
+\parskip=\intableparskip
+\parindent=\intableparindent
+\overfullrule=0pt
+\global\colcount=0\relax%
+\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}%
+ % To parse everything between @multitable and @item :
+\def\one{#1}\expandafter\setuptable\one\endsetuptable
+ % Need to reset this to 0 after \setuptable.
+\global\colcount=0\relax%
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+\halign\bgroup&\global\advance\colcount by 1\relax%
+\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \spacebetweencols to all columns after
+ % the first one.
+ % If a template has been used, we will add \spacebetweencols
+ % to the width of each template entry.
+ % If user has set preamble in terms of percent of \hsize
+ % we will use that dimension as the width of the column, and
+ % the \leftskip will keep entries from bumping into each other.
+ % Table will start at left margin and final column will justify at
+ % right margin.
+\ifnum\colcount=1
+\else
+ \ifsetpercent
+ \else
+ % If user has <not> set preamble in terms of percent of \hsize
+ % we will advance \hsize by \spacebetweencols
+ \advance\hsize by \spacebetweencols
+ \fi
+ % In either case we will make \leftskip=\spacebetweencols:
+\leftskip=\spacebetweencols
+\fi
+\noindent##}\cr%
+ % \everycr will reset column counter, \colcount, at the end of
+ % each line. Every column entry will cause \colcount to advance by one.
+ % The table preamble
+ % looks at the current \colcount to find the correct column width.
+\global\everycr{\noalign{\nointerlineskip\vskip\spacebetweenlines
+\filbreak%% keeps underfull box messages off when table breaks over pages.
+\global\colcount=0\relax}}}
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within \newindex.
+{\catcode`\@=11
+\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+
+\def\newindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#1}}
+}
+
+% @defindex foo == \newindex{foo}
+
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+
+\def\newcodeindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#1}}
+}
+
+\def\defcodeindex{\parsearg\newcodeindex}
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+\def\synindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#2}}%
+}
+
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+\def\syncodeindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#2}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+\def\indexdummies{%
+% Take care of the plain tex accent commands.
+\def\"{\realbackslash "}%
+\def\`{\realbackslash `}%
+\def\'{\realbackslash '}%
+\def\^{\realbackslash ^}%
+\def\~{\realbackslash ~}%
+\def\={\realbackslash =}%
+\def\b{\realbackslash b}%
+\def\c{\realbackslash c}%
+\def\d{\realbackslash d}%
+\def\u{\realbackslash u}%
+\def\v{\realbackslash v}%
+\def\H{\realbackslash H}%
+% Take care of the plain tex special European modified letters.
+\def\oe{\realbackslash oe}%
+\def\ae{\realbackslash ae}%
+\def\aa{\realbackslash aa}%
+\def\OE{\realbackslash OE}%
+\def\AE{\realbackslash AE}%
+\def\AA{\realbackslash AA}%
+\def\o{\realbackslash o}%
+\def\O{\realbackslash O}%
+\def\l{\realbackslash l}%
+\def\L{\realbackslash L}%
+\def\ss{\realbackslash ss}%
+% Take care of texinfo commands likely to appear in an index entry.
+\def\_{{\realbackslash _}}%
+\def\w{\realbackslash w }%
+\def\bf{\realbackslash bf }%
+\def\rm{\realbackslash rm }%
+\def\sl{\realbackslash sl }%
+\def\sf{\realbackslash sf}%
+\def\tt{\realbackslash tt}%
+\def\gtr{\realbackslash gtr}%
+\def\less{\realbackslash less}%
+\def\hat{\realbackslash hat}%
+\def\char{\realbackslash char}%
+\def\TeX{\realbackslash TeX}%
+\def\dots{\realbackslash dots }%
+\def\copyright{\realbackslash copyright }%
+\def\tclose##1{\realbackslash tclose {##1}}%
+\def\code##1{\realbackslash code {##1}}%
+\def\samp##1{\realbackslash samp {##1}}%
+\def\t##1{\realbackslash r {##1}}%
+\def\r##1{\realbackslash r {##1}}%
+\def\i##1{\realbackslash i {##1}}%
+\def\b##1{\realbackslash b {##1}}%
+\def\cite##1{\realbackslash cite {##1}}%
+\def\key##1{\realbackslash key {##1}}%
+\def\file##1{\realbackslash file {##1}}%
+\def\var##1{\realbackslash var {##1}}%
+\def\kbd##1{\realbackslash kbd {##1}}%
+\def\dfn##1{\realbackslash dfn {##1}}%
+\def\emph##1{\realbackslash emph {##1}}%
+}
+
+% \indexnofonts no-ops all font-change commands.
+% This is used when outputting the strings to sort the index by.
+\def\indexdummyfont#1{#1}
+\def\indexdummytex{TeX}
+\def\indexdummydots{...}
+
+\def\indexnofonts{%
+% Just ignore accents.
+\let\"=\indexdummyfont
+\let\`=\indexdummyfont
+\let\'=\indexdummyfont
+\let\^=\indexdummyfont
+\let\~=\indexdummyfont
+\let\==\indexdummyfont
+\let\b=\indexdummyfont
+\let\c=\indexdummyfont
+\let\d=\indexdummyfont
+\let\u=\indexdummyfont
+\let\v=\indexdummyfont
+\let\H=\indexdummyfont
+% Take care of the plain tex special European modified letters.
+\def\oe{oe}%
+\def\ae{ae}%
+\def\aa{aa}%
+\def\OE{OE}%
+\def\AE{AE}%
+\def\AA{AA}%
+\def\o{o}%
+\def\O{O}%
+\def\l{l}%
+\def\L{L}%
+\def\ss{ss}%
+\let\w=\indexdummyfont
+\let\t=\indexdummyfont
+\let\r=\indexdummyfont
+\let\i=\indexdummyfont
+\let\b=\indexdummyfont
+\let\emph=\indexdummyfont
+\let\strong=\indexdummyfont
+\let\cite=\indexdummyfont
+\let\sc=\indexdummyfont
+%Don't no-op \tt, since it isn't a user-level command
+% and is used in the definitions of the active chars like <, >, |...
+%\let\tt=\indexdummyfont
+\let\tclose=\indexdummyfont
+\let\code=\indexdummyfont
+\let\file=\indexdummyfont
+\let\samp=\indexdummyfont
+\let\kbd=\indexdummyfont
+\let\key=\indexdummyfont
+\let\var=\indexdummyfont
+\let\TeX=\indexdummytex
+\let\dots=\indexdummydots
+}
+
+% To define \realbackslash, we must make \ not be an escape.
+% We must first make another character (@) an escape
+% so we do not become unable to do a definition.
+
+{\catcode`\@=0 \catcode`\\=\other
+@gdef@realbackslash{\}}
+
+\let\indexbackslash=0 %overridden during \printindex.
+
+\let\SETmarginindex=\relax %initialize!
+% workhorse for all \fooindexes
+% #1 is name of index, #2 is stuff to put there
+\def\doind #1#2{%
+% Put the index entry in the margin if desired.
+\ifx\SETmarginindex\relax\else%
+\insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}%
+\fi%
+{\count10=\lastpenalty %
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\escapechar=`\\%
+{\let\folio=0% Expand all macros now EXCEPT \folio
+\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now
+% so it will be output as is; and it will print as backslash in the indx.
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}}}%
+\temp }%
+}\penalty\count10}}
+
+\def\dosubind #1#2#3{%
+{\count10=\lastpenalty %
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\escapechar=`\\%
+{\let\folio=0%
+\def\rawbackslashxx{\indexbackslash}%
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2 #3}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}{#3}}}%
+\temp }%
+}\penalty\count10}}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% This is what you call to cause a particular index to get printed.
+% Write
+% @unnumbered Function Index
+% @printindex fn
+
+\def\printindex{\parsearg\doprintindex}
+
+\def\doprintindex#1{%
+ \tex
+ \dobreak \chapheadingskip {10000}
+ \catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
+ \catcode`\$=\other
+ \catcode`\~=\other
+ \indexbreaks
+ %
+ % The following don't help, since the chars were translated
+ % when the raw index was written, and their fonts were discarded
+ % due to \indexnofonts.
+ %\catcode`\"=\active
+ %\catcode`\^=\active
+ %\catcode`\_=\active
+ %\catcode`\|=\active
+ %\catcode`\<=\active
+ %\catcode`\>=\active
+ % %
+ \def\indexbackslash{\rawbackslashxx}
+ \indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
+ \begindoublecolumns
+ %
+ % See if the index file exists and is nonempty.
+ \openin 1 \jobname.#1s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ (Index is nonexistent)
+ \else
+ %
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \temp
+ \ifeof 1
+ (Index is empty)
+ \else
+ \input \jobname.#1s
+ \fi
+ \fi
+ \closein 1
+ \enddoublecolumns
+ \Etex
+}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+% Same as \bigskipamount except no shrink.
+% \balancecolumns gets confused if there is any shrink.
+\newskip\initialskipamount \initialskipamount 12pt plus4pt
+
+\def\initial #1{%
+{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+\ifdim\lastskip<\initialskipamount
+\removelastskip \penalty-200 \vskip \initialskipamount\fi
+\line{\secbf#1\hfill}\kern 2pt\penalty10000}}
+
+% This typesets a paragraph consisting of #1, dot leaders, and then #2
+% flush to the right margin. It is used for index and table of contents
+% entries. The paragraph is indented by \leftskip.
+%
+\def\entry #1#2{\begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % Do not fill out the last line with white space.
+ \parfillskip = 0in
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % \hangindent is only relevant when the entry text and page number
+ % don't both fit on one line. In that case, bob suggests starting the
+ % dots pretty far over on the line. Unfortunately, a large
+ % indentation looks wrong when the entry text itself is broken across
+ % lines. So we use a small indentation and put up with long leaders.
+ %
+ % \hangafter is reset to 1 (which is the value we want) at the start
+ % of each paragraph, so we need not do anything with that.
+ \hangindent=2em
+ %
+ % When the entry text needs to be broken, just fill out the first line
+ % with blank space.
+ \rightskip = 0pt plus1fil
+ %
+ % Start a ``paragraph'' for the index entry so the line breaking
+ % parameters we've set above will have an effect.
+ \noindent
+ %
+ % Insert the text of the index entry. TeX will do line-breaking on it.
+ #1%
+ % The following is kluged to not output a line of dots in the index if
+ % there are no page numbers. The next person who breaks this will be
+ % cursed by a Unix daemon.
+ \def\tempa{{\rm }}%
+ \def\tempb{#2}%
+ \edef\tempc{\tempa}%
+ \edef\tempd{\tempb}%
+ \ifx\tempc\tempd\ \else%
+ %
+ % If we must, put the page number on a line of its own, and fill out
+ % this line with blank space. (The \hfil is overwhelmed with the
+ % fill leaders glue in \indexdotfill if the page number does fit.)
+ \hfil\penalty50
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ % The `\ ' here is removed by the implicit \unskip that TeX does as
+ % part of (the primitive) \par. Without it, a spurious underfull
+ % \hbox ensues.
+ \ #2% The page number ends the paragraph.
+ \fi%
+ \par
+\endgroup}
+
+% Like \dotfill except takes at least 1 em.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+
+\def\secondary #1#2{
+{\parfillskip=0in \parskip=0in
+\hangindent =1in \hangafter=1
+\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par
+}}
+
+%% Define two-column mode, which is used in indexes.
+%% Adapted from the TeXbook, page 416.
+\catcode `\@=11
+
+\newbox\partialpage
+
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup
+ % Grab any single-column material above us.
+ \output = {\global\setbox\partialpage
+ =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}%
+ \eject
+ %
+ % Now switch to the double-column output routine.
+ \output={\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it once.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +- <
+ % 1pt) as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Double the \vsize as well. (We don't need a separate register here,
+ % since nobody clobbers \vsize.)
+ \vsize = 2\vsize
+ \doublecolumnpagegoal
+}
+
+\def\enddoublecolumns{\eject \endgroup \pagegoal=\vsize \unvbox\partialpage}
+
+\def\doublecolumnsplit{\splittopskip=\topskip \splitmaxdepth=\maxdepth
+ \global\dimen@=\pageheight \global\advance\dimen@ by-\ht\partialpage
+ \global\setbox1=\vsplit255 to\dimen@ \global\setbox0=\vbox{\unvbox1}
+ \global\setbox3=\vsplit255 to\dimen@ \global\setbox2=\vbox{\unvbox3}
+ \ifdim\ht0>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
+ \ifdim\ht2>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
+}
+\def\doublecolumnpagegoal{%
+ \dimen@=\vsize \advance\dimen@ by-2\ht\partialpage \global\pagegoal=\dimen@
+}
+\def\pagesofar{\unvbox\partialpage %
+ \hsize=\doublecolumnhsize % have to restore this since output routine
+ \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
+\def\doublecolumnout{%
+ \setbox5=\copy255
+ {\vbadness=10000 \doublecolumnsplit}
+ \ifvbox255
+ \setbox0=\vtop to\dimen@{\unvbox0}
+ \setbox2=\vtop to\dimen@{\unvbox2}
+ \onepageout\pagesofar \unvbox255 \penalty\outputpenalty
+ \else
+ \setbox0=\vbox{\unvbox5}
+ \ifvbox0
+ \dimen@=\ht0 \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by2 \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ {\vbadness=10000
+ \loop \global\setbox5=\copy0
+ \setbox1=\vsplit5 to\dimen@
+ \setbox3=\vsplit5 to\dimen@
+ \ifvbox5 \global\advance\dimen@ by1pt \repeat
+ \setbox0=\vbox to\dimen@{\unvbox1}
+ \setbox2=\vbox to\dimen@{\unvbox3}
+ \global\setbox\partialpage=\vbox{\pagesofar}
+ \doublecolumnpagegoal
+ }
+ \fi
+ \fi
+}
+
+\catcode `\@=\other
+\message{sectioning,}
+% Define chapters, sections, etc.
+
+\newcount \chapno
+\newcount \secno \secno=0
+\newcount \subsecno \subsecno=0
+\newcount \subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount \appendixno \appendixno = `\@
+\def\appendixletter{\char\the\appendixno}
+
+\newwrite \contentsfile
+% This is called from \setfilename.
+\def\opencontents{\openout \contentsfile = \jobname.toc}
+
+% Each @chapter defines this as the name of the chapter.
+% page headings and footings can use it. @section does likewise
+
+\def\thischapter{} \def\thissection{}
+\def\seccheck#1{\if \pageno<0 %
+\errmessage{@#1 not allowed after generating table of contents}\fi
+%
+}
+
+\def\chapternofonts{%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\def\result{\realbackslash result}
+\def\equiv{\realbackslash equiv}
+\def\expansion{\realbackslash expansion}
+\def\print{\realbackslash print}
+\def\TeX{\realbackslash TeX}
+\def\dots{\realbackslash dots}
+\def\copyright{\realbackslash copyright}
+\def\tt{\realbackslash tt}
+\def\bf{\realbackslash bf }
+\def\w{\realbackslash w}
+\def\less{\realbackslash less}
+\def\gtr{\realbackslash gtr}
+\def\hat{\realbackslash hat}
+\def\char{\realbackslash char}
+\def\tclose##1{\realbackslash tclose {##1}}
+\def\code##1{\realbackslash code {##1}}
+\def\samp##1{\realbackslash samp {##1}}
+\def\r##1{\realbackslash r {##1}}
+\def\b##1{\realbackslash b {##1}}
+\def\key##1{\realbackslash key {##1}}
+\def\file##1{\realbackslash file {##1}}
+\def\kbd##1{\realbackslash kbd {##1}}
+% These are redefined because @smartitalic wouldn't work inside xdef.
+\def\i##1{\realbackslash i {##1}}
+\def\cite##1{\realbackslash cite {##1}}
+\def\var##1{\realbackslash var {##1}}
+\def\emph##1{\realbackslash emph {##1}}
+\def\dfn##1{\realbackslash dfn {##1}}
+}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raise/lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% Choose a numbered-heading macro
+% #1 is heading level if unmodified by @raisesections or @lowersections
+% #2 is text for heading
+\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \chapterzzz{#2}
+\or
+ \seczzz{#2}
+\or
+ \numberedsubseczzz{#2}
+\or
+ \numberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \chapterzzz{#2}
+ \else
+ \numberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses appendix heading levels
+\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \appendixzzz{#2}
+\or
+ \appendixsectionzzz{#2}
+\or
+ \appendixsubseczzz{#2}
+\or
+ \appendixsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \appendixzzz{#2}
+ \else
+ \appendixsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses numberless heading levels
+\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \unnumberedzzz{#2}
+\or
+ \unnumberedseczzz{#2}
+\or
+ \unnumberedsubseczzz{#2}
+\or
+ \unnumberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \unnumberedzzz{#2}
+ \else
+ \unnumberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+
+\def\thischaptername{No Chapter Title}
+\outer\def\chapter{\parsearg\chapteryyy}
+\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz #1{\seccheck{chapter}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \chapno by 1 \message{\putwordChapter \the\chapno}%
+\chapmacro {#1}{\the\chapno}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+% We don't substitute the actual chapter name into \thischapter
+% because we don't want its macros evaluated now.
+\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}%
+{\chapternofonts%
+\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+}}
+
+\outer\def\appendix{\parsearg\appendixyyy}
+\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz #1{\seccheck{appendix}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \appendixno by 1 \message{Appendix \appendixletter}%
+\chapmacro {#1}{\putwordAppendix{} \appendixletter}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}%
+{\chapternofonts%
+\edef\temp{{\realbackslash chapentry
+ {#1}{\putwordAppendix{} \appendixletter}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\global\let\section = \appendixsec
+\global\let\subsection = \appendixsubsec
+\global\let\subsubsection = \appendixsubsubsec
+}}
+
+\outer\def\top{\parsearg\unnumberedyyy}
+\outer\def\unnumbered{\parsearg\unnumberedyyy}
+\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz #1{\seccheck{unnumbered}%
+\secno=0 \subsecno=0 \subsubsecno=0
+%
+% This used to be simply \message{#1}, but TeX fully expands the
+% argument to \message. Therefore, if #1 contained @-commands, TeX
+% expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+% expanded @cite (which turns out to cause errors because \cite is meant
+% to be executed, not expanded).
+%
+% Anyway, we don't want the fully-expanded definition of @cite to appear
+% as a result of the \message, we just want `@cite' itself. We use
+% \the<toks register> to achieve this: TeX expands \the<toks> only once,
+% simply yielding the contents of the <toks register>.
+\toks0 = {#1}\message{(\the\toks0)}%
+%
+\unnumbchapmacro {#1}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\global\let\section = \unnumberedsec
+\global\let\subsection = \unnumberedsubsec
+\global\let\subsubsection = \unnumberedsubsubsec
+}}
+
+\outer\def\numberedsec{\parsearg\secyyy}
+\def\secyyy #1{\numhead1{#1}} % normally calls seczzz
+\def\seczzz #1{\seccheck{section}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash secentry %
+{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appenixsection{\parsearg\appendixsecyyy}
+\outer\def\appendixsec{\parsearg\appendixsecyyy}
+\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz #1{\seccheck{appendixsection}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash secentry %
+{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy}
+\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy}
+\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz #1{\seccheck{subsection}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy}
+\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy}
+\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy}
+\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz #1{\seccheck{subsubsection}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsubsecentry %
+ {#1}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}
+ {\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy}
+\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\edef\temp{{\realbackslash subsubsecentry{#1}%
+ {\appendixletter}
+ {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy}
+\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+% These are variants which are not "outer", so they can appear in @ifinfo.
+% Actually, they should now be obsolete; ordinary section commands should work.
+\def\infotop{\parsearg\unnumberedzzz}
+\def\infounnumbered{\parsearg\unnumberedzzz}
+\def\infounnumberedsec{\parsearg\unnumberedseczzz}
+\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+
+\def\infoappendix{\parsearg\appendixzzz}
+\def\infoappendixsec{\parsearg\appendixseczzz}
+\def\infoappendixsubsec{\parsearg\appendixsubseczzz}
+\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz}
+
+\def\infochapter{\parsearg\chapterzzz}
+\def\infosection{\parsearg\sectionzzz}
+\def\infosubsection{\parsearg\subsectionzzz}
+\def\infosubsubsection{\parsearg\subsubsectionzzz}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and
+% such:
+% 1) We use \vbox rather than the earlier \line to permit
+% overlong headings to fold.
+% 2) \hyphenpenalty is set to 10000 because hyphenation in a
+% heading is obnoxious; this forbids it.
+% 3) Likewise, headings look best if no \parindent is used, and
+% if justification is not attempted. Hence \raggedright.
+
+
+\def\majorheading{\parsearg\majorheadingzzz}
+\def\majorheadingzzz #1{%
+{\advance\chapheadingskip by 10pt \chapbreak }%
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\chapheading{\parsearg\chapheadingzzz}
+\def\chapheadingzzz #1{\chapbreak %
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\heading{\parsearg\secheadingi}
+
+\def\subheading{\parsearg\subsecheadingi}
+
+\def\subsubheading{\parsearg\subsubsecheadingi}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+\def\CHAPFplain{
+\global\let\chapmacro=\chfplain
+\global\let\unnumbchapmacro=\unnchfplain}
+
+\def\chfplain #1#2{%
+ \pchapsepmacro
+ {%
+ \chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #2\enspace #1}%
+ }%
+ \bigskip
+ \penalty5000
+}
+
+\def\unnchfplain #1{%
+\pchapsepmacro %
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+\CHAPFplain % The default
+
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+
+\def\CHAPFopen{
+\global\let\chapmacro=\chfopen
+\global\let\unnumbchapmacro=\unnchfopen}
+
+% Parameter controlling skip before section headings.
+
+\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt
+\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+
+\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt
+\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+
+% @paragraphindent is defined for the Info formatting commands only.
+\let\paragraphindent=\comment
+
+% Section fonts are the base font at magstep2, which produces
+% a size a bit more than 14 points in the default situation.
+
+\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
+\def\plainsecheading #1{\secheadingi {#1}}
+\def\secheadingi #1{{\advance \secheadingskip by \parskip %
+\secheadingbreak}%
+{\secfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+
+% Subsection fonts are the base font at magstep1,
+% which produces a size of 12 points.
+
+\def\subsecheading #1#2#3#4{\subsecheadingi {#2.#3.#4\enspace #1}}
+\def\subsecheadingi #1{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+\def\subsubsecfonts{\subsecfonts} % Maybe this should change:
+ % Perhaps make sssec fonts scaled
+ % magstep half
+\def\subsubsecheading #1#2#3#4#5{\subsubsecheadingi {#2.#3.#4.#5\enspace #1}}
+\def\subsubsecheadingi #1{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsubsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
+
+
+\message{toc printing,}
+
+% Finish up the main text and prepare to read what we've written
+% to \contentsfile.
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\def\startcontents#1{%
+ \pagealignmacro
+ \immediate\closeout \contentsfile
+ \ifnum \pageno>0
+ \pageno = -1 % Request roman numbered pages.
+ \fi
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \unnumbchapmacro{#1}\def\thischapter{}%
+ \begingroup % Set up to handle contents files properly.
+ \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11
+ \catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+}
+
+
+% Normal (long) toc.
+\outer\def\contents{%
+ \startcontents{\putwordTableofContents}%
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+
+% And just the chapters.
+\outer\def\summarycontents{%
+ \startcontents{\putwordShortContents}%
+ %
+ \let\chapentry = \shortchapentry
+ \let\unnumbchapentry = \shortunnumberedentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl
+ \rm
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\secentry ##1##2##3##4{}
+ \def\unnumbsecentry ##1##2{}
+ \def\subsecentry ##1##2##3##4##5{}
+ \def\unnumbsubsecentry ##1##2{}
+ \def\subsubsecentry ##1##2##3##4##5##6{}
+ \def\unnumbsubsubsecentry ##1##2{}
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+\let\shortcontents = \summarycontents
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapter-level things, for both the long and short contents.
+\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}}
+
+% See comments in \dochapentry re vbox and related settings
+\def\shortchapentry#1#2#3{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}%
+}
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter.
+% We could simplify the code here by writing out an \appendixentry
+% command in the toc file for appendices, instead of using \chapentry
+% for both, but it doesn't seem worth it.
+\setbox0 = \hbox{\shortcontrm \putwordAppendix }
+\newdimen\shortappendixwidth \shortappendixwidth = \wd0
+
+\def\shortchaplabel#1{%
+ % We typeset #1 in a box of constant width, regardless of the text of
+ % #1, so the chapter titles will come out aligned.
+ \setbox0 = \hbox{#1}%
+ \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi
+ %
+ % This space should be plenty, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in in \shortchapentry above.)
+ \advance\dimen0 by 1.1em
+ \hbox to \dimen0{#1\hfil}%
+}
+
+\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}}
+\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}}
+
+% Sections.
+\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}}
+\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}}
+
+% Subsections.
+\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}}
+\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}}
+
+% And subsubsections.
+\def\subsubsecentry#1#2#3#4#5#6{%
+ \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}}
+\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}}
+
+
+% This parameter controls the indentation of the various levels.
+\newdimen\tocindent \tocindent = 3pc
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we would want to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip\baselineskip
+ \begingroup
+ \chapentryfonts
+ \tocentry{#1}{\dopageno{#2}}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+% Final typesetting of a toc entry; we use the same \entry macro as for
+% the index entries, but we want to suppress hyphenation here. (We
+% can't do that in the \entry macro, since index entries might consist
+% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.)
+%
+\def\tocentry#1#2{\begingroup
+ \hyphenpenalty = 10000
+ \entry{#1}{#2}%
+\endgroup}
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\let\subsecentryfonts = \textfonts
+\let\subsubsecentryfonts = \textfonts
+
+
+\message{environments,}
+
+% Since these characters are used in examples, it should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+% Furthermore, these definitions must come after we define our fonts.
+\newbox\dblarrowbox \newbox\longdblarrowbox
+\newbox\pushcharbox \newbox\bullbox
+\newbox\equivbox \newbox\errorbox
+
+\let\ptexequiv = \equiv
+
+%{\tentt
+%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil}
+%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil}
+%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil}
+%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil}
+% Adapted from the manmac format (p.420 of TeXbook)
+%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex
+% depth .1ex\hfil}
+%}
+
+\def\point{$\star$}
+
+\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+
+\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% Adapted from the TeXbook's \boxit.
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt}
+
+\global\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+
+% The @error{} command.
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\def\tex{\begingroup
+\catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+\catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie
+\catcode `\%=14
+\catcode 43=12
+\catcode`\"=12
+\catcode`\==12
+\catcode`\|=12
+\catcode`\<=12
+\catcode`\>=12
+\escapechar=`\\
+%
+\let\~=\ptextilde
+\let\{=\ptexlbrace
+\let\}=\ptexrbrace
+\let\.=\ptexdot
+\let\*=\ptexstar
+\let\dots=\ptexdots
+\def\@{@}%
+\let\bullet=\ptexbullet
+\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl
+\let\L=\ptexL
+%
+\let\Etex=\endgroup}
+
+% Define @lisp ... @endlisp.
+% @lisp does a \begingroup so it can rebind things,
+% including the definition of @endlisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% Make each space character in the input produce a normal interword
+% space in the output. Don't allow a line break at this space, as this
+% is used only in environments like @example, where each line of input
+% should produce a line of output anyway.
+%
+{\obeyspaces %
+\gdef\sepspaces{\obeyspaces\let =\tie}}
+
+% Define \obeyedspace to be our active space, whatever it is. This is
+% for use in \parsearg.
+{\sepspaces%
+\global\let\obeyedspace= }
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip
+%
+\def\aboveenvbreak{{\advance\envskipamount by \parskip
+\endgraf \ifdim\lastskip<\envskipamount
+\removelastskip \penalty-50 \vskip\envskipamount \fi}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins.
+\let\nonarrowing=\relax
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \cartouche: draw rectangle w/rounded corners around argument
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\long\def\cartouche{%
+\begingroup
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt %we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18pt % allow for 3pt kerns on either
+% side, and for 6pt waste from
+% each corner char
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing=\comment
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \hsize=\cartinner
+ \kern3pt
+ \begingroup
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+\def\Ecartouche{%
+ \endgroup
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+\endgroup
+}}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+ \aboveenvbreak
+ \inENV % This group ends at the end of the body
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \singlespace
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ \parindent = 0pt
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ % @cartouche defines \nonarrowing to inhibit narrowing
+ % at next level down.
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \let\exdent=\nofillexdent
+ \let\nonarrowing=\relax
+ \fi
+}
+
+% To ending an @example-like environment, we first end the paragraph
+% (via \afterenvbreak's vertical glue), and then the group. That way we
+% keep the zero \parskip that the environments set -- \parskip glue
+% will be inserted at the beginning of the next paragraph in the
+% document, after the environment.
+%
+\def\nonfillfinish{\afterenvbreak\endgroup}%
+
+% This macro is
+\def\lisp{\begingroup
+ \nonfillstart
+ \let\Elisp = \nonfillfinish
+ \tt
+ \rawbackslash % have \ input char produce \ char from current font
+ \gobble
+}
+
+% Define the \E... control sequence only if we are inside the
+% environment, so the error checking in \end will work.
+%
+% We must call \lisp last in the definition, since it reads the
+% return following the @example (or whatever) command.
+%
+\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp}
+\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp}
+\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp}
+
+% @smallexample and @smalllisp. This is not used unless the @smallbook
+% command is given. Originally contributed by Pavel@xerox.
+%
+\def\smalllispx{\begingroup
+ \nonfillstart
+ \let\Esmalllisp = \nonfillfinish
+ \let\Esmallexample = \nonfillfinish
+ %
+ % Smaller interline space and fonts for small examples.
+ \setleading{10pt}%
+ \indexfonts \tt
+ \rawbackslash % make \ output the \ character from the current font (tt)
+ \gobble
+}
+
+% This is @display; same as @lisp except use roman font.
+%
+\def\display{\begingroup
+ \nonfillstart
+ \let\Edisplay = \nonfillfinish
+ \gobble
+}
+
+% This is @format; same as @display except don't narrow margins.
+%
+\def\format{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eformat = \nonfillfinish
+ \gobble
+}
+
+% @flushleft (same as @format) and @flushright.
+%
+\def\flushleft{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushleft = \nonfillfinish
+ \gobble
+}
+\def\flushright{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushright = \nonfillfinish
+ \advance\leftskip by 0pt plus 1fill
+ \gobble}
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins.
+%
+\def\quotation{%
+ \begingroup\inENV %This group ends at the end of the @quotation body
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \singlespace
+ \parindent=0pt
+ % We have retained a nonzero parskip for the environment, since we're
+ % doing normal filling. So to avoid extra space below the environment...
+ \def\Equotation{\parskip = 0pt \nonfillfinish}%
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \let\nonarrowing = \relax
+ \fi
+}
+
+\message{defuns,}
+% Define formatter for defuns
+% First, allow user to change definition object font (\df) internally
+\def\setdeffont #1 {\csname DEF#1\endcsname}
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deftypemargin \deftypemargin=12pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+
+\newcount\parencount
+% define \functionparens, which makes ( and ) and & do special things.
+% \functionparens affects the group it is contained in.
+\def\activeparens{%
+\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active
+\catcode`\[=\active \catcode`\]=\active}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+\global\let(=\lparen \global\let)=\rparen
+\global\let[=\lbrack \global\let]=\rbrack
+
+\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
+\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+
+% Definitions of (, ) and & used in args for functions.
+% This is the definition of ( outside of all parentheses.
+\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested %
+\global\advance\parencount by 1 }
+%
+% This is the definition of ( when already inside a level of parens.
+\gdef\opnested{\char`\(\global\advance\parencount by 1 }
+%
+\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0.
+% also in that case restore the outer-level definition of (.
+\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi
+\global\advance \parencount by -1 }
+% If we encounter &foo, then turn on ()-hacking afterwards
+\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ }
+%
+\gdef\normalparens{\boldbrax\let&=\ampnr}
+} % End of definition inside \activeparens
+%% These parens (in \boldbrax) actually are a little bolder than the
+%% contained text. This is especially needed for [ and ]
+\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&}
+\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}}
+
+% First, defname, which formats the header line itself.
+% #1 should be the function name.
+% #2 should be the type of definition, such as "Function".
+
+\def\defname #1#2{%
+% Get the values of \leftskip and \rightskip as they were
+% outside the @def...
+\dimen2=\leftskip
+\advance\dimen2 by -\defbodyindent
+\dimen3=\rightskip
+\advance\dimen3 by -\defbodyindent
+\noindent %
+\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}%
+\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line
+\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations
+\parshape 2 0in \dimen0 \defargsindent \dimen1 %
+% Now output arg 2 ("Function" or some such)
+% ending at \deftypemargin from the right margin,
+% but stuck inside a box of width 0 so it does not interfere with linebreaking
+{% Adjust \hsize to exclude the ambient margins,
+% so that \rightline will obey them.
+\advance \hsize by -\dimen2 \advance \hsize by -\dimen3
+\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}%
+% Make all lines underfull and no complaints:
+\tolerance=10000 \hbadness=10000
+\advance\leftskip by -\defbodyindent
+\exdentamount=\defbodyindent
+{\df #1}\enskip % Generate function name
+}
+
+% Actually process the body of a definition
+% #1 should be the terminating control sequence, such as \Edefun.
+% #2 should be the "another name" control sequence, such as \defunx.
+% #3 should be the control sequence that actually processes the header,
+% such as \defunheader.
+
+\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\activeparens\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active % 61 is `='
+\obeylines\activeparens\spacesplit#3}
+
+\def\defmethparsebody #1#2#3#4 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+
+\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#5}}}
+
+% These parsing functions are similar to the preceding ones
+% except that they do not make parens into active characters.
+% These are used for "variables" since they have no arguments.
+
+\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active %
+\obeylines\spacesplit#3}
+
+% This is used for \def{tp,vr}parsebody. It could probably be used for
+% some of the others, too, with some judicious conditionals.
+%
+\def\parsebodycommon#1#2#3{%
+ \begingroup\inENV %
+ \medbreak %
+ % Define the end token that this defining construct specifies
+ % so that it will exit this group.
+ \def#1{\endgraf\endgroup\medbreak}%
+ \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}%
+ \parindent=0in
+ \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+ \exdentamount=\defbodyindent
+ \begingroup\obeylines
+}
+
+\def\defvrparsebody#1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{#3{#4}}%
+}
+
+% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the
+% type is just `struct', because we lose the braces in `{struct
+% termios}' when \spacesplit reads its undelimited argument. Sigh.
+% \let\deftpparsebody=\defvrparsebody
+%
+% So, to get around this, we put \empty in with the type name. That
+% way, TeX won't find exactly `{...}' as an undelimited argument, and
+% won't strip off the braces.
+%
+\def\deftpparsebody #1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{\parsetpheaderline{#3{#4}}}\empty
+}
+
+% Fine, but then we have to eventually remove the \empty *and* the
+% braces (if any). That's what this does, putting the result in \tptemp.
+%
+\def\removeemptybraces\empty#1\relax{\def\tptemp{#1}}%
+
+% After \spacesplit has done its work, this is called -- #1 is the final
+% thing to call, #2 the type name (which starts with \empty), and #3
+% (which might be empty) the arguments.
+%
+\def\parsetpheaderline#1#2#3{%
+ \removeemptybraces#2\relax
+ #1{\tptemp}{#3}%
+}%
+
+\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\spacesplit{#3{#5}}}
+
+% Split up #2 at the first space token.
+% call #1 with two arguments:
+% the first is all of #2 before the space token,
+% the second is all of #2 after that space token.
+% If #2 contains no space token, all of it is passed as the first arg
+% and the second is passed as empty.
+
+{\obeylines
+\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}%
+\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{%
+\ifx\relax #3%
+#1{#2}{}\else #1{#2}{#3#4}\fi}}
+
+% So much for the things common to all kinds of definitions.
+
+% Define @defun.
+
+% First, define the processing that is wanted for arguments of \defun
+% Use this to expand the args and terminate the paragraph they make up
+
+\def\defunargs #1{\functionparens \sl
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+\hyphenchar\tensl=0
+#1%
+\hyphenchar\tensl=45
+\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+\def\deftypefunargs #1{%
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+\functionparens
+\tclose{#1}% avoid \code because of side effects on active chars
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+% Do complete processing of one @defun or @defunx line already parsed.
+
+% @deffn Command forward-char nchars
+
+\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+
+\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defun == @deffn Function
+
+\def\defun{\defparsebody\Edefun\defunx\defunheader}
+
+\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Function}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefun int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader}
+
+% #1 is the data type. #2 is the name and args.
+\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax}
+% #1 is the data type, #2 the name, #3 the args.
+\def\deftypefunheaderx #1#2 #3\relax{%
+\doind {fn}{\code{#2}}% Make entry in function index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}%
+\deftypefunargs {#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader}
+
+% \defheaderxcond#1\relax$$$
+% puts #1 in @code, followed by a space, but does nothing if #1 is null.
+\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi}
+
+% #1 is the classification. #2 is the data type. #3 is the name and args.
+\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax}
+% #1 is the classification, #2 the data type, #3 the name, #4 the args.
+\def\deftypefnheaderx #1#2#3 #4\relax{%
+\doind {fn}{\code{#3}}% Make entry in function index
+\begingroup
+\normalparens % notably, turn off `&' magic, which prevents
+% at least some C++ text from working
+\defname {\defheaderxcond#2\relax$$$#3}{#1}%
+\deftypefunargs {#4}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defmac == @deffn Macro
+
+\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+
+\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Macro}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defspec == @deffn Special Form
+
+\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+
+\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Special Form}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% This definition is run if you use @defunx
+% anywhere other than immediately after a @defun or @defunx.
+
+\def\deffnx #1 {\errmessage{@deffnx in invalid context}}
+\def\defunx #1 {\errmessage{@defunx in invalid context}}
+\def\defmacx #1 {\errmessage{@defmacx in invalid context}}
+\def\defspecx #1 {\errmessage{@defspecx in invalid context}}
+\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}}
+\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}}
+
+% @defmethod, and so on
+
+% @defop {Funny Method} foo-class frobnicate argument
+
+\def\defop #1 {\def\defoptype{#1}%
+\defopparsebody\Edefop\defopx\defopheader\defoptype}
+
+\def\defopheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index
+\begingroup\defname {#2}{\defoptype{} on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defmethod == @defop Method
+
+\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+
+\def\defmethodheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% entry in function index
+\begingroup\defname {#2}{Method on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defcv {Class Option} foo-class foo-flag
+
+\def\defcv #1 {\def\defcvtype{#1}%
+\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype}
+
+\def\defcvarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{\defcvtype{} of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% @defivar == @defcv {Instance Variable}
+
+\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader}
+
+\def\defivarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{Instance Variable of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% These definitions are run if you use @defmethodx, etc.,
+% anywhere other than immediately after a @defmethod, etc.
+
+\def\defopx #1 {\errmessage{@defopx in invalid context}}
+\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}}
+\def\defcvx #1 {\errmessage{@defcvx in invalid context}}
+\def\defivarx #1 {\errmessage{@defivarx in invalid context}}
+
+% Now @defvar
+
+% First, define the processing that is wanted for arguments of @defvar.
+% This is actually simple: just print them in roman.
+% This must expand the args and terminate the paragraph they make up
+\def\defvarargs #1{\normalparens #1%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000}
+
+% @defvr Counter foo-count
+
+\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader}
+
+\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+
+% @defvar == @defvr Variable
+
+\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader}
+
+\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{Variable}%
+\defvarargs {#2}\endgroup %
+}
+
+% @defopt == @defvr {User Option}
+
+\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader}
+
+\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{User Option}%
+\defvarargs {#2}\endgroup %
+}
+
+% @deftypevar int foobar
+
+\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader}
+
+% #1 is the data type. #2 is the name.
+\def\deftypevarheader #1#2{%
+\doind {vr}{\code{#2}}% Make entry in variables index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+
+% @deftypevr {Global Flag} int enable
+
+\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader}
+
+\def\deftypevrheader #1#2#3{\doind {vr}{\code{#3}}%
+\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1}
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+
+% This definition is run if you use @defvarx
+% anywhere other than immediately after a @defvar or @defvarx.
+
+\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
+\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
+\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
+\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}}
+\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}}
+
+% Now define @deftp
+% Args are printed in bold, a slight difference from @defvar.
+
+\def\deftpargs #1{\bf \defvarargs{#1}}
+
+% @deftp Class window height width ...
+
+\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader}
+
+\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
+\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+
+% This definition is run if you use @deftpx, etc
+% anywhere other than immediately after a @deftp, etc.
+
+\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+
+\message{cross reference,}
+% Define cross-reference macros
+\newwrite \auxfile
+
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% \setref{foo} defines a cross-reference point named foo.
+
+\def\setref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ysectionnumberandtype}}
+
+\def\unnumbsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ynothing}}
+
+\def\appendixsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Yappendixletterandtype}}
+
+% \xref, \pxref, and \ref generate cross-references to specified points.
+% For \xrefX, #1 is the node name, #2 the name of the Info
+% cross-reference, #3 the printed node name, #4 the name of the Info
+% file, #5 the name of the printed manual. All but the node name can be
+% omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printednodename{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual}%
+ \setbox0=\hbox{\printednodename}%
+ \ifdim \wd0 = 0pt
+ % No printed node name was explicitly given.
+ \ifx\SETxref-automatic-section-title\relax %
+ % Use the actual chapter/section title appear inside
+ % the square brackets. Use the real section title if we have it.
+ \ifdim \wd1>0pt%
+ % It is in another manual, so we don't have it.
+ \def\printednodename{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printednodename{\refx{#1-title}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printednodename{\ignorespaces #1}%
+ \fi%
+ \fi
+ \def\printednodename{#1-title}%
+ \else
+ % Use the node name inside the square brackets.
+ \def\printednodename{\ignorespaces #1}%
+ \fi
+ \fi
+ %
+ % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+ % insert empty discretionaries after hyphens, which means that it will
+ % not find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens, this
+ % is a loss. Therefore, we give the text of the node name again, so it
+ % is as if TeX is seeing it for the first time.
+ \ifdim \wd1 > 0pt
+ \putwordsection{} ``\printednodename'' in \cite{\printedmanual}%
+ \else
+ % _ (for example) has to be the character _ for the purposes of the
+ % control sequence corresponding to the node, but it has to expand
+ % into the usual \leavevmode...\vrule stuff for purposes of
+ % printing. So we \turnoffactive for the \refx-snt, back on for the
+ % printing, back off for the \refx-pg.
+ {\turnoffactive \refx{#1-snt}{}}%
+ \space [\printednodename],\space
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+\endgroup}
+
+% \dosetq is the interface for calls from other macros
+
+% Use \turnoffactive so that punctuation chars such as underscore
+% work in node names.
+\def\dosetq #1#2{{\let\folio=0 \turnoffactive \auxhat%
+\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}%
+\next}}
+
+% \internalsetq {foo}{page} expands into
+% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...}
+% When the aux file is read, ' is the escape character
+
+\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
+
+% Things to be expanded by \internalsetq
+
+\def\Ypagenumber{\folio}
+
+\def\Ytitle{\thissection}
+
+\def\Ynothing{}
+
+\def\Ysectionnumberandtype{%
+\ifnum\secno=0 \putwordChapter\xreftie\the\chapno %
+\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\def\Yappendixletterandtype{%
+\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}%
+\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\gdef\xreftie{'tie}
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Non-3.0.
+\else
+ \def\linenumber{\the\inputlineno:\space}
+\fi
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+
+\def\refx#1#2{%
+ \expandafter\ifx\csname X#1\endcsname\relax
+ % If not defined, say something at least.
+ $\langle$un\-de\-fined$\rangle$%
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \csname X#1\endcsname
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% Read the last existing aux file, if any. No error if none exists.
+
+% This is the macro invoked by entries in the aux file.
+\def\xrdef #1#2{
+{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}}
+
+\def\readauxfile{%
+\begingroup
+\catcode `\^^@=\other
+\catcode `\\ 1=\other
+\catcode `\\ 2=\other
+\catcode `\^^C=\other
+\catcode `\^^D=\other
+\catcode `\^^E=\other
+\catcode `\^^F=\other
+\catcode `\^^G=\other
+\catcode `\^^H=\other
+\catcode `\\v=\other
+\catcode `\^^L=\other
+\catcode `\\ e=\other
+\catcode `\\ f=\other
+\catcode `\\10=\other
+\catcode `\\11=\other
+\catcode `\\12=\other
+\catcode `\\13=\other
+\catcode `\\14=\other
+\catcode `\\15=\other
+\catcode `\\16=\other
+\catcode `\\17=\other
+\catcode `\\18=\other
+\catcode `\\19=\other
+\catcode 26=\other
+\catcode `\^^[=\other
+\catcode `\^^\=\other
+\catcode `\^^]=\other
+\catcode `\^^^=\other
+\catcode `\^^_=\other
+\catcode `\@=\other
+\catcode `\^=\other
+\catcode `\~=\other
+\catcode `\[=\other
+\catcode `\]=\other
+\catcode`\"=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode `\$=\other
+\catcode `\#=\other
+\catcode `\&=\other
+% `\+ does not work, so use 43.
+\catcode 43=\other
+% Make the characters 128-255 be printing characters
+{%
+ \count 1=128
+ \def\loop{%
+ \catcode\count 1=\other
+ \advance\count 1 by 1
+ \ifnum \count 1<256 \loop \fi
+ }%
+}%
+% the aux file uses ' as the escape.
+% Turn off \ as an escape so we do not lose on
+% entries which were dumped with control sequences in their names.
+% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^
+% Reference to such entries still does not work the way one would wish,
+% but at least they do not bomb out when the aux file is read in.
+\catcode `\{=1 \catcode `\}=2
+\catcode `\%=\other
+\catcode `\'=0
+\catcode`\^=7 % to make ^^e4 etc usable in xref tags
+\catcode `\\=\other
+\openin 1 \jobname.aux
+\ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue
+\global\warnedobstrue
+\fi
+% Open the new aux file. Tex will close it automatically at exit.
+\openout \auxfile=\jobname.aux
+\endgroup}
+
+
+% Footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed.
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only..
+\let\footnotestyle=\comment
+
+\let\ptexfootnote=\footnote
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \footnotezzz
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+\long\gdef\footnotezzz#1{\insert\footins{%
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ % Hang the footnote text off the number.
+ \hang
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ #1\strut}%
+}
+
+}%end \catcode `\@=11
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+\def\setleading#1{%
+ \normalbaselineskip = #1\relax
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% @| inserts a changebar to the left of the current line. It should
+% surround any changed text. This approach does *not* work if the
+% change spans more than two lines of output. To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+ % \vadjust can only be used in horizontal mode.
+ \leavevmode
+ %
+ % Append this vertical mode material after the current line in the output.
+ \vadjust{%
+ % We want to insert a rule with the height and depth of the current
+ % leading; that is exactly what \strutbox is supposed to record.
+ \vskip-\baselineskip
+ %
+ % \vadjust-items are inserted at the left edge of the type. So
+ % the \llap here moves out into the left-hand margin.
+ \llap{%
+ %
+ % For a thicker or thinner bar, change the `1pt'.
+ \vrule height\baselineskip width1pt
+ %
+ % This is the space between the bar and the text.
+ \hskip 12pt
+ }%
+ }%
+}
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+
+% End of control word definitions.
+
+\message{and turning on texinfo input format.}
+
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% Set some numeric style parameters, for 8.5 x 11 format.
+
+%\hsize = 6.5in
+\newdimen\defaultparindent \defaultparindent = 15pt
+\parindent = \defaultparindent
+\parskip 18pt plus 1pt
+\setleading{15pt}
+\advance\topskip by 1.2cm
+
+% Prevent underfull vbox error messages.
+\vbadness=10000
+
+% Following George Bush, just get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. This makes it come to about 9pt for the 8.5x11 format.
+%
+\ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+\else
+ \emergencystretch = \hsize
+ \divide\emergencystretch by 45
+\fi
+
+% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25)
+\def\smallbook{
+
+% These values for secheadingskip and subsecheadingskip are
+% experiments. RJC 7 Aug 1992
+\global\secheadingskip = 17pt plus 6pt minus 3pt
+\global\subsecheadingskip = 14pt plus 6pt minus 3pt
+
+\global\lispnarrowing = 0.3in
+\setleading{12pt}
+\advance\topskip by -1cm
+\global\parskip 3pt plus 1pt
+\global\hsize = 5in
+\global\vsize=7.5in
+\global\tolerance=700
+\global\hfuzz=1pt
+\global\contentsrightmargin=0pt
+\global\deftypemargin=0pt
+\global\defbodyindent=.5cm
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+
+\global\let\smalllisp=\smalllispx
+\global\let\smallexample=\smalllispx
+\global\def\Esmallexample{\Esmalllisp}
+}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{
+\global\tolerance=700
+\global\hfuzz=1pt
+\setleading{12pt}
+\global\parskip 15pt plus 1pt
+
+\global\vsize= 53\baselineskip
+\advance\vsize by \topskip
+%\global\hsize= 5.85in % A4 wide 10pt
+\global\hsize= 6.5in
+\global\outerhsize=\hsize
+\global\advance\outerhsize by 0.5in
+\global\outervsize=\vsize
+\global\advance\outervsize by 0.6in
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+}
+
+% Allow control of the text dimensions. Parameters in order: textheight;
+% textwidth; \voffset; \hoffset (!); binding offset. All require a dimension;
+% header is additional; added length extends the bottom of the page.
+
+\def\changepagesizes#1#2#3#4#5{
+ \global\vsize= #1
+ \advance\vsize by \topskip
+ \global\voffset= #3
+ \global\hsize= #2
+ \global\outerhsize=\hsize
+ \global\advance\outerhsize by 0.5in
+ \global\outervsize=\vsize
+ \global\advance\outervsize by 0.6in
+ \global\pagewidth=\hsize
+ \global\pageheight=\vsize
+ \global\normaloffset= #4
+ \global\bindingoffset= #5}
+
+% This layout is compatible with Latex on A4 paper.
+
+\def\afourlatex{\changepagesizes{22cm}{15cm}{7mm}{4.6mm}{5mm}}
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+
+% This macro is used to make a character print one way in ttfont
+% where it can probably just be output, and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt \char '042}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt \char '176}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def\auxhat{\def^{'hat}}
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+% Subroutine for the previous macro.
+\def\_{\lvvmode \kern.06em \vbox{\hrule width.3em height.1ex}}
+
+% \lvvmode is equivalent in function to \leavevmode.
+% Using \leavevmode runs into trouble when written out to
+% an index file due to the expansion of \leavevmode into ``\unhbox
+% \voidb@x'' ---which looks to TeX like ``\unhbox \voidb\x'' due to our
+% magic tricks with @.
+\def\lvvmode{\vbox to 0pt{}}
+
+\catcode`\|=\active
+\def|{{\tt \char '174}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+%\catcode 27=\active
+%\def^^[{$\diamondsuit$}
+
+% Set up an active definition for =, but don't enable it most of the time.
+{\catcode`\==\active
+\global\def={{\tt \char 61}}}
+
+\catcode`\@=0
+
+% \rawbackslashxx output one backslash character in current font
+\global\chardef\rawbackslashxx=`\\
+%{\catcode`\\=\other
+%@gdef@rawbackslashxx{\}}
+
+% \rawbackslash redefines \ as input to do \rawbackslashxx.
+{\catcode`\\=\active
+@gdef@rawbackslash{@let\=@rawbackslashxx }}
+
+% \normalbackslash outputs one backslash in fixed width font.
+\def\normalbackslash{{\tt\rawbackslashxx}}
+
+% Say @foo, not \foo, in error messages.
+\escapechar=`\@
+
+% \catcode 17=0 % Define control-q
+\catcode`\\=\active
+
+% Used sometimes to turn off (effectively) the active characters
+% even after parsing them.
+@def@turnoffactive{@let"=@normaldoublequote
+@let\=@realbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+@def@normalturnoffactive{@let"=@normaldoublequote
+@let\=@normalbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\{ in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+%
+@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi}
+
+%% These look ok in all fonts, so just make them not special. The @rm below
+%% makes sure that the current font starts out as the newly loaded cmr10
+@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other
+
+@textfonts
+@rm
+
+@c Local variables:
+@c page-delimiter: "^\\\\message"
+@c End:
+Tue Apr 9 20:56:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * README: Mention CRLF for src/server.c.
+
+Mon Mar 25 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * filesubr.c (expand_wild): New function.
+
+Tue Mar 19 17:55:39 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * startserver.c (wnt_start_server): Cleaned up code to get port
+ (and thus made consistent with VMS and Mac)
+
+Mon Mar 18 14:54:50 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * config.h: Don't declare gethostname; main.c already includes
+ winsock.h. Define FILENAMES_CASE_INSENSITIVE.
+
+Fri Mar 15 1996 Jim Kingdon <kingdon@cyclic.com>
+
+ * filesubr.c (fncmp): Fix typo (n1 -> n2) which had caused the
+ function to always return 0.
+
+Thu Mar 7 08:55:39 1996 Jim Blandy <jimb@totoro.cyclic.com>
+
+ * Makefile.in (HEADERS): Remove alloca.h from list.
+
+Wed Feb 28 11:08:06 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * options.h: Remove AUTH_SERVER_SUPPORT; no longer should be
+ defined in options.h.
+
+ * config.h: Remove C_ALLOCA, CRAY_STACKSEG_END, HAVE_ALLOCA,
+ HAVE_ALLOCA_H, and STACK_DIRECTION to reflect alloca removal.
+ * startserver.c (wnt_start_server): Don't use alloca.
+ * alloca.h: Removed.
+
Fri Feb 23 18:00:00 1996 Jim Kingdon <kingdon@cyclic.com>
* options.h: Define AUTH_CLIENT_SUPPORT and NO_SOCKET_TO_FD.
HEADERS = \
- alloca.h \
config.h \
ndir.h \
options.h \
To compile, use Microsoft Visual C++ on the file cvsnt.mak in the
distribution's top directory. At least with the tar port I'm using,
the sources get extracted without carriage returns and you must add
-carriage returns to the end of every line in cvsnt.mak. It doesn't
-seem to be necessary to add them to any other file. This makefile
-was generated with Visual C++ 2.1. As far as is known, it should work
-with Visual C++ 4,0 also.
+carriage returns to the end of every line in cvsnt.mak. I also had to
+add them to src/server.c. It doesn't seem to be necessary to add them
+to any other file. This makefile was generated with Visual C++ 2.1.
+As far as is known, it should work with Visual C++ 4,0 also.
Send bug reports to bug-cvs@prep.ai.mit.edu.
We just want to avoid a redefinition error message. */
#undef _ALL_SOURCE
-/* Define if using alloca.c. */
-#undef C_ALLOCA
-
/* Define if type char is unsigned and you are not using gcc. */
/* We wrote a little test program whose output suggests that char is
signed on this system. Go back and check the verdict when CVS
/* Const is working. */
#undef const
-/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
- This function is required for alloca.c support on those systems. */
-/* This shouldn't matter, but pro forma: */
-#undef CRAY_STACKSEG_END
-
/* Define to `int' if <sys/types.h> doesn't define. */
/* Windows NT doesn't have gid_t. It doesn't even really have group
numbers, I think. This will take more thought to get right, but
let's get it running first. */
#define gid_t int
-/* Define if you have alloca, as a function or macro. */
-/* Windows NT has alloca... */
-#define HAVE_ALLOCA 1
-
-/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
-/* but calls it _alloca and says it returns void *. We provide our
- own header file. */
-#define HAVE_ALLOCA_H 1
-
/* Define if you support file names longer than 14 characters. */
/* Yes. Woo. */
#define HAVE_LONG_FILE_NAMES 1
#includes, so things should be okay. */
/* #undef size_t */
-/* If using the C implementation of alloca, define if you know the
- direction of stack growth for your system; otherwise it will be
- automatically deduced at run-time.
- STACK_DIRECTION > 0 => grows toward higher addresses
- STACK_DIRECTION < 0 => grows toward lower addresses
- STACK_DIRECTION = 0 => direction of growth unknown
- */
-/* This shouldn't matter, but pro forma: */
-#undef STACK_DIRECTION
-
/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
/* We don't seem to have them at all; let ../lib/system.h define them. */
#define STAT_MACROS_BROKEN 1
/* This is just a call to the Win32 Sleep function. */
unsigned sleep (unsigned);
-/* This is in the winsock library. */
-int __stdcall gethostname(char *name, int namelen);
-
/* Don't worry, Microsoft, it's okay for these functions to
be in our namespace. */
#define popen _popen
are path component separators. */
#define FOLD_FN_CHAR(c) (WNT_filename_classes[(unsigned char) (c)])
extern unsigned char WNT_filename_classes[];
+#define FILENAMES_CASE_INSENSITIVE 1
/* Is the character C a path name separator? Under
Windows NT, you can use either / or \. */
file system semantics. */
#include <io.h>
+#include <windows.h>
#include "cvs.h"
== WNT_filename_classes[(unsigned char) *n2]))
n1++, n2++;
return (WNT_filename_classes[(unsigned char) *n1]
- - WNT_filename_classes[(unsigned char) *n1]);
+ - WNT_filename_classes[(unsigned char) *n2]);
}
/* Fold characters in FILENAME to their canonical forms.
{
return getenv ("HOMEPATH");
}
+
+/* See cvs.h for description. */
+void
+expand_wild (argc, argv, pargc, pargv)
+ int argc;
+ char **argv;
+ int *pargc;
+ char ***pargv;
+{
+ int i;
+ int new_argc;
+ char **new_argv;
+ /* Allocated size of new_argv. We arrange it so there is always room for
+ one more element. */
+ int max_new_argc;
+
+ new_argc = 0;
+ /* Add one so this is never zero. */
+ max_new_argc = argc + 1;
+ new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
+ for (i = 0; i < argc; ++i)
+ {
+ HANDLE h;
+ WIN32_FIND_DATA fdata;
+
+ h = FindFirstFile (argv[i], &fdata);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError () == ENOENT)
+ {
+ /* No match. The file specified didn't contain a wildcard (in which case
+ we clearly should return it unchanged), or it contained a wildcard which
+ didn't match (in which case it might be better for it to be an error,
+ but we don't try to do that). */
+ new_argv [new_argc++] = xstrdup (argv[i]);
+ if (new_argc == max_new_argc)
+ {
+ max_new_argc *= 2;
+ new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
+ }
+ }
+ else
+ {
+ error (1, errno, "cannot find %s", argv[i]);
+ }
+ }
+ else
+ {
+ while (1)
+ {
+ new_argv [new_argc++] = xstrdup (fdata.cFileName);
+ if (new_argc == max_new_argc)
+ {
+ max_new_argc *= 2;
+ new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
+ }
+ if (!FindNextFile (h, &fdata))
+ {
+ if (GetLastError () == ERROR_NO_MORE_FILES)
+ break;
+ else
+ error (1, errno, "cannot find %s", argv[i]);
+ }
+ }
+ if (!FindClose (h))
+ error (1, GetLastError (), "cannot close %s", argv[i]);
+ }
+ }
+ *pargc = new_argc;
+ *pargv = new_argv;
+}
* unless the user overrides the default with the RCSBIN environment variable
* or the "-b" option to CVS.
*
- * If you're compiling the authenticating server (see
- * AUTH_SERVER_SUPPORT farther down), then you probably want to set
- * RCSBIN_DFLT. The authenticating server starts out running as root,
- * and then switches to run as the appropriate user once
- * authentication is complete. No shell is ever started by that user,
- * so the PATH environment variable may not contain the directory with
- * the RCS binaries, even though if that user logged in normally, PATH
- * would include the directory. An alternative to setting RCSBIN_DFLT
- * is to make sure that root has the right directory in its path
- * already.
+ * If you use the password-authenticating server, then you need to
+ * make sure that the server can find the RCS programs to invoke them.
+ * The authenticating server starts out running as root, and then
+ * switches to run as the appropriate user once authentication is
+ * complete. But no actual shell is ever started by that user, so the
+ * PATH environment variable may not contain the directory with the
+ * RCS binaries, even though if that user logged in normally, PATH
+ * would include the directory.
+ *
+ * One way to solve this problem is to set RCSBIN_DFLT here. An
+ * alternative is to make sure that root has the right directory in
+ * its path already. Another, probably better alternative is to
+ * specify -b in /etc/inetd.conf.
*
* This define should be either the empty string ("") or a full pathname to the
* directory containing all the installed programs from the RCS distribution.
*/
#define AUTH_CLIENT_SUPPORT 1
-/* Authenticated server doesn't work yet on NT. */
-/* #define AUTH_SERVER_SUPPORT 1 */
-
/*
* This tells the client that it must use send()/recv() to talk to the
* server if it is connected to the server via a socket; Win95 is said to
{
char *cvs_server;
char *command;
- struct servent *s;
+ struct servent *sptr;
unsigned short port;
int read_fd, write_fd;
-
+ char *portenv;
+
if (! (cvs_server = getenv ("CVS_SERVER")))
cvs_server = "cvs";
- command = alloca (strlen (cvs_server)
- + strlen (server_cvsroot)
- + 50);
+ command = xmalloc (strlen (cvs_server)
+ + strlen (server_cvsroot)
+ + 50);
sprintf (command, "%s -d %s server", cvs_server, server_cvsroot);
- if ((s = getservbyname("shell", "tcp")) == NULL)
- port = IPPORT_CMDSERVER;
+ portenv = getenv("CVS_RCMD_PORT");
+ if (portenv)
+ port = atoi(portenv);
+ else if ((sptr = getservbyname("shell", "tcp")) != NULL)
+ port = sptr->s_port;
else
- port = s->s_port;
+ port = IPPORT_CMDSERVER; /* shell/tcp */
read_fd = rcmd (&server_host,
port,
*tofd = write_fd;
*fromfd = read_fd;
+ free (command);
}