v24i001: RCS source control system, Part01/12
Rich Salz
rsalz at uunet.uu.net
Fri Feb 22 06:58:40 AEST 1991
Submitted-by: Adam Hammer <hammer at cs.purdue.edu>
Posting-number: Volume 24, Issue 1
Archive-name: rcs/part01
RCS is a revision control system that keeps audit trails, edit histories,
and so on. See the README file for more extensive details. GNU diff,
useful for RCS, will follow this posting.
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents: PACKNOTES README MANIFEST Makefile man src src/ci.c
# Wrapped by rsalz at litchi.bbn.com on Thu Feb 21 14:36:53 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 1 (of 12)."'
if test -f 'PACKNOTES' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'PACKNOTES'\"
else
echo shar: Extracting \"'PACKNOTES'\" \(78 characters\)
sed "s/^X//" >'PACKNOTES' <<'END_OF_FILE'
X# "rcs.ms" was split into 2 parts; to create it, do
X cat rcs.ms.[1-9] >rcs.ms
END_OF_FILE
if test 78 -ne `wc -c <'PACKNOTES'`; then
echo shar: \"'PACKNOTES'\" unpacked with wrong size!
fi
# end of 'PACKNOTES'
fi
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(11703 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
X Copyright 1990 by Paul Eggert
X Distributed under license by the Free Software Foundation, Inc.
X
XThis file is part of RCS.
X
XRCS is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XRCS is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with RCS; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
XReport problems and direct all questions to:
X
X rcs-bugs at cs.purdue.edu
X
X*/
X
X$Id: README,v 5.6 1990/12/13 06:54:04 eggert Exp $
X
XThis directory contains complete sources for RCS version 5.5.
X
X
XInstallation notes:
X
X RCS requires a diff that supports the -n option.
X Get GNU diff (version 1.15 or later) if your diff lacks -n.
X
X RCS works best with a diff that supports -a, -L, and (diff3 only) -m.
X GNU diff supports these options.
X
X Sources for RCS are in the src directory.
X Read the directions in src/Makefile to set up the options
X for building RCS on your system.
X If `make' fails to build src/conf.h, look in src/conf.error
X to see what went wrong in the src/conf.sh shell file.
X If all else fails, create src/conf.h manually by editing a
X copy of src/conf.heg.
X
X Manual entries reside in man.
X
X To test your installation of RCS, run the shell file src/rcstest.
X
X Troff source for the paper `RCS--A System for Version Control', which
X appeared in _Software--Practice & Experience_, is in rcs.ms.
X
X
XRCS compatibility notes:
X
X RCS version 5 reads RCS files written by any RCS version released since 1982.
X It also writes RCS files that these older versions of RCS can read,
X unless you use one of the following new features:
X
X checkin times after 1999/12/31 23:59:59 GMT
X checking in non-text files
X non-Ascii symbolic names
X rcs -bX, where X is nonempty
X rcs -kX, where X is not `kv'
X RCS files that exceed hardcoded limits in older RCS versions
X
X
XFeatures new to RCS version 5 include:
X
X RCS can check in arbitrary files, not just text files, if diff -a works.
X RCS can merge lines containing just a single `.' if diff3 -m works.
X GNU diff supports the -a and -m options.
X
X RCS can now be installed as a setgid or setuid program
X if the setegid() and seteuid() system calls work.
X Setid privileges yield extra security if RCS files are protected so that
X only the effective group or user can write RCS directories.
X RCS uses the real group and user for all accesses other than to RCS files.
X On older hosts lacking setegid() and seteuid(), RCS uses the effective group
X and user for all accesses; formerly it was inconsistent.
X
X New options to co, rcsdiff, and rcsmerge give more flexibility to keyword
X substitution.
X
X -kkv substitutes the default `$Keyword: value $' for keyword strings.
X However, a locker's name is inserted only as a file is being locked,
X i.e. by `ci -l' and `co -l'. This is normally the default.
X
X -kkvl acts like -kkv, except that a locker's name is always inserted
X if the given revision is currently locked. This was the default in
X version 4. It is now the default only with when using rcsdiff to
X compare a revision to a working file whose mode is that of a file
X checked out for changes.
X
X -kk substitutes just `$Keyword$', which helps to ignore keyword values
X when comparing revisions.
X
X -ko retrieves the old revision's keyword string, thus bypassing keyword
X substitution.
X
X -kv retrieves just `value'. This can ease the use of keyword values, but
X it is dangerous because it causes RCS to lose track of where the keywords
X are, so for safety the owner write permission of the working file is
X turned off when -kv is used; to edit the file later, check it out again
X without -kv.
X
X rcs -ko sets the default keyword substitution to be in the style of co -ko,
X and similarly for the other -k options. This can be useful with binary file
X formats that cannot tolerate changing the lengths of keyword strings.
X However it also renders a RCS file readable only by RCS version 5 or later.
X Use rcs -kkv to restore the usual default substitution.
X
X RCS can now be used by development groups that span timezone boundaries.
X All times are now displayed in GMT, and GMT is the default timezone.
X To use local time with co -d, append ` LT' to the time.
X When interchanging RCS files with sites running older versions of RCS,
X users may encounter discrepancies of up to 13 hours in old time stamps.
X The list of timezone names has been modernized.
X
X Dates are now displayed using four-digit years, not two-digit years.
X Years given in -d options must now have four digits.
X This change is required for RCS to continue to work after 1999/12/31.
X The form of dates in version 5 RCS files will not change until 2000/01/01,
X so in the meantime RCS files can still be interchanged with sites
X running older versions of RCS. To make room for the longer dates,
X rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'.
X
X To help prevent diff programs that are broken or have run out of memory
X from trashing an RCS file, ci now checks diff output more carefully.
X
X ci -k now handles the Log keyword, so that checking in a file
X with -k does not normally alter the file's contents.
X
X RCS no longer outputs white space at the ends of lines
X unless the original working file had it.
X For consistency with other keywords,
X a space, not a tab, is now output after `$Log:'.
X Rlog now puts lockers and symbolic names on separate lines in the output
X to avoid generating lines that are too long.
X A similar fix has been made to lists in the RCS files themselves.
X
X RCS no longer outputs the string `Locker: ' when expanding Header or Id
X keywords. This saves space and reverts back to version 3 behavior.
X
X The default branch is not put into the RCS file unless it is nonempty.
X Therefore, files generated by RCS version 5 can be read by RCS version 3
X unless they use the default branch feature introduced in version 4.
X This fixes a compatibility problem introduced by version 4.
X
X RCS can now emulate older versions of RCS; see `co -V'.
X This may be useful to overcome compatibility problems
X due to the above changes.
X
X Programs like Emacs can now interact with RCS commands via a pipe:
X the new -I option causes ci, co, and rcs to run interactively,
X even if standard input is not a terminal.
X These commands now accept multiple inputs from stdin separated by `.' lines.
X
X ci now silently ignores the -t option if the RCS file already exists.
X This simplifies some shell scripts and improves security in setuid sites.
X
X Descriptive text may be given directly in an argument of the form -t-string.
X
X The character set for symbolic names has been upgraded
X from Ascii to ISO 8859.
X
X rcsdiff now passes through all options used by GNU diff;
X this is a longer list than 4.3BSD diff.
X
X merge's new -L option gives tags for merge's overlap report lines.
X This ability used to be present in a different, undocumented form;
X the new form is chosen for compatibility with GNU diff3's -L option.
X
X rcsmerge and merge now have a -q option, just like their siblings do.
X
X RCS now attempts to ignore parts of an RCS file that look like they come
X from a future version of RCS.
X
X When properly configured, RCS now strictly conforms with Posix 1003.1-1988.
X Normally, RCS file names contain `,', which is outside the Posix portable
X filename character set; but in impoverished Posix environments, you can
X compile RCS so that the RCS file for Foo is named just RCS/Foo.
X RCS can still be compiled in non-Posix traditional Unix environments,
X and can use common BSD and USG extensions to Posix.
X RCS is a conforming ANSI C program, and also compiles under traditional C.
X
X Arbitrary limits on internal table sizes have been removed.
X The only limit now is the amount of memory available via malloc().
X
X File temporaries, lock files, signals, and system call return codes
X are now handled more cleanly, portably, and quickly.
X Some race conditions have been removed.
X
X A new compile-time option RCSPREFIX lets administrators avoid absolute path
X names for subsidiary programs, trading speed for flexibility.
X
X The configuration procedure is now more automatic.
X
X Snooping has been removed; it did not work in version 4.
X
X
XVersion 4 was the first version distributed by FSF.
XBeside bug fixes, features new to RCS version 4 include:
X
X The notion of default branch has been added; see rcs -b.
X
X
XVersion 3 was included in the 4.3BSD distribution.
X
X
XFurther projects:
X
X Improve performance when checking out branch revisions;
X see the `piece table' comments in rcs.ms.
X Joe Berkovitz of Stratus has written some fast revision extraction code;
X unfortunately there wasn't enough time to integrate it into RCS version 5.
X It's probably best to use mmap() here if available.
X
X Let the user mark an RCS revision as deleted; checking out such a revision
X would result in no working file. Similarly, using `co -d' with a date either
X before the initial revision or after the file was marked deleted should
X remove the working file. For extra credit, extend the notion of `deleted' to
X include `renamed', i.e. when an RCS file gets renamed.
X
X Use a better scheme for locking revisions; the current scheme requires
X changing the RCS file just to lock or unlock a revision.
X The new scheme should coexist as well as possible with older versions of RCS.
X
X Permit multiple option-filename pairs, e.g. co -r1.4 a -r1.5 b.
X
X Add rcs options for changing keyword names, e.g. XConsortium instead of Id.
X
X If there are multiple locks by a user, ci should fall back on ci -k's
X method to figure out which version it is.
X
X Add frozen branches a la SCCS. In general, be able to emulate all of
X SCCS, so that an SCCS-to-RCS program can be practical.
X
X Improve RCS's method for storing binary files.
X Although it is more efficient than SCCS's,
X the diff algorithm is still line oriented,
X and often generates long output for minor changes to an executable file.
X
X Port binary file handling to non-Unix hosts where fopen(F,"r") and
X fopen(F,"rb") are quite different beasts.
X
X Extend the grammar of RCS files so that keywords need not be in a fixed order.
X
X Clean up the source code with a consistent indenting style.
X
X Update the date parser to use the more modern getdate.y by Bellovin, Salz,
X and Berets.
X
X Internationalize messages; unfortunately, there's no common standard yet.
X
X Prune the unnecessary keyword substitution baggage from the rcs command.
X
X Break up the code into a library so that it's easier to write new programs
X that manipulate RCS files.
X
X
XCredits:
X
X RCS was designed and built by Walter F. Tichy of Purdue University.
X RCS version 3 was released in 1983.
X
X Thomas Narten, Dan Trinkle, and others of Purdue supported RCS through
X version 4.2, released in 1989. Guy Harris of Sun contributed many porting
X fixes. Paul Eggert of System Development Corporation contributed bug fixes
X and tuneups. Jay Lepreau contributed 4.3BSD support.
X
X Paul Eggert of Twin Sun wrote the changes for RCS version 5, released in
X 1990. Ideas for setgid support were contributed by Bill Hahn of Stratus.
X Test case ideas were contributed by Matt Cross of Stratus.
X Adam Hammer of Purdue QAed.
END_OF_FILE
if test 11703 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(1641 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X File Name Archive # Description
X----------------------------------------------------------
XPACKNOTES 1 Warnings about long lines, etc
XREADME 1
XMANIFEST 1
XCOPYING 8
XMakefile 1
Xman 1
Xman/Makefile 12
Xman/ci.1 9
Xman/co.1 9
Xman/ident.1 12
Xman/merge.1 11
Xman/rcs.1 11
Xman/rcsclean.1 11
Xman/rcsdiff.1 11
Xman/rcsfile.5 11
Xman/rcsfreeze.1 12
Xman/rcsintro.1 10
Xman/rcsmerge.1 11
Xman/rlog.1 6
Xrcs.ms.01 2 (part 1)
Xrcs.ms.02 11 (part 2)
Xrcs_func.ms 10
Xsrc 1
Xsrc/Makefile 3
Xsrc/ci.c 1
Xsrc/co.c 7
Xsrc/conf.heg 11
Xsrc/conf.sh 9
Xsrc/ident.c 11
Xsrc/maketime.c 10
Xsrc/merge.sh 11
Xsrc/partime.c 8
Xsrc/rcs.c 3
Xsrc/rcsbase.h 6
Xsrc/rcsclean.sh 12
Xsrc/rcsdiff.c 10
Xsrc/rcsedit.c 6
Xsrc/rcsfcmp.c 7
Xsrc/rcsfnms.c 5
Xsrc/rcsfreeze.sh 11
Xsrc/rcsgen.c 9
Xsrc/rcskeep.c 10
Xsrc/rcskeys.c 2
Xsrc/rcslex.c 5
Xsrc/rcsmap.c 11
Xsrc/rcsmerge.c 10
Xsrc/rcsrev.c 7
Xsrc/rcssyn.c 8
Xsrc/rcsutil.c 4
Xsrc/rlog.c 4
END_OF_FILE
if test 1641 -ne `wc -c <'MANIFEST'`; then
echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(300 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XSUBDIR= src man
XDESTDIR=
X
Xall: ${SUBDIR}
X
X${SUBDIR}: FRC
X cd $@; make ${MFLAGS} DESTDIR=${DESTDIR}
X
Xinstall:
X for i in ${SUBDIR}; do \
X (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} install); \
X done
X
Xclean:
X for i in ${SUBDIR}; do \
X (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} clean); \
X done
X
XFRC:
X
END_OF_FILE
if test 300 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test ! -d 'man' ; then
echo shar: Creating directory \"'man'\"
mkdir 'man'
fi
if test ! -d 'src' ; then
echo shar: Creating directory \"'src'\"
mkdir 'src'
fi
if test -f 'src/ci.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/ci.c'\"
else
echo shar: Extracting \"'src/ci.c'\" \(34353 characters\)
sed "s/^X//" >'src/ci.c' <<'END_OF_FILE'
X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
X Copyright 1990 by Paul Eggert
X Distributed under license by the Free Software Foundation, Inc.
X
XThis file is part of RCS.
X
XRCS is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XRCS is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with RCS; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
XReport problems and direct all questions to:
X
X rcs-bugs at cs.purdue.edu
X
X*/
X
X/*
X * RCS checkin operation
X */
X/*******************************************************************
X * check revisions into RCS files
X *******************************************************************
X */
X
X
X
X/* $Log: ci.c,v $
X * Revision 5.12 1990/12/31 01:00:12 eggert
X * Don't use uninitialized storage when handling -{N,n}.
X *
X * Revision 5.11 1990/12/04 05:18:36 eggert
X * Use -I for prompts and -q for diagnostics.
X *
X * Revision 5.10 1990/11/05 20:30:10 eggert
X * Don't remove working file when aborting due to no changes.
X *
X * Revision 5.9 1990/11/01 05:03:23 eggert
X * Add -I and new -t behavior. Permit arbitrary data in logs.
X *
X * Revision 5.8 1990/10/04 06:30:09 eggert
X * Accumulate exit status across files.
X *
X * Revision 5.7 1990/09/25 20:11:46 hammer
X * fixed another small typo
X *
X * Revision 5.6 1990/09/24 21:48:50 hammer
X * added cleanups from Paul Eggert.
X *
X * Revision 5.5 1990/09/21 06:16:38 hammer
X * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin
X * the same as the terminal
X *
X * Revision 5.4 1990/09/20 02:38:51 eggert
X * ci -k now checks dates more thoroughly.
X *
X * Revision 5.3 1990/09/11 02:41:07 eggert
X * Fix revision bug with `ci -k file1 file2'.
X *
X * Revision 5.2 1990/09/04 08:02:10 eggert
X * Permit adjacent revisions with identical time stamps (possible on fast hosts).
X * Improve incomplete line handling. Standardize yes-or-no procedure.
X *
X * Revision 5.1 1990/08/29 07:13:44 eggert
X * Expand locker value like co. Clean old log messages too.
X *
X * Revision 5.0 1990/08/22 08:10:00 eggert
X * Don't require a final newline.
X * Make lock and temp files faster and safer.
X * Remove compile-time limits; use malloc instead.
X * Permit dates past 1999/12/31. Switch to GMT.
X * Add setuid support. Don't pass +args to diff. Check diff's output.
X * Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
X * Check diff's output.
X *
X * Revision 4.9 89/05/01 15:10:54 narten
X * changed copyright header to reflect current distribution rules
X *
X * Revision 4.8 88/11/08 13:38:23 narten
X * changes from root at seismo.CSS.GOV (Super User)
X * -d with no arguments uses the mod time of the file it is checking in
X *
X * Revision 4.7 88/08/09 19:12:07 eggert
X * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
X * Use execv(), not system(); allow cc -R; remove lint.
X * isatty(fileno(stdin)) -> ttystdin()
X *
X * Revision 4.6 87/12/18 11:34:41 narten
X * lint cleanups (from Guy Harris)
X *
X * Revision 4.5 87/10/18 10:18:48 narten
X * Updating version numbers. Changes relative to revision 1.1 are actually
X * relative to 4.3
X *
X * Revision 1.3 87/09/24 13:57:19 narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
X * warnings)
X *
X * Revision 1.2 87/03/27 14:21:33 jenkins
X * Port to suns
X *
X * Revision 4.3 83/12/15 12:28:54 wft
X * ci -u and ci -l now set mode of working file properly.
X *
X * Revision 4.2 83/12/05 13:40:54 wft
X * Merged with 3.9.1.1: added calls to clearerr(stdin).
X * made rewriteflag external.
X *
X * Revision 4.1 83/05/10 17:03:06 wft
X * Added option -d and -w, and updated assingment of date, etc. to new delta.
X * Added handling of default branches.
X * Option -k generates std. log message; fixed undef. pointer in reading of log.
X * Replaced getlock() with findlock(), link--unlink with rename(),
X * getpwuid() with getcaller().
X * Moved all revision number generation to new routine addelta().
X * Removed calls to stat(); now done by pairfilenames().
X * Changed most calls to catchints() with restoreints().
X * Directed all interactive messages to stderr.
X *
X * Revision 3.9.1.1 83/10/19 04:21:03 lepreau
X * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
X *
X * Revision 3.9 83/02/15 15:25:44 wft
X * 4.2 prerelease
X *
X * Revision 3.9 83/02/15 15:25:44 wft
X * Added call to fastcopy() to copy remainder of RCS file.
X *
X * Revision 3.8 83/01/14 15:34:05 wft
X * Added ignoring of interrupts while new RCS file is renamed;
X * Avoids deletion of RCS files by interrupts.
X *
X * Revision 3.7 82/12/10 16:09:20 wft
X * Corrected checking of return code from diff.
X *
X * Revision 3.6 82/12/08 21:34:49 wft
X * Using DATEFORM to prepare date of checked-in revision;
X * Fixed return from addbranch().
X *
X * Revision 3.5 82/12/04 18:32:42 wft
X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
X * field lockedby in removelock(), moved getlogmsg() before calling diff.
X *
X * Revision 3.4 82/12/02 13:27:13 wft
X * added option -k.
X *
X * Revision 3.3 82/11/28 20:53:31 wft
X * Added mustcheckin() to check for redundant checkins.
X * Added xpandfile() to do keyword expansion for -u and -l;
X * -m appends linefeed to log message if necessary.
X * getlogmsg() suppresses prompt if stdin is not a terminal.
X * Replaced keeplock with lockflag, fclose() with ffclose(),
X * %02d with %.2d, getlogin() with getpwuid().
X *
X * Revision 3.2 82/10/18 20:57:23 wft
X * An RCS file inherits its mode during the first ci from the working file,
X * otherwise it stays the same, except that write permission is removed.
X * Fixed ci -l, added ci -u (both do an implicit co after the ci).
X * Fixed call to getlogin(), added call to getfullRCSname(), added check
X * for write error.
X * Changed conflicting identifiers.
X *
X * Revision 3.1 82/10/13 16:04:59 wft
X * fixed type of variables receiving from getc() (char -> int).
X * added include file dbm.h for getting BYTESIZ. This is used
X * to check the return code from diff portably.
X */
X
X#include "rcsbase.h"
X
Xstruct Symrev {
X const char *ssymbol;
X int override;
X struct Symrev * nextsym;
X};
X
X/* rcsfcmp */
Xint rcsfcmp P((const char*,const char*,const struct hshentry*));
X
X/* rcskeep */
Xextern char prevdate[];
Xextern struct buf prevauthor, prevrev, prevstate;
Xint getoldkeys P((FILE*));
X
Xstatic const char *xpandfile P((const char*,const char*,const struct hshentry*));
Xstatic const char *getdate P((void));
Xstatic int addbranch P((struct hshentry*,struct buf*));
Xstatic int addelta P((void));
Xstatic int mustcheckin P((const char*,const struct hshentry*));
Xstatic struct cbuf getlogmsg P((void));
Xstatic struct hshentry *removelock P((struct hshentry*));
Xstatic void cleanup P((void));
Xstatic void incnum P((const char*,struct buf*));
Xstatic void addassoclst P((int, char *));
X
Xstatic const char diff[] = DIFF;
X
Xstatic FILE *workptr; /* working file pointer */
Xstatic const char *olddeltanum; /* number of old delta */
Xstatic struct buf newdelnum; /* new revision number */
Xstatic struct cbuf msg;
Xstatic int exitstatus;
Xstatic int forceciflag; /* forces check in */
Xstatic int keepflag, keepworkingfile, rcsinitflag;
Xstatic struct hshentries *gendeltas; /* deltas to be generated */
Xstatic struct hshentry *targetdelta; /* old delta to be generated */
Xstatic struct hshentry newdelta; /* new delta to be inserted */
Xstatic struct Symrev *assoclst, *lastassoc;
X
XmainProg(ciId, "ci", "$Id: ci.c,v 5.12 1990/12/31 01:00:12 eggert Exp $")
X{
X static const char cmdusage[] =
X "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ...";
X
X char altdate[datesize];
X const char *author, *krev, *rev, *state, *textfile;
X const char *diffilename, *expfilename;
X const char *workdiffname, *newworkfilename;
X int exit_stats; /* return code for command invocations */
X int lockflag;
X int r;
X int usestatdate; /* Use mod time of file for -d. */
X mode_t newRCSmode; /* mode for RCS file */
X mode_t newworkmode; /* mode for working file */
X struct Symrev *curassoc;
X
X initid();
X catchints();
X
X author = rev = state = textfile = nil;
X curassoc = assoclst = lastassoc = (struct Symrev *) nil;
X lockflag = false;
X altdate[0]= '\0'; /* empty alternate date for -d */
X usestatdate=false;
X
X while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
X switch ((*argv)[1]) {
X
X case 'r':
X keepworkingfile = lockflag = false;
X revno: if ((*argv)[2]!='\0') {
X if (rev) warn("redefinition of revision number");
X rev = (*argv)+2;
X }
X break;
X
X case 'l':
X keepworkingfile=lockflag=true;
X goto revno;
X
X case 'u':
X keepworkingfile=true; lockflag=false;
X goto revno;
X
X case 'I':
X interactiveflag = true;
X goto revno;
X
X case 'q':
X quietflag=true;
X goto revno;
X
X case 'f':
X forceciflag=true;
X goto revno;
X
X case 'k':
X keepflag=true;
X goto revno;
X
X case 'm':
X if (msg.size) redefined('m');
X msg = cleanlogmsg(*argv+2, strlen(*argv+2));
X if (!msg.size)
X warn("missing message for -m option");
X break;
X
X case 'n':
X if ((*argv)[2] == '\0') {
X error("missing symbolic name after -n");
X break;
X }
X checksid((*argv)+2);
X addassoclst(false, (*argv)+2);
X break;
X
X case 'N':
X if ((*argv)[2] == '\0') {
X error("missing symbolic name after -N");
X break;
X }
X checksid((*argv)+2);
X addassoclst(true, (*argv)+2);
X break;
X
X case 's':
X if ((*argv)[2]!='\0'){
X if (state) redefined('s');
X checksid((*argv)+2);
X state = (*argv)+2;
X } else
X warn("missing state for -s option");
X break;
X
X case 't':
X if ((*argv)[2]!='\0'){
X if (textfile) redefined('t');
X textfile = (*argv)+2;
X }
X break;
X
X case 'd':
X if (altdate[0] || usestatdate)
X redefined('d');
X altdate[0] = 0;
X usestatdate = false;
X if ((*argv)[2])
X str2date(*argv+2, altdate);
X else
X usestatdate = true;
X break;
X
X case 'w':
X if ((*argv)[2]!='\0'){
X if (author) redefined('w');
X checksid((*argv)+2);
X author = (*argv)+2;
X } else
X warn("missing author for -w option");
X break;
X
X case 'V':
X setRCSversion(*argv);
X break;
X
X
X
X default:
X faterror("unknown option: %s%s", *argv, cmdusage);
X };
X } /* end processing of options */
X
X if (argc<1) faterror("no input file%s", cmdusage);
X
X /* now handle all filenames */
X do {
X finptr=frewrite=NULL;
X fcopy = foutptr = NULL;
X workptr = NULL;
X targetdelta=nil;
X olddeltanum=nil;
X ffree();
X
X switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
X
X case -1: /* New RCS file */
X rcsinitflag = true;
X break;
X
X case 0: /* Error */
X continue;
X
X case 1: /* Normal checkin with prev . RCS file */
X rcsinitflag = !Head;
X }
X
X /* now RCSfilename contains the name of the RCS file, and
X * workfilename contains the name of the working file.
X * If the RCS file exists, finptr contains the file descriptor for the
X * RCS file. The admin node is initialized.
X * RCSstat is set.
X */
X
X diagnose("%s <-- %s\n", RCSfilename,workfilename);
X
X errno = 0;
X if (!(workptr = fopen(workfilename,"r"))) {
X eerror(workfilename);
X continue;
X }
X if (!getfworkstat(fileno(workptr))) continue;
X newRCSmode =
X (rcsinitflag ? workstat.st_mode : RCSstat.st_mode)
X & ~(S_IWUSR|S_IWGRP|S_IWOTH);
X /* newRCSmode also adjusts mode of working file for -u and -l. */
X if (finptr && !checkaccesslist()) continue; /* give up */
X
X krev = rev;
X if (keepflag) {
X /* get keyword values from working file */
X if (!getoldkeys(workptr)) continue;
X if (!rev && !*(krev = prevrev.string)) {
X error("can't find a revision number in %s",workfilename);
X continue;
X }
X if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false)
X warn("can't find a date in %s", workfilename);
X if (!*prevauthor.string && !author)
X warn("can't find an author in %s", workfilename);
X if (!*prevstate.string && !state)
X warn("can't find a state in %s", workfilename);
X } /* end processing keepflag */
X
X gettree(); /* reads in the delta tree.*/
X
X /* expand symbolic revision number */
X if (!expandsym(krev,&newdelnum)) continue;
X
X /* splice new delta into tree */
X if (!addelta()) continue;
X
X if (rcsinitflag) {
X diagnose("initial revision: %s\n", newdelnum.string);
X } else diagnose("new revision: %s; previous revision: %s\n",
X newdelnum.string, olddeltanum);
X
X newdelta.num = newdelnum.string;
X newdelta.branches=nil;
X newdelta.lockedby=nil; /*might be changed by addlock() */
X newdelta.selector = true;
X /* set author */
X if (author!=nil)
X newdelta.author=author; /* set author given by -w */
X else if (keepflag && *prevauthor.string)
X newdelta.author=prevauthor.string; /* preserve old author if possible*/
X else newdelta.author=getcaller();/* otherwise use caller's id */
X if (state!=nil)
X newdelta.state=state; /* set state given by -s */
X else if (keepflag && *prevstate.string)
X newdelta.state=prevstate.string; /* preserve old state if possible */
X else newdelta.state=DEFAULTSTATE;/* otherwise use default state */
X if (usestatdate) {
X time2date(workstat.st_mtime, altdate);
X }
X if (*altdate!='\0')
X newdelta.date=altdate; /* set date given by -d */
X else if (keepflag && *prevdate) /* preserve old date if possible */
X newdelta.date = prevdate;
X else
X newdelta.date = getdate(); /* use current date */
X /* now check validity of date -- needed because of -d and -k */
X if (targetdelta!=nil &&
X cmpnum(newdelta.date,targetdelta->date) < 0) {
X error("Date %s precedes %s in existing revision %s.",
X newdelta.date,targetdelta->date, targetdelta->num);
X continue;
X }
X
X
X if (lockflag && addlock(&newdelta) < 0) continue;
X curassoc = assoclst;
X while (curassoc) {
X if (!addsymbol(newdelta.num, curassoc->ssymbol, curassoc->override))
X break;
X curassoc = curassoc->nextsym;
X }
X if (curassoc) continue;
X
X
X putadmin(frewrite);
X puttree(Head,frewrite);
X putdesc(false,textfile);
X
X
X /* build rest of file */
X if (rcsinitflag) {
X /* get logmessage */
X newdelta.log=getlogmsg();
X if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
X } else {
X diffilename = maketemp(0);
X workdiffname = workfilename;
X if (workdiffname[0] == '+') {
X /* Some diffs have options with leading '+'. */
X char *w = ftnalloc(char, strlen(workfilename)+3);
X workdiffname = w;
X *w++ = '.';
X *w++ = SLASH;
X VOID strcpy(w, workfilename);
X }
X if (&newdelta==Head) {
X /* prepend new one */
X foutptr = NULL;
X if (!(expfilename=
X buildrevision(gendeltas,targetdelta,false,false))) continue;
X if (!mustcheckin(expfilename,targetdelta)) continue;
X /* don't check in files that aren't different, unless forced*/
X newdelta.log=getlogmsg();
X exit_stats = run((char*)nil,diffilename,
X diff DIFF_FLAGS, workdiffname, expfilename,
X (char*)nil);
X if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
X faterror ("diff failed");
X /* diff status is EXIT_TROUBLE on failure. */
X if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
X if (!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite,true)) continue;
X } else {
X /* insert new delta text */
X foutptr = frewrite;
X if (!(expfilename=
X buildrevision(gendeltas,targetdelta,false,false))) continue;
X if (!mustcheckin(expfilename,targetdelta)) continue;
X /* don't check in files that aren't different, unless forced*/
X newdelta.log=getlogmsg();
X exit_stats = run((char*)nil, diffilename,
X diff DIFF_FLAGS, expfilename, workdiffname,
X (char*)nil);
X if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
X faterror ("diff failed");
X if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue;
X }
X
X /* rewrite rest of RCS file */
X fastcopy(finptr,frewrite);
X ffclose(finptr); finptr=NULL; /* Help the file system. */
X }
X ffclose(frewrite); frewrite=NULL;
X ffclose(workptr); workptr=NULL;
X seteid();
X if ((r = chmod(newRCSfilename,newRCSmode)) == 0) {
X ignoreints();
X r = re_name(newRCSfilename,RCSfilename);
X keepdirtemp(newRCSfilename);
X restoreints();
X }
X setrid();
X if (r != 0) {
X eerror(RCSfilename);
X error("saved in %s", newRCSfilename);
X dirtempunlink();
X break;
X }
X
X if (!keepworkingfile) {
X r = unlink(workfilename); /* Get rid of old file */
X } else {
X newworkmode = WORKMODE(newRCSmode,
X !(Expand == OLD_EXPAND || !lockflag && StrictLocks)
X );
X /* Expand if !OLD_EXPAND, or if mode can't be fixed. */
X if (
X Expand != OLD_EXPAND
X || ( workstat.st_mode != newworkmode
X && (r = chmod(workfilename,newworkmode)) < 0
X )
X ) {
X /* Expand keywords in file. */
X locker_expansion = lockflag;
X newworkfilename=
X xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
X if (!newworkfilename) continue; /* expand failed */
X if ((r = chmod(newworkfilename, newworkmode)) == 0) {
X ignoreints();
X r = re_name(newworkfilename,workfilename);
X keepdirtemp(newworkfilename);
X restoreints();
X }
X }
X }
X if (r != 0) {
X eerror(workfilename);
X continue;
X }
X diagnose("done\n");
X
X } while (cleanup(),
X ++argv, --argc >=1);
X
X tempunlink();
X exitmain(exitstatus);
X} /* end of main (ci) */
X
X static void
Xcleanup()
X{
X if (nerror) exitstatus = EXIT_FAILURE;
X if (finptr) ffclose(finptr);
X if (frewrite) ffclose(frewrite);
X if (workptr) ffclose(workptr);
X dirtempunlink();
X}
X
X#if lint
X# define exiterr ciExit
X#endif
X exiting void
Xexiterr()
X{
X dirtempunlink();
X tempunlink();
X _exit(EXIT_FAILURE);
X}
X
X/*****************************************************************/
X/* the rest are auxiliary routines */
X
X
X static int
Xaddelta()
X/* Function: Appends a delta to the delta tree, whose number is
X * given by newdelnum. Updates Head, newdelnum, newdelnumlength,
X * olddeltanum and the links in newdelta.
X * Returns false on error, true on success.
X */
X{
X register char *tp;
X register unsigned i;
X unsigned newdnumlength; /* actual length of new rev. num. */
X
X newdnumlength = countnumflds(newdelnum.string);
X
X if (rcsinitflag) {
X /* this covers non-existing RCS file and a file initialized with rcs -i */
X if ((newdnumlength==0)&&(Dbranch!=nil)) {
X bufscpy(&newdelnum, Dbranch);
X newdnumlength = countnumflds(Dbranch);
X }
X if (newdnumlength==0) bufscpy(&newdelnum, "1.1");
X else if (newdnumlength==1) bufscat(&newdelnum, ".1");
X else if (newdnumlength>2) {
X error("Branch point doesn't exist for %s.",newdelnum.string);
X return false;
X } /* newdnumlength == 2 is OK; */
X olddeltanum=nil;
X Head = &newdelta;
X newdelta.next=nil;
X return true;
X }
X if (newdnumlength==0) {
X /* derive new revision number from locks */
X switch (findlock(true, &targetdelta)) {
X
X default:
X /* found two or more old locks */
X return false;
X
X case 1:
X /* found an old lock */
X olddeltanum=targetdelta->num;
X /* check whether locked revision exists */
X if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
X if (targetdelta==Head) {
X /* make new head */
X newdelta.next=Head;
X Head= &newdelta;
X incnum(olddeltanum, &newdelnum);
X } else if (!targetdelta->next && countnumflds(olddeltanum)>2) {
X /* new tip revision on side branch */
X targetdelta->next= &newdelta;
X newdelta.next = nil;
X incnum(olddeltanum, &newdelnum);
X } else {
X /* middle revision; start a new branch */
X bufscpy(&newdelnum, "");
X if (!addbranch(targetdelta,&newdelnum)) return false;
X }
X return true; /* successful use of existing lock */
X
X case 0:
X /* no existing lock; try Dbranch */
X /* update newdelnum */
X if (StrictLocks || !myself(RCSstat.st_uid)) {
X error("no lock set by %s",getcaller());
X return false;
X }
X if (Dbranch) {
X bufscpy(&newdelnum, Dbranch);
X } else {
X incnum(Head->num, &newdelnum);
X }
X newdnumlength = countnumflds(newdelnum.string);
X /* now fall into next statement */
X }
X }
X if (newdnumlength<=2) {
X /* add new head per given number */
X olddeltanum=Head->num;
X if(newdnumlength==1) {
X /* make a two-field number out of it*/
X if (cmpnumfld(newdelnum.string,olddeltanum,1)==0)
X incnum(olddeltanum, &newdelnum);
X else
X bufscat(&newdelnum, ".1");
X }
X if (cmpnum(newdelnum.string,olddeltanum) <= 0) {
X error("deltanumber %s too low; must be higher than %s",
X newdelnum.string, Head->num);
X return false;
X }
X if (!(targetdelta=removelock(Head))) return false;
X if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
X newdelta.next=Head;
X Head= &newdelta;
X } else {
X /* put new revision on side branch */
X /*first, get branch point */
X tp = newdelnum.string;
X for (i = newdnumlength - (newdnumlength&1 ^ 1); (--i); )
X while (*tp++ != '.')
X ;
X *--tp = 0; /* Kill final dot to get old delta temporarily. */
X if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas)))
X return false;
X olddeltanum = targetdelta->num;
X if (cmpnum(olddeltanum, newdelnum.string) != 0) {
X error("can't find branchpoint %s", newdelnum.string);
X return false;
X }
X *tp = '.'; /* Restore final dot. */
X if (!addbranch(targetdelta,&newdelnum)) return false;
X }
X return true;
X}
X
X
X
X static int
Xaddbranch(branchpoint,num)
X struct hshentry *branchpoint;
X struct buf *num;
X/* adds a new branch and branch delta at branchpoint.
X * If num is the null string, appends the new branch, incrementing
X * the highest branch number (initially 1), and setting the level number to 1.
X * the new delta and branchhead are in globals newdelta and newbranch, resp.
X * the new number is placed into num.
X * returns false on error.
X */
X{
X struct branchhead *bhead, **btrail;
X struct buf branchnum;
X int result;
X unsigned field, numlength;
X static struct branchhead newbranch; /* new branch to be inserted */
X
X numlength = countnumflds(num->string);
X
X if (branchpoint->branches==nil) {
X /* start first branch */
X branchpoint->branches = &newbranch;
X if (numlength==0) {
X bufscpy(num, branchpoint->num);
X bufscat(num, ".1.1");
X } else if (numlength&1)
X bufscat(num, ".1");
X newbranch.nextbranch=nil;
X
X } else if (numlength==0) {
X /* append new branch to the end */
X bhead=branchpoint->branches;
X while (bhead->nextbranch) bhead=bhead->nextbranch;
X bhead->nextbranch = &newbranch;
X bufautobegin(&branchnum);
X getbranchno(bhead->hsh->num, &branchnum);
X incnum(branchnum.string, num);
X bufautoend(&branchnum);
X bufscat(num, ".1");
X newbranch.nextbranch=nil;
X } else {
X /* place the branch properly */
X field = numlength - (numlength&1 ^ 1);
X /* field of branch number */
X btrail = &branchpoint->branches;
X while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) {
X btrail = &(*btrail)->nextbranch;
X if (!*btrail) {
X result = -1;
X break;
X }
X }
X if (result < 0) {
X /* insert/append new branchhead */
X newbranch.nextbranch = *btrail;
X *btrail = &newbranch;
X if (numlength&1) bufscat(num, ".1");
X } else {
X /* branch exists; append to end */
X bufautobegin(&branchnum);
X getbranchno(num->string, &branchnum);
X targetdelta=genrevs(branchnum.string,(char*)nil,
X (char*)nil,(char*)nil,&gendeltas);
X bufautoend(&branchnum);
X if (!targetdelta) return false;
X olddeltanum=targetdelta->num;
X if (cmpnum(num->string,olddeltanum) <= 0) {
X error("deltanumber %s too low; must be higher than %s",
X num->string,olddeltanum);
X return false;
X }
X if (!removelock(targetdelta)) return false;
X if (numlength&1) incnum(olddeltanum,num);
X targetdelta->next= &newdelta;
X newdelta.next=nil;
X return true; /* Don't do anything to newbranch */
X }
X }
X newbranch.hsh = &newdelta;
X newdelta.next=nil;
X return true;
X}
X
X
X
X static void
Xincnum(onum,nnum)
X const char *onum;
X struct buf *nnum;
X/* Increment the last field of revision number onum by one and
X * place the result into nnum.
X */
X{
X register const char *sp;
X register char *tp;
X register unsigned i;
X
X sp = onum;
X bufalloc(nnum, strlen(sp)+2);
X tp = nnum->string;
X for (i=countnumflds(sp); (--i); ) {
X while (*sp != '.') *tp++ = *sp++;
X *tp++ = *sp++; /* copy dot also */
X }
X VOID sprintf(tp, "%d", atoi(sp)+1);
X}
X
X
X
X static struct hshentry *
Xremovelock(delta)
Xstruct hshentry * delta;
X/* function: Finds the lock held by caller on delta,
X * removes it, and returns a pointer to the delta.
X * Prints an error message and returns nil if there is no such lock.
X * An exception is if !StrictLocks, and caller is the owner of
X * the RCS file. If caller does not have a lock in this case,
X * delta is returned.
X */
X{
X register struct lock * next, * trail;
X const char *num;
X struct lock dummy;
X int whomatch, nummatch;
X
X num=delta->num;
X dummy.nextlock=next=Locks;
X trail = &dummy;
X while (next!=nil) {
X whomatch = strcmp(getcaller(), next->login);
X nummatch=strcmp(num,next->delta->num);
X if ((whomatch==0) && (nummatch==0)) break;
X /*found a lock on delta by caller*/
X if ((whomatch!=0)&&(nummatch==0)) {
X error("revision %s locked by %s",num,next->login);
X return nil;
X }
X trail=next;
X next=next->nextlock;
X }
X if (next!=nil) {
X /*found one; delete it */
X trail->nextlock=next->nextlock;
X Locks=dummy.nextlock;
X next->delta->lockedby=nil; /* reset locked-by */
X return next->delta;
X } else {
X if (StrictLocks || !myself(RCSstat.st_uid)) {
X error("no lock set by %s for revision %s",getcaller(),num);
X return nil;
X } else {
X return delta;
X }
X }
X}
X
X
X
X static const char *
Xgetdate()
X/* Return a pointer to the current date. */
X{
X static char buffer[datesize]; /* date buffer */
X time_t t;
X
X if (!buffer[0]) {
X t = time((time_t *)0);
X if (t == -1)
X faterror("time not available");
X time2date(t, buffer);
X }
X return buffer;
X}
X
X
X static const char *
Xxpandfile (unexfname,dir,delta)
X const char *unexfname, *dir;
X const struct hshentry *delta;
X/* Function: Reads file unexpfname and copies it to a
X * file in dir, performing keyword substitution with data from delta.
X * returns the name of the expanded file if successful, nil otherwise.
X */
X{
X const char *targetfname;
X FILE * unexfile, *exfile;
X
X targetfname = makedirtemp(dir,0);
X errno = 0;
X if (!(unexfile = fopen(unexfname, "r"))) {
X eerror(unexfname);
X return nil;
X }
X errno = 0;
X if (!(exfile = fopen(targetfname, "w"))) {
X eerror(targetfname);
X error("can't expand file %s",unexfname);
X ffclose(unexfile);
X return nil;
X }
X if (Expand == OLD_EXPAND)
X fastcopy(unexfile,exfile);
X else
X while (0 < expandline(unexfile,exfile,delta,false,(FILE*)nil))
X ;
X ffclose(unexfile);ffclose(exfile);
X return targetfname;
X}
X
X
X static int
Xmustcheckin (unexfname,delta)
X const char *unexfname;
X const struct hshentry *delta;
X/* Function: determines whether checkin should proceed.
X * Compares the workfilename with unexfname, disregarding keywords.
X * If the 2 files differ, returns true. If they do not differ, asks the user
X * whether to return true or false (i.e., whether to checkin the file anyway);
X * the default answer is false.
X * Shortcut: If forceciflag is set, mustcheckin() always returns true.
X */
X{
X int result;
X
X if (forceciflag) return true;
X
X if (!rcsfcmp(workfilename,unexfname,delta)) return true;
X /* If files are different, must check them in. */
X
X /* files are the same */
X if (!(result = yesorno(false,
X "File %s is unchanged with respect to revision %s\ncheckin anyway? [ny](n): ",
X workfilename, delta->num
X ))) {
X error("%scheckin aborted",
X !quietflag && ttystdin() ? "" : "file is unchanged; "
X );
X }
X return result;
X}
X
X
X
X
X/* --------------------- G E T L O G M S G --------------------------------*/
X
X
X static struct cbuf
Xgetlogmsg()
X/* Function: obtains a log message and returns a pointer to it.
X * If a log message is given via the -m option, a pointer to that
X * string is returned.
X * If this is the initial revision, a standard log message is returned.
X * Otherwise, reads a character string from the terminal.
X * Stops after reading EOF or a single '.' on a
X * line. getlogmsg prompts the first time it is called for the
X * log message; during all later calls it asks whether the previous
X * log message can be reused.
X * returns a pointer to the character string; the pointer is always non-nil.
X */
X{
X static const char
X emptych[] = "*** empty log message ***",
X initialch[] = "Initial revision";
X static const struct cbuf
X emptylog = { emptych, sizeof(emptych)-sizeof(char) },
X initiallog = { initialch, sizeof(initialch)-sizeof(char) };
X static struct buf logbuf;
X static struct cbuf logmsg;
X
X int cin;
X register char *tp;
X register size_t i;
X register const char *p;
X const char *caller, *date;
X
X if (keepflag) {
X /* generate std. log message */
X caller = getcaller();
X p = date = getdate();
X while (*p++ != '.')
X ;
X i = strlen(caller);
X bufalloc(&logbuf, sizeof(ciklog)+strlen(caller)+4+datesize);
X tp = logbuf.string;
X VOID sprintf(tp,
X "%s%s at %s%.*s/%.2s/%.2s %.2s:%.2s:%s",
X ciklog, caller,
X date[2]=='.' && VERSION(5)<=RCSversion ? "19" : "",
X p-date-1, date,
X p, p+3, p+6, p+9, p+12
X );
X logmsg.string = tp;
X logmsg.size = strlen(tp);
X return logmsg;
X }
X
X if (msg.size) return msg;
X
X if (!olddeltanum && (
X cmpnum(newdelnum.string,"1.1")==0 ||
X cmpnum(newdelnum.string,"1.0")==0
X ))
X return initiallog;
X
X if (logmsg.size) {
X /*previous log available*/
X if (yesorno(true, "reuse log message of previous file? [yn](y): "))
X return logmsg;
X }
X
X /* now read string from stdin */
X if (feof(stdin))
X faterror("can't reread redirected stdin for log message; use -m");
X if (ttystdin())
X aputs("enter log message:\n(terminate with single '.' or end of file)\n>> ",stderr);
X
X i = 0;
X tp = logbuf.string;
X while ((cin = getcstdin()) != EOF) {
X if (cin=='\n') {
X if (i && tp[i-1]=='.' && (i==1 || tp[i-2]=='\n')) {
X /* Remove trailing '.'. */
X --i;
X break;
X }
X if (ttystdin()) aputs(">> ", stderr);
X }
X bufrealloc(&logbuf, i+1);
X tp = logbuf.string;
X tp[i++] = cin;
X /*SDELIM will be changed to double SDELIM by putdtext*/
X } /* end for */
X
X /* now check whether the log message is not empty */
X logmsg = cleanlogmsg(tp, i);
X if (logmsg.size)
X return logmsg;
X return emptylog;
X}
X
X/* Make a linked list of Symbolic names */
X
X static void
Xaddassoclst(flag, sp)
Xint flag;
Xchar * sp;
X{
X struct Symrev *pt;
X
X pt = talloc(struct Symrev);
X pt->ssymbol = sp;
X pt->override = flag;
X pt->nextsym = nil;
X if (lastassoc)
X lastassoc->nextsym = pt;
X else
X assoclst = pt;
X lastassoc = pt;
X return;
X}
END_OF_FILE
if test 34353 -ne `wc -c <'src/ci.c'`; then
echo shar: \"'src/ci.c'\" unpacked with wrong size!
fi
# end of 'src/ci.c'
fi
echo shar: End of archive 1 \(of 12\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 12 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list