Subject: v24i001: RCS source control system, Part01/12 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: 825fd1d0 b2dd0696 15275f29 d497c069 Submitted-by: Adam Hammer 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@uunet.uu.net if you want that tool. # Contents: PACKNOTES README MANIFEST Makefile man src src/ci.c # Wrapped by rsalz@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@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@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@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) || 1log,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=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...