home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1991-12-19 | 67.0 KB | 2,307 lines
Newsgroups: comp.sources.unix From: hammer@cs.purdue.edu (Adam Hammer) Subject: v25i081: rcs-5.6 - Revision Control System, V5.6, Part05/11 Sender: sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: hammer@cs.purdue.edu (Adam Hammer) Posting-Number: Volume 25, Issue 81 Archive-Name: rcs-5.6/part05 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 5 (of 11)." # Contents: src/co.c src/rcsbase.h src/rcsrev.c # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 20 16:23:40 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'src/co.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/co.c'\" else echo shar: Extracting \"'src/co.c'\" \(21119 characters\) sed "s/^X//" >'src/co.c' <<'END_OF_FILE' X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990, 1991 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X This file is part of RCS. X RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. X RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. X You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X Report problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X/* X * RCS checkout operation X */ X/***************************************************************************** X * check out revisions from RCS files X ***************************************************************************** X */ X X X/* $Log: co.c,v $ X * Revision 5.9 1991/10/07 17:32:46 eggert X * ci -u src/RCS/co.c,v src/co.c <<\. X * -k affects just working file, not RCS file. X * X * Revision 5.8 1991/08/19 03:13:55 eggert X * Warn before removing somebody else's file. X * Add -M. Fix co -j bugs. Tune. X * X * Revision 5.7 1991/04/21 11:58:15 eggert X * Ensure that working file is newer than RCS file after co -[lu]. X * Add -x, RCSINIT, MS-DOS support. X * X * Revision 5.6 1990/12/04 05:18:38 eggert X * Don't checkaccesslist() unless necessary. X * Use -I for prompts and -q for diagnostics. X * X * Revision 5.5 1990/11/01 05:03:26 eggert X * Fix -j. Add -I. X * X * Revision 5.4 1990/10/04 06:30:11 eggert X * Accumulate exit status across files. X * X * Revision 5.3 1990/09/11 02:41:09 eggert X * co -kv yields a readonly working file. X * X * Revision 5.2 1990/09/04 08:02:13 eggert X * Standardize yes-or-no procedure. X * X * Revision 5.0 1990/08/22 08:10:02 eggert X * Permit multiple locks by same user. Add setuid support. X * Remove compile-time limits; use malloc instead. X * Permit dates past 1999/12/31. Switch to GMT. X * Make lock and temp files faster and safer. X * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. X * X * Revision 4.7 89/05/01 15:11:41 narten X * changed copyright header to reflect current distribution rules X * X * Revision 4.6 88/08/09 19:12:15 eggert X * Fix "co -d" core dump; rawdate wasn't always initialized. X * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint X * X * Revision 4.5 87/12/18 11:35:40 narten X * lint cleanups (from Guy Harris) X * X * Revision 4.4 87/10/18 10:20:53 narten X * Updating version numbers changes relative to 1.1, are actually X * relative to 4.2 X * X * Revision 1.3 87/09/24 13:58:30 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:38 jenkins X * Port to suns X * X * Revision 4.2 83/12/05 13:39:48 wft X * made rewriteflag external. X * X * Revision 4.1 83/05/10 16:52:55 wft X * Added option -u and -f. X * Added handling of default branch. X * Replaced getpwuid() with getcaller(). X * Removed calls to stat(); now done by pairfilenames(). X * Changed and renamed rmoldfile() to rmworkfile(). X * Replaced catchints() calls with restoreints(), unlink()--link() with rename(); X * X * Revision 3.7 83/02/15 15:27:07 wft X * Added call to fastcopy() to copy remainder of RCS file. X * X * Revision 3.6 83/01/15 14:37:50 wft X * Added ignoring of interrupts while RCS file is renamed; this avoids X * deletion of RCS files during the unlink/link window. X * X * Revision 3.5 82/12/08 21:40:11 wft X * changed processing of -d to use DATEFORM; removed actual from X * call to preparejoin; re-fixed printing of done at the end. X * X * Revision 3.4 82/12/04 18:40:00 wft X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. X * Fixed printing of "done". X * X * Revision 3.3 82/11/28 22:23:11 wft X * Replaced getlogin() with getpwuid(), flcose() with ffclose(), X * %02d with %.2d, mode generation for working file with WORKMODE. X * Fixed nil printing. Fixed -j combined with -l and -p, and exit X * for non-existing revisions in preparejoin(). X * X * Revision 3.2 82/10/18 20:47:21 wft X * Mode of working file is now maintained even for co -l, but write permission X * is removed. X * The working file inherits its mode from the RCS file, plus write permission X * for the owner. The write permission is not given if locking is strict and X * co does not lock. X * An existing working file without write permission is deleted automatically. X * Otherwise, co asks (empty answer: abort co). X * Call to getfullRCSname() added, check for write error added, call X * for getlogin() fixed. X * X * Revision 3.1 82/10/13 16:01:30 wft X * fixed type of variables receiving from getc() (char -> int). X * removed unused variables. X */ X X X X X#include "rcsbase.h" X static char const *getancestor P((char const*,char const*)); static int buildjoin P((char const*)); static int preparejoin P((void)); static int rmlock P((struct hshentry const*)); static int rmworkfile P((void)); static void cleanup P((void)); X static char const quietarg[] = "-q"; X static char const *expandarg, *join, *suffixarg, *versionarg; static char const *joinlist[joinlength]; /* revisions to be joined */ static FILE *neworkptr; static int exitstatus; static int forceflag; static int lastjoin; /* index of last element in joinlist */ static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */ static int mtimeflag; static struct hshentries *gendeltas; /* deltas to be generated */ static struct hshentry *targetdelta; /* final delta to be generated */ static struct stat workstat; X mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") X{ X static char const cmdusage[] = X "\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ..."; X X char *a, **newargv; X char const *author, *date, *rev, *state; X char const *joinfilename, *newdate, *neworkfilename; X int changelock; /* 1 if a lock has been changed, -1 if error */ X int expmode, r, tostdout, workstatstat; X struct buf numericrev; /* expanded revision number */ X char finaldate[datesize]; X X setrid(); X author = date = rev = state = nil; X bufautobegin(&numericrev); X expmode = -1; X suffixes = X_DEFAULT; X tostdout = false; X X argc = getRCSINIT(argc, argv, &newargv); X argv = newargv; X while (a = *++argv, 0<--argc && *a++=='-') { X switch (*a++) { X X case 'r': X revno: X if (*a) { X if (rev) warn("redefinition of revision number"); X rev = a; X } X break; X X case 'f': X forceflag=true; X goto revno; X X case 'l': X if (lockflag < 0) { X warn("-l overrides -u."); X } X lockflag = 1; X goto revno; X X case 'u': X if (0 < lockflag) { X warn("-l overrides -u."); X } X lockflag = -1; X goto revno; X X case 'p': X tostdout = true; 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 'd': X if (date) X redefined('d'); X str2date(a, finaldate); X date=finaldate; X break; X X case 'j': X if (*a) { X if (join) redefined('j'); X join = a; X } X break; X X case 'M': X mtimeflag = true; X goto revno; X X case 's': X if (*a) { X if (state) redefined('s'); X state = a; X } X break; X X case 'w': X if (author) redefined('w'); X if (*a) X author = a; X else X author = getcaller(); X break; X X case 'x': X suffixarg = *argv; X suffixes = a; X break; X X case 'V': X versionarg = *argv; X setRCSversion(versionarg); X break; X X case 'k': /* set keyword expand mode */ X expandarg = *argv; X if (0 <= expmode) redefined('k'); X if (0 <= (expmode = str2expmode(a))) X break; X /* fall into */ X default: X faterror("unknown option: %s%s", *argv, cmdusage); X X }; X } /* end of option processing */ X X if (argc<1) faterror("no input file%s", cmdusage); X if (tostdout) X# if text_equals_binary_stdio || text_work_stdio X workstdout = stdout; X# else X if (!(workstdout = fdopen(STDOUT_FILENO, FOPEN_W_WORK))) X efaterror("stdout"); X# endif X X /* now handle all filenames */ X do { X ffree(); X X if (pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) X continue; X X /* now RCSfilename contains the name of the RCS file, and finptr X * points at it. workfilename contains the name of the working file. X * Also, RCSstat has been set. X */ X diagnose("%s --> %s\n", RCSfilename,tostdout?"stdout":workfilename); X X workstatstat = -1; X if (tostdout) { X neworkfilename = 0; X neworkptr = workstdout; X } else { X workstatstat = stat(workfilename, &workstat); X neworkfilename = makedirtemp(workfilename, 1); X if (!(neworkptr = fopen(neworkfilename, FOPEN_W_WORK))) { X if (errno == EACCES) X error("%s: parent directory isn't writable", X workfilename X ); X else X eerror(neworkfilename); X continue; X } X } X X gettree(); /* reads in the delta tree */ X X if (Head==nil) { X /* no revisions; create empty file */ X diagnose("no revisions present; generating empty revision 0.0\n"); X Ozclose(&fcopy); X if (workstatstat == 0) X if (!rmworkfile()) continue; X changelock = 0; X newdate = 0; X /* Can't reserve a delta, so don't call addlock */ X } else { X if (rev!=nil) { X /* expand symbolic revision number */ X if (!expandsym(rev, &numericrev)) X continue; X } else X switch (lockflag<0 ? findlock(false,&targetdelta) : 0) { X default: X continue; X case 0: X bufscpy(&numericrev, Dbranch?Dbranch:""); X break; X case 1: X bufscpy(&numericrev, targetdelta->num); X break; X } X /* get numbers of deltas to be generated */ X if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) X continue; X /* check reservations */ X changelock = X lockflag < 0 ? X rmlock(targetdelta) X : lockflag == 0 ? X 0 X : X addlock(targetdelta); X X if ( X changelock < 0 || X changelock && !checkaccesslist() || X !dorewrite(lockflag, changelock) X ) X continue; X X if (0 <= expmode) X Expand = expmode; X if (0 < lockflag && Expand == VAL_EXPAND) { X error("cannot combine -kv and -l"); X continue; X } X X if (join && !preparejoin()) continue; X X diagnose("revision %s%s\n",targetdelta->num, X 0<lockflag ? " (locked)" : X lockflag<0 ? " (unlocked)" : ""); X X /* Prepare to remove old working file if necessary. */ X if (workstatstat == 0) X if (!rmworkfile()) continue; X X /* skip description */ X getdesc(false); /* don't echo*/ X X locker_expansion = 0 < lockflag; X joinfilename = buildrevision( X gendeltas, targetdelta, X join&&tostdout ? (FILE*)0 : neworkptr, X Expand!=OLD_EXPAND X ); X# if !large_memory X if (fcopy == neworkptr) X fcopy = 0; /* Don't close it twice. */ X# endif X if_advise_access(changelock && gendeltas->first!=targetdelta, X finptr, MADV_SEQUENTIAL X ); X X if (!donerewrite(changelock)) X continue; X X newdate = targetdelta->date; X if (join) { X newdate = 0; X if (!joinfilename) { X aflush(neworkptr); X joinfilename = neworkfilename; X } X if (!buildjoin(joinfilename)) X continue; X } X } X if (!tostdout) { X r = 0; X if (mtimeflag && newdate) { X if (!join) X aflush(neworkptr); X r = setfiledate(neworkfilename, newdate); X } X if (r == 0) { X ignoreints(); X r = chnamemod(&neworkptr, neworkfilename, workfilename, X WORKMODE(RCSstat.st_mode, X !(Expand==VAL_EXPAND || lockflag<=0&&StrictLocks) X ) X ); X keepdirtemp(neworkfilename); X restoreints(); X } X if (r != 0) { X eerror(workfilename); X error("see %s", neworkfilename); X continue; X } X diagnose("done\n"); X } X } while (cleanup(), X ++argv, --argc >=1); X X tempunlink(); X Ofclose(workstdout); X exitmain(exitstatus); X X} /* end of main (co) */ X X static void cleanup() X{ X if (nerror) exitstatus = EXIT_FAILURE; X Izclose(&finptr); X Ozclose(&frewrite); X# if !large_memory X if (fcopy!=workstdout) Ozclose(&fcopy); X# endif X if (neworkptr!=workstdout) Ozclose(&neworkptr); X dirtempunlink(); X} X X#if lint X# define exiterr coExit X#endif X exiting void exiterr() X{ X dirtempunlink(); X tempunlink(); X _exit(EXIT_FAILURE); X} X X X/***************************************************************** X * The following routines are auxiliary routines X *****************************************************************/ X X static int rmworkfile() X/* Function: prepares to remove workfilename, if it exists, and if X * it is read-only. X * Otherwise (file writable): X * if !quietmode asks the user whether to really delete it (default: fail); X * otherwise failure. X * Returns true if permission is gotten. X */ X{ X if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { X /* File is writable */ X if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", X workfilename, X myself(workstat.st_uid) ? "" : ", and you do not own it" X )) { X error(!quietflag && ttystdin() X ? "checkout aborted" X : "writable %s exists; checkout aborted", workfilename); X return false; X } X } X /* Actual unlink is done later by caller. */ X return true; X} X X X static int rmlock(delta) X struct hshentry const *delta; X/* Function: removes the lock held by caller on delta. X * Returns -1 if someone else holds the lock, X * 0 if there is no lock on delta, X * and 1 if a lock was found and removed. X */ X{ register struct lock * next, * trail; X char const *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; use co -r or rcs -u",num,next->login); X return -1; 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 1; /*success*/ X } else return 0; /*no lock on delta*/ X} X X X X X/***************************************************************** X * The rest of the routines are for handling joins X *****************************************************************/ X X X static char const * addjoin(joinrev) X char *joinrev; X/* Add joinrev's number to joinlist, yielding address of char past joinrev, X * or nil if no such revision exists. X */ X{ X register char *j; X register struct hshentry const *d; X char terminator; X struct buf numrev; X struct hshentries *joindeltas; X X j = joinrev; X for (;;) { X switch (*j++) { X default: X continue; X case 0: X case ' ': case '\t': case '\n': X case ':': case ',': case ';': X break; X } X break; X } X terminator = *--j; X *j = 0; X bufautobegin(&numrev); X d = 0; X if (expandsym(joinrev, &numrev)) X d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas); X bufautoend(&numrev); X *j = terminator; X if (d) { X joinlist[++lastjoin] = d->num; X return j; X } X return nil; X} X X static int preparejoin() X/* Function: Parses a join list pointed to by join and places pointers to the X * revision numbers into joinlist. X */ X{ X register char const *j; X X j=join; X lastjoin= -1; X for (;;) { X while ((*j==' ')||(*j=='\t')||(*j==',')) j++; X if (*j=='\0') break; X if (lastjoin>=joinlength-2) { X error("too many joins"); X return(false); X } X if (!(j = addjoin(j))) return false; X while ((*j==' ') || (*j=='\t')) j++; X if (*j == ':') { X j++; X while((*j==' ') || (*j=='\t')) j++; X if (*j!='\0') { X if (!(j = addjoin(j))) return false; X } else { X error("join pair incomplete"); X return false; X } X } else { X if (lastjoin==0) { /* first pair */ X /* common ancestor missing */ X joinlist[1]=joinlist[0]; X lastjoin=1; X /*derive common ancestor*/ X if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) X return false; X } else { X error("join pair incomplete"); X return false; X } X } X } X if (lastjoin<1) { X error("empty join"); X return false; X } else return true; X} X X X X static char const * getancestor(r1, r2) X char const *r1, *r2; X/* Yield the common ancestor of r1 and r2 if successful, nil otherwise. X * Work reliably only if r1 and r2 are not branch numbers. X */ X{ X static struct buf t1, t2; X X unsigned l1, l2, l3; X char const *r; X X l1 = countnumflds(r1); X l2 = countnumflds(r2); X if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) { X /* not on main trunk or identical */ X l3 = 0; X while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0) X l3 += 2; X /* This will terminate since r1 and r2 are not the same; see above. */ X if (l3==0) { X /* no common prefix; common ancestor on main trunk */ X VOID partialno(&t1, r1, l1>2 ? (unsigned)2 : l1); X VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2); X r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; X if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) X return r; X } else if (cmpnumfld(r1, r2, l3+1)!=0) X return partialno(&t1,r1,l3); X } X error("common ancestor of %s and %s undefined", r1, r2); X return nil; X} X X X X static int buildjoin(initialfile) X char const *initialfile; X/* Function: merge pairs of elements in joinlist into initialfile X * If workstdout is set, copy result to stdout. X * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). X */ X{ X struct buf commarg; X struct buf subs; X char const *rev2, *rev3; X int i; X char const *cov[10], *mergev[12]; X char const **p; X X bufautobegin(&commarg); X bufautobegin(&subs); X rev2 = maketemp(0); X rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ X X cov[0] = nil; X /* cov[1] setup below */ X cov[2] = CO; X /* cov[3] setup below */ X p = &cov[4]; X if (expandarg) *p++ = expandarg; X if (suffixarg) *p++ = suffixarg; X if (versionarg) *p++ = versionarg; X *p++ = quietarg; X *p++ = RCSfilename; X *p = nil; X X mergev[0] = nil; X mergev[1] = nil; X mergev[2] = MERGE; X mergev[3] = mergev[5] = "-L"; X /* rest of mergev setup below */ X X i=0; X while (i<lastjoin) { X /*prepare marker for merge*/ X if (i==0) X bufscpy(&subs, targetdelta->num); X else { X bufscat(&subs, ","); X bufscat(&subs, joinlist[i-2]); X bufscat(&subs, ":"); X bufscat(&subs, joinlist[i-1]); X } X diagnose("revision %s\n",joinlist[i]); X bufscpy(&commarg, "-p"); X bufscat(&commarg, joinlist[i]); X cov[1] = rev2; X cov[3] = commarg.string; X if (runv(cov)) X goto badmerge; X diagnose("revision %s\n",joinlist[i+1]); X bufscpy(&commarg, "-p"); X bufscat(&commarg, joinlist[i+1]); X cov[1] = rev3; X cov[3] = commarg.string; X if (runv(cov)) X goto badmerge; X diagnose("merging...\n"); X mergev[4] = subs.string; X mergev[6] = joinlist[i+1]; X p = &mergev[7]; X if (quietflag) *p++ = quietarg; X if (lastjoin<=i+2 && workstdout) *p++ = "-p"; X *p++ = initialfile; X *p++ = rev2; X *p++ = rev3; X *p = nil; X switch (runv(mergev)) { X case DIFF_FAILURE: case DIFF_SUCCESS: X break; X default: X goto badmerge; X } X i=i+2; X } X bufautoend(&commarg); X bufautoend(&subs); X return true; X X badmerge: X nerror++; X bufautoend(&commarg); X bufautoend(&subs); X return false; X} END_OF_FILE if test 21119 -ne `wc -c <'src/co.c'`; then echo shar: \"'src/co.c'\" unpacked with wrong size! fi # end of 'src/co.c' fi if test -f 'src/rcsbase.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rcsbase.h'\" else echo shar: Extracting \"'src/rcsbase.h'\" \(22152 characters\) sed "s/^X//" >'src/rcsbase.h' <<'END_OF_FILE' X X/* X * RCS common definitions and data structures X */ X#define RCSBASE "$Id: rcsbase.h,v 5.11 1991/10/07 17:32:46 eggert Exp $" X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990, 1991 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X This file is part of RCS. X RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. X RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. X You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X Report problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X/***************************************************************************** X * INSTRUCTIONS: X * ============= X * See the Makefile for how to define C preprocessor symbols. X * If you need to change the comment leaders, update the table comtable[] X * in rcsfnms.c. (This can wait until you know what a comment leader is.) X ***************************************************************************** X */ X X X/* $Log: rcsbase.h,v $ X * Revision 5.11 1991/10/07 17:32:46 eggert X * Support piece tables even if !has_mmap. X * X * Revision 5.10 1991/09/24 00:28:39 eggert X * Remove unexported functions. X * X * Revision 5.9 1991/08/19 03:13:55 eggert X * Add piece tables and other tuneups, and NFS workarounds. X * X * Revision 5.8 1991/04/21 11:58:20 eggert X * Add -x, RCSINIT, MS-DOS support. X * X * Revision 5.7 1991/02/28 19:18:50 eggert X * Try setuid() if seteuid() doesn't work. X * X * Revision 5.6 1991/02/26 17:48:37 eggert X * Support new link behavior. Move ANSI C / Posix declarations into conf.sh. X * X * Revision 5.5 1990/12/04 05:18:43 eggert X * Use -I for prompts and -q for diagnostics. X * X * Revision 5.4 1990/11/01 05:03:35 eggert X * Don't assume that builtins are functions; they may be macros. X * Permit arbitrary data in logs. X * X * Revision 5.3 1990/09/26 23:36:58 eggert X * Port wait() to non-Posix ANSI C hosts. X * X * Revision 5.2 1990/09/04 08:02:20 eggert X * Don't redefine NAME_MAX, PATH_MAX. X * Improve incomplete line handling. Standardize yes-or-no procedure. X * X * Revision 5.1 1990/08/29 07:13:53 eggert X * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too. X * X * Revision 5.0 1990/08/22 08:12:44 eggert X * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access(). X * Remove compile-time limits; use malloc instead. X * Ansify and Posixate. Add support for ISO 8859. X * Remove snoop and v2 support. X * X * Revision 4.9 89/05/01 15:17:14 narten X * botched previous USG fix X * X * Revision 4.8 89/05/01 14:53:05 narten X * changed #include <strings.h> -> string.h for USG systems. X * X * Revision 4.7 88/11/08 15:58:45 narten X * removed defs for functions loaded from libraries X * X * Revision 4.6 88/08/09 19:12:36 eggert X * Shrink stdio code size; remove lint; permit -Dhshsize=nn. X * X * Revision 4.5 87/12/18 17:06:41 narten X * made removed BSD ifdef, now uses V4_2BSD X * X * Revision 4.4 87/10/18 10:29:49 narten X * Updating version numbers X * Changes relative to 1.1 are actually relative to 4.2 X * X * Revision 1.3 87/09/24 14:02:25 narten X * changes for lint X * X * Revision 1.2 87/03/27 14:22:02 jenkins X * Port to suns X * X * Revision 4.2 83/12/20 16:04:20 wft X * merged 3.6.1.1 and 4.1 (SMALLOG, logsize). X * moved setting of STRICT_LOCKING to Makefile. X * changed DOLLAR to UNKN (conflict with KDELIM). X * X * Revision 4.1 83/05/04 09:12:41 wft X * Added markers Id and RCSfile. X * Added Dbranch for default branches. X * X * Revision 3.6.1.1 83/12/02 21:56:22 wft X * Increased logsize, added macro SMALLOG. X * X * Revision 3.6 83/01/15 16:43:28 wft X * 4.2 prerelease X * X * Revision 3.6 83/01/15 16:43:28 wft X * Replaced dbm.h with BYTESIZ, fixed definition of rindex(). X * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD. X * Added macro DELNUMFORM to have uniform format for printing delta text nodes. X * Added macro DELETE to mark deleted deltas. X * X * Revision 3.5 82/12/10 12:16:56 wft X * Added two forms of DATEFORM, one using %02d, the other %.2d. X * X * Revision 3.4 82/12/04 20:01:25 wft X * added LOCKER, Locker, and USG (redefinition of rindex). X * X * Revision 3.3 82/12/03 12:22:04 wft X * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3, X * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength X * using NCPPN. Changed putc() to abort on write error. X * X * Revision 3.2 82/10/18 15:03:52 wft X * added macro STRICT_LOCKING, removed RCSUMASK. X * renamed JOINFILE[1,2] to JOINFIL[1,2]. X * X * Revision 3.1 82/10/11 19:41:17 wft X * removed NBPW, NBPC, NCPW. X * added typdef int void to aid compiling X */ X X X#include "conf.h" X X X#define EXIT_TROUBLE DIFF_TROUBLE X X#ifdef PATH_MAX X# define SIZEABLE_PATH PATH_MAX /* size of a large path; not a hard limit */ X#else X# define SIZEABLE_PATH _POSIX_PATH_MAX X#endif X X/* for traditional C hosts with unusual size arguments */ X#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f) X#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f) X X X/* X * Parameters X */ X X/* backwards compatibility with old versions of RCS */ X#define VERSION_min 3 /* old output RCS format supported */ X#define VERSION_max 5 /* newest output RCS format supported */ X#ifndef VERSION_DEFAULT /* default RCS output format */ X# define VERSION_DEFAULT VERSION_max X#endif X#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */ X X#ifndef STRICT_LOCKING X#define STRICT_LOCKING 1 X#endif X /* 0 sets the default locking to non-strict; */ X /* used in experimental environments. */ X /* 1 sets the default locking to strict; */ X /* used in production environments. */ X X#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ X#define datesize (yearlength+16) /* size of output of DATEFORM */ X#define joinlength 20 /* number of joined revisions permitted */ X#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */ X#define KDELIM '$' /* delimiter for keywords */ X#define VDELIM ':' /* separates keywords from values */ X#define DEFAULTSTATE "Exp" /* default state of revisions */ X X X X#define true 1 X#define false 0 X#define nil 0 X X X/* X * RILE - readonly file X * declarecache; - declares local cache for RILE variable(s) X * setupcache - sets up the local RILE cache, but does not initialize it X * cache, uncache - caches and uncaches the local RILE; X * (uncache,cache) is needed around functions that advance the RILE pointer X * Igeteof(f,c,s) - get a char c from f, executing statement s at EOF X * cachegeteof(c,s) - Igeteof applied to the local RILE X * Iget(f,c) - like Igeteof, except EOF is an error X * cacheget(c) - Iget applied to the local RILE X * Ifileno, Irewind, Iseek, Itell - analogs to stdio routines X */ X X#if large_memory X typedef unsigned char const *Iptr_type; X typedef struct { X Iptr_type ptr, lim; X unsigned char *base; /* for lint, not Iptr_type even if has_mmap */ X# if has_mmap X# define Ifileno(f) ((f)->fd) X int fd; X# else X# define Ifileno(f) fileno((f)->stream) X FILE *stream; X unsigned char *readlim; X# endif X } RILE; X# if has_mmap X# define declarecache register Iptr_type ptr, lim X# define setupcache(f) (lim = (f)->lim) X# define Igeteof(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++ X# define cachegeteof(c,s) if (ptr==lim) s else (c)= *ptr++ X# else X# define declarecache register Iptr_type ptr; register RILE *rRILE X# define setupcache(f) (rRILE = (f)) X# define Igeteof(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++ X# define cachegeteof(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++ X# endif X# define uncache(f) ((f)->ptr = ptr) X# define cache(f) (ptr = (f)->ptr) X# define Iget(f,c) Igeteof(f,c,Ieof();) X# define cacheget(c) cachegeteof(c,Ieof();) X# define Itell(f) ((f)->ptr) X# define Iseek(f,p) ((f)->ptr = (p)) X# define Irewind(f) Iseek(f, (f)->base) X# define cachetell() ptr X#else X# define RILE FILE X# define declarecache register FILE *ptr X# define setupcache(f) (ptr = (f)) X# define uncache(f) X# define cache(f) X# define Igeteof(f,c,s) if(((c)=getc(f))<0){testIerror(f);if(feof(f))s}else X# define cachegeteof(c,s) Igeteof(ptr,c,s) X# define Iget(f,c) if (((c)=getc(f))<0) testIeof(f); else X# define cacheget(c) Iget(ptr,c) X# define Ifileno(f) fileno(f) X#endif X X/* Print a char, but abort on write error. */ X#define aputc(c,o) if (putc(c,o)<0) testOerror(o); else X X/* Get a character from an RCS file, perhaps copying to a new RCS file. */ X#define GETCeof(o,c,s) { cachegeteof(c,s); if (o) aputc(c,o); } X#define GETC(o,c) { cacheget(c); if (o) aputc(c,o); } X X X#define WORKMODE(RCSmode, writable) ((RCSmode)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0)) X/* computes mode of working file: same as RCSmode, but write permission */ X/* determined by writable */ X X X/* character classes and token codes */ enum tokens { X/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter, X PERIOD, SBEGIN, SPACE, UNKN, X/* tokens */ COLON, ID, NUM, SEMI, STRING X}; X X#define SDELIM '@' /* the actual character is needed for string handling*/ X/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN. X * there should be no overlap among SDELIM, KDELIM, and VDELIM X */ X X#define isdigit(c) ((unsigned)((c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ X X X X X X/*************************************** X * Data structures for the symbol table X ***************************************/ X X/* Buffer of arbitrary data */ struct buf { X char *string; X size_t size; X}; struct cbuf { X char const *string; X size_t size; X}; X X/* Hash table entry */ struct hshentry { X char const * num; /* pointer to revision number (ASCIZ) */ X char const * date; /* pointer to date of checkin */ X char const * author; /* login of person checking in */ X char const * lockedby; /* who locks the revision */ X char const * state; /* state of revision (Exp by default) */ X struct cbuf log; /* log message requested at checkin */ X struct branchhead * branches; /* list of first revisions on branches*/ X struct cbuf ig; /* ignored phrases of revision */ X struct hshentry * next; /* next revision on same branch */ X struct hshentry * nexthsh; /* next revision with same hash value */ X unsigned long insertlns;/* lines inserted (computed by rlog) */ X unsigned long deletelns;/* lines deleted (computed by rlog) */ X char selector; /* true if selected, false if deleted */ X}; X X/* list of hash entries */ struct hshentries { X struct hshentries *rest; X struct hshentry *first; X}; X X/* list element for branch lists */ struct branchhead { X struct hshentry * hsh; X struct branchhead * nextbranch; X}; X X/* accesslist element */ struct access { X char const * login; X struct access * nextaccess; X}; X X/* list element for locks */ struct lock { X char const * login; X struct hshentry * delta; X struct lock * nextlock; X}; X X/* list element for symbolic names */ struct assoc { X char const * symbol; X char const * num; X struct assoc * nextassoc; X}; X X X#define mainArgs (argc,argv) int argc; char **argv; X X#if lint X# define libId(name,rcsid) X# define mainProg(name,cmd,rcsid) int name mainArgs X#else X# define libId(name,rcsid) char const name[] = rcsid; X# define mainProg(name,cmd,rcsid) char const copyright[] = "Copyright 1982,1988,1989 by Walter F. Tichy\nPurdue CS\nCopyright 1990,1991 by Paul Eggert", rcsbaseId[] = RCSBASE, cmdid[] = cmd; libId(name,rcsid) int main mainArgs X#endif X X/* X * Markers for keyword expansion (used in co and ident) X * Every byte must have class LETTER or Letter. X */ X#define AUTHOR "Author" X#define DATE "Date" X#define HEADER "Header" X#define IDH "Id" X#define LOCKER "Locker" X#define LOG "Log" X#define RCSFILE "RCSfile" X#define REVISION "Revision" X#define SOURCE "Source" X#define STATE "State" X#define keylength 8 /* max length of any of the above keywords */ X enum markers { Nomatch, Author, Date, Header, Id, X Locker, Log, RCSfile, Revision, Source, State }; X /* This must be in the same order as rcskeys.c's Keyword[] array. */ X X#define DELNUMFORM "\n\n%s\n%s\n" X/* used by putdtext and scanlogtext */ X X#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */ X X/* main program */ extern char const cmdid[]; exiting void exiterr P((void)); X X/* maketime */ int setfiledate P((char const*,char const[datesize])); void str2date P((char const*,char[datesize])); void time2date P((time_t,char[datesize])); X X/* merge */ int merge P((int,char const*const[2],char const*const[3])); X X/* partime */ int partime P((char const*,struct tm*,int*)); X X/* rcsedit */ X#define ciklogsize 23 /* sizeof("checked in with -k by ") */ extern FILE *fcopy; extern char const *resultfile; extern char const ciklog[ciklogsize]; extern int locker_expansion; extern struct buf dirtfname[]; X#define newRCSfilename (dirtfname[0].string) RILE *rcswriteopen P((struct buf*,struct stat*,int)); char const *makedirtemp P((char const*,int)); char const *getcaller P((void)); int addlock P((struct hshentry*)); int addsymbol P((char const*,char const*,int)); int checkaccesslist P((void)); int chnamemod P((FILE**,char const*,char const*,mode_t)); int donerewrite P((int)); int dorewrite P((int,int)); int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*)); int findlock P((int,struct hshentry**)); void aflush P((FILE*)); void copystring P((void)); void dirtempunlink P((void)); void enterstring P((void)); void finishedit P((struct hshentry const*,FILE*,int)); void keepdirtemp P((char const*)); void openfcopy P((FILE*)); void snapshotedit P((FILE*)); void xpandstring P((struct hshentry const*)); X#if has_NFS || bad_unlink X int un_link P((char const*)); X#else X# define un_link(s) unlink(s) X#endif X#if large_memory X void edit_string P((void)); X# define editstring(delta) edit_string() X#else X void editstring P((struct hshentry const*)); X#endif X X/* rcsfcmp */ int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); X X/* rcsfnms */ X#define bufautobegin(b) ((void) ((b)->string = 0, (b)->size = 0)) extern FILE *workstdout; extern char *workfilename; extern char const *RCSfilename; extern char const *suffixes; extern struct stat RCSstat; RILE *rcsreadopen P((struct buf*,struct stat*,int)); char *bufenlarge P((struct buf*,char const**)); char const *basename P((char const*)); char const *getfullRCSname P((void)); char const *maketemp P((int)); char const *rcssuffix P((char const*)); int pairfilenames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); size_t dirlen P((char const*)); struct cbuf bufremember P((struct buf*,size_t)); void bufalloc P((struct buf*,size_t)); void bufautoend P((struct buf*)); void bufrealloc P((struct buf*,size_t)); void bufscat P((struct buf*,char const*)); void bufscpy P((struct buf*,char const*)); void tempunlink P((void)); X X/* rcsgen */ extern int interactiveflag; extern struct buf curlogbuf; char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int)); int getcstdin P((void)); int ttystdin P((void)); int yesorno P((int,char const*,...)); struct cbuf cleanlogmsg P((char*,size_t)); struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*)); void putdesc P((int,char*)); X X/* rcskeep */ extern int prevkeys; extern struct buf prevauthor, prevdate, prevrev, prevstate; int getoldkeys P((RILE*)); X X/* rcskeys */ extern char const *const Keyword[]; enum markers trymatch P((char const*)); X X/* rcslex */ extern FILE *foutptr; extern FILE *frewrite; extern RILE *finptr; extern char const *NextString; extern enum tokens nexttok; extern int hshenter; extern int nerror; extern int nextc; extern int quietflag; extern unsigned long rcsline; char const *getid P((void)); exiting void efaterror P((char const*)); exiting void enfaterror P((int,char const*)); exiting void faterror P((char const*,...)); exiting void fatserror P((char const*,...)); exiting void Ieof P((void)); exiting void Ierror P((void)); exiting void Oerror P((void)); char *checkid P((char*,int)); int eoflex P((void)); int getkeyopt P((char const*)); int getlex P((enum tokens)); struct cbuf getphrases P((char const*)); struct cbuf savestring P((struct buf*)); struct hshentry *getnum P((void)); void Ifclose P((RILE*)); void Izclose P((RILE**)); void Lexinit P((void)); void Ofclose P((FILE*)); void Ozclose P((FILE**)); void afputc P((int,FILE*)); void aprintf P((FILE*,char const*,...)); void aputs P((char const*,FILE*)); void checksid P((char*)); void diagnose P((char const*,...)); void eerror P((char const*)); void eflush P((void)); void enerror P((int,char const*)); void error P((char const*,...)); void fvfprintf P((FILE*,char const*,va_list)); void getkey P((char const*)); void getkeystring P((char const*)); void nextlex P((void)); void oflush P((void)); void printstring P((void)); void readstring P((void)); void redefined P((int)); void testIerror P((FILE*)); void testOerror P((FILE*)); void warn P((char const*,...)); void warnignore P((void)); X#if has_madvise && has_mmap && large_memory X void advise_access P((RILE*,int)); X# define if_advise_access(p,f,advice) if (p) advise_access(f,advice) X#else X# define advise_access(f,advice) X# define if_advise_access(p,f,advice) X#endif X#if has_mmap && large_memory X RILE *I_open P((char const*,struct stat*)); X# define Iopen(f,m,s) I_open(f,s) X#else X RILE *Iopen P((char const*,char const*,struct stat*)); X#endif X#if !large_memory X void testIeof P((FILE*)); X void Irewind P((RILE*)); X#endif X X/* rcsmap */ extern const enum tokens ctab[]; X X/* rcsrev */ char *partialno P((struct buf*,char const*,unsigned)); char const *tiprev P((void)); int cmpnum P((char const*,char const*)); int cmpnumfld P((char const*,char const*,unsigned)); int compartial P((char const*,char const*,unsigned)); int expandsym P((char const*,struct buf*)); int fexpandsym P((char const*,struct buf*,RILE*)); struct hshentry *genrevs P((char const*,char const*,char const*,char const*,struct hshentries**)); unsigned countnumflds P((char const*)); void getbranchno P((char const*,struct buf*)); X X/* rcssyn */ X/* These expand modes must agree with Expand_names[] in rcssyn.c. */ X#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */ X#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */ X#define KEY_EXPAND 2 /* -kk `$Keyword$' */ X#define VAL_EXPAND 3 /* -kv `value' */ X#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */ struct diffcmd { X unsigned long X line1, /* number of first line */ X nlines, /* number of lines affected */ X adprev, /* previous 'a' line1+1 or 'd' line1 */ X dafter; /* sum of previous 'd' line1 and previous 'd' nlines */ X}; extern char const * Dbranch; extern struct access * AccessList; extern struct assoc * Symbols; extern struct cbuf Comment; extern struct lock * Locks; extern struct hshentry * Head; extern int Expand; extern int StrictLocks; extern unsigned TotalDeltas; extern char const *const expand_names[]; extern char const Kdesc[]; extern char const Klog[]; extern char const Ktext[]; int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*)); int putdftext P((char const*,struct cbuf,RILE*,FILE*,int)); int putdtext P((char const*,struct cbuf,char const*,FILE*,int)); int str2expmode P((char const*)); void getadmin P((void)); void getdesc P((int)); void gettree P((void)); void ignorephrase P((void)); void initdiffcmd P((struct diffcmd*)); void putadmin P((FILE*)); void putstring P((FILE*,int,struct cbuf,int)); void puttree P((struct hshentry const*,FILE*)); X X/* rcsutil */ extern int RCSversion; char *cgetenv P((char const*)); char *fstr_save P((char const*)); char *str_save P((char const*)); char const *date2str P((char const[datesize],char[datesize])); char const *getusername P((int)); int getRCSINIT P((int,char**,char***)); int run P((char const*,char const*,...)); int runv P((char const**)); malloc_type fremember P((malloc_type)); malloc_type ftestalloc P((size_t)); malloc_type testalloc P((size_t)); malloc_type testrealloc P((malloc_type,size_t)); X#define ftalloc(T) ftnalloc(T,1) X#define talloc(T) tnalloc(T,1) X#if lint X extern malloc_type lintalloc; X# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0) X# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0) X# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p) X# define tfree(p) X#else X# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n))) X# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n))) X# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n))) X# define tfree(p) free((malloc_type)(p)) X#endif void awrite P((char const*,size_t,FILE*)); void fastcopy P((RILE*,FILE*)); void ffree P((void)); void ffree1 P((char const*)); void setRCSversion P((char const*)); X#if has_signal X void catchints P((void)); X void ignoreints P((void)); X void restoreints P((void)); X#else X# define catchints() X# define ignoreints() X# define restoreints() X#endif X#if has_getuid X uid_t ruid P((void)); X# define myself(u) ((u) == ruid()) X#else X# define myself(u) true X#endif X#if has_setuid X uid_t euid P((void)); X void nosetid P((void)); X void seteid P((void)); X void setrid P((void)); X#else X# define nosetid() X# define seteid() X# define setrid() X#endif END_OF_FILE if test 22152 -ne `wc -c <'src/rcsbase.h'`; then echo shar: \"'src/rcsbase.h'\" unpacked with wrong size! fi # end of 'src/rcsbase.h' fi if test -f 'src/rcsrev.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rcsrev.c'\" else echo shar: Extracting \"'src/rcsrev.c'\" \(21002 characters\) sed "s/^X//" >'src/rcsrev.c' <<'END_OF_FILE' X/* X * RCS revision number handling X */ X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990, 1991 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X This file is part of RCS. X RCS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. X RCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. X You should have received a copy of the GNU General Public License along with RCS; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X Report problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X X/* $Log: rcsrev.c,v $ X * Revision 5.3 1991/08/19 03:13:55 eggert X * Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune. X * X * Revision 5.2 1991/04/21 11:58:28 eggert X * Add tiprev(). X * X * Revision 5.1 1991/02/25 07:12:43 eggert X * Avoid overflow when comparing revision numbers. X * X * Revision 5.0 1990/08/22 08:13:43 eggert X * Remove compile-time limits; use malloc instead. X * Ansify and Posixate. Tune. X * Remove possibility of an internal error. Remove lint. X * X * Revision 4.5 89/05/01 15:13:22 narten X * changed copyright header to reflect current distribution rules X * X * Revision 4.4 87/12/18 11:45:22 narten X * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, X * since there's now a return value there with a value. (Guy Harris) X * X * Revision 4.3 87/10/18 10:38:42 narten X * Updating version numbers. Changes relative to version 1.1 actually X * relative to 4.1 X * X * Revision 1.3 87/09/24 14:00:37 narten X * Sources now pass through lint (if you ignore printf/sprintf/fprintf X * warnings) X * X * Revision 1.2 87/03/27 14:22:37 jenkins X * Port to suns X * X * Revision 4.1 83/03/25 21:10:45 wft X * Only changed $Header to $Id. X * X * Revision 3.4 82/12/04 13:24:08 wft X * Replaced getdelta() with gettree(). X * X * Revision 3.3 82/11/28 21:33:15 wft X * fixed compartial() and compnum() for nil-parameters; fixed nils X * in error messages. Testprogram output shortenend. X * X * Revision 3.2 82/10/18 21:19:47 wft X * renamed compnum->cmpnum, compnumfld->cmpnumfld, X * numericrevno->numricrevno. X * X * Revision 3.1 82/10/11 19:46:09 wft X * changed expandsym() to check for source==nil; returns zero length string X * in that case. X */ X X X X/* X#define REVTEST X*/ X/* version REVTEST is for testing the routines that generate a sequence X * of delta numbers needed to regenerate a given delta. X */ X X#include "rcsbase.h" X libId(revId, "$Id: rcsrev.c,v 5.3 1991/08/19 03:13:55 eggert Exp $") X static char const *branchtip P((char const*)); static struct hshentry *genbranch P((struct hshentry const*,char const*,unsigned,char const*,char const*,char const*,struct hshentries**)); X X X X unsigned countnumflds(s) X char const *s; X/* Given a pointer s to a dotted number (date or revision number), X * countnumflds returns the number of digitfields in s. X */ X{ X register char const *sp; X register unsigned count; X if ((sp=s)==nil) return(0); X if (*sp == '\0') return(0); X count = 1; X do { X if (*sp++ == '.') count++; X } while (*sp); X return(count); X} X X void getbranchno(revno,branchno) X char const *revno; X struct buf *branchno; X/* Given a non-nil revision number revno, getbranchno copies the number of the branch X * on which revno is into branchno. If revno itself is a branch number, X * it is copied unchanged. X */ X{ X register unsigned numflds; X register char *tp; X X bufscpy(branchno, revno); X numflds=countnumflds(revno); X if (!(numflds & 1)) { X tp = branchno->string; X while (--numflds) X while (*tp++ != '.') X ; X *(tp-1)='\0'; X } X} X X X int cmpnum(num1, num2) X char const *num1, *num2; X/* compares the two dotted numbers num1 and num2 lexicographically X * by field. Individual fields are compared numerically. X * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp. X * omitted fields are assumed to be higher than the existing ones. X*/ X{ X register char const *s1, *s2; X register size_t d1, d2; X register int r; X X s1=num1==nil?"":num1; X s2=num2==nil?"":num2; X X for (;;) { X /* Give precedence to shorter one. */ X if (!*s1) X return (unsigned char)*s2; X if (!*s2) X return -1; X X /* Strip leading zeros, then find number of digits. */ X while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; X while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; X X /* Do not convert to integer; it might overflow! */ X if (d1 != d2) X return d1<d2 ? -1 : 1; X if ((r = memcmp(s1, s2, d1))) X return r; X s1 += d1; X s2 += d1; X X /* skip '.' */ X if (*s1) s1++; X if (*s2) s2++; X } X} X X X int cmpnumfld(num1, num2, fld) X char const *num1, *num2; X unsigned fld; X/* Compare the two dotted numbers at field fld. X * num1 and num2 must have at least fld fields. X * fld must be positive. X*/ X{ X register char const *s1, *s2; X register size_t d1, d2; X X s1 = num1; X s2 = num2; X /* skip fld-1 fields */ X while (--fld) { X while (*s1++ != '.') X ; X while (*s2++ != '.') X ; X } X /* Now s1 and s2 point to the beginning of the respective fields */ X while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; X while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; X X return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1; X} X X X static void cantfindbranch(revno, date, author, state) X char const *revno, date[datesize], *author, *state; X{ X char datebuf[datesize]; X X error("No revision on branch %s has%s%s%s%s%s%s.", X revno, X date ? " a date before " : "", X date ? date2str(date,datebuf) : "", X author ? " and author "+(date?0:4) : "", X author ? author : "", X state ? " and state "+(date||author?0:4) : "", X state ? state : "" X ); X} X X static void absent(revno, field) X char const *revno; X unsigned field; X{ X struct buf t; X bufautobegin(&t); X error("%s %s absent", field&1?"revision":"branch", X partialno(&t,revno,field) X ); X bufautoend(&t); X} X X X int compartial(num1, num2, length) X char const *num1, *num2; X unsigned length; X X/* compare the first "length" fields of two dot numbers; X the omitted field is considered to be larger than any number */ X/* restriction: at least one number has length or more fields */ X X{ X register char const *s1, *s2; X register size_t d1, d2; X register int r; X X s1 = num1; s2 = num2; X if (!s1) return 1; X if (!s2) return -1; X X for (;;) { X if (!*s1) return 1; X if (!*s2) return -1; X X while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; X while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; X X if (d1 != d2) X return d1<d2 ? -1 : 1; X if ((r = memcmp(s1, s2, d1))) X return r; X s1 += d1; X s2 += d1; X X if (*s1 == '.') s1++; X if (*s2 == '.') s2++; X X if ( --length == 0 ) return 0; X } X} X X char * partialno(rev1,rev2,length) X struct buf *rev1; X char const *rev2; X register unsigned length; X/* Function: Copies length fields of revision number rev2 into rev1. X * Return rev1's string. X */ X{ X register char *r1; X X bufscpy(rev1, rev2); X r1 = rev1->string; X while (length) { X while (*r1!='.' && *r1) X ++r1; X ++r1; X length--; X } X /* eliminate last '.'*/ X *(r1-1)='\0'; X return rev1->string; X} X X X X X static void store1(store, next) X struct hshentries ***store; X struct hshentry *next; X/* X * Allocate a new list node that addresses NEXT. X * Append it to the list that **STORE is the end pointer of. X */ X{ X register struct hshentries *p; X X p = ftalloc(struct hshentries); X p->first = next; X **store = p; X *store = &p->rest; X} X struct hshentry * genrevs(revno,date,author,state,store) X char const *revno, *date, *author, *state; X struct hshentries **store; X/* Function: finds the deltas needed for reconstructing the X * revision given by revno, date, author, and state, and stores pointers X * to these deltas into a list whose starting address is given by store. X * The last delta (target delta) is returned. X * If the proper delta could not be found, nil is returned. X */ X{ X unsigned length; X register struct hshentry * next; X int result; X char const *branchnum; X struct buf t; X char datebuf[datesize]; X X bufautobegin(&t); X X if (!(next = Head)) { X error("RCS file empty"); X goto norev; X } X X length = countnumflds(revno); X X if (length >= 1) { X /* at least one field; find branch exactly */ X while ((result=cmpnumfld(revno,next->num,1)) < 0) { X store1(&store, next); X next = next->next; X if (!next) { X error("branch number %s too low", partialno(&t,revno,1)); X goto norev; X } X } X X if (result>0) { X absent(revno, 1); X goto norev; X } X } X if (length<=1){ X /* pick latest one on given branch */ X branchnum = next->num; /* works even for empty revno*/ X while ((next!=nil) && X (cmpnumfld(branchnum,next->num,1)==0) && X !( X (date==nil?1:(cmpnum(date,next->date)>=0)) && X (author==nil?1:(strcmp(author,next->author)==0)) && X (state ==nil?1:(strcmp(state, next->state) ==0)) X ) X ) X { X store1(&store, next); X next=next->next; X } X if ((next==nil) || X (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { X cantfindbranch( X length ? revno : partialno(&t,branchnum,1), X date, author, state X ); X goto norev; X } else { X store1(&store, next); X } X *store = nil; X return next; X } X X /* length >=2 */ X /* find revision; may go low if length==2*/ X while ((result=cmpnumfld(revno,next->num,2)) < 0 && X (cmpnumfld(revno,next->num,1)==0) ) { X store1(&store, next); X next = next->next; X if (!next) X break; X } X X if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { X error("revision number %s too low", partialno(&t,revno,2)); X goto norev; X } X if ((length>2) && (result!=0)) { X absent(revno, 2); X goto norev; X } X X /* print last one */ X store1(&store, next); X X if (length>2) X return genbranch(next,revno,length,date,author,state,store); X else { /* length == 2*/ X if ((date!=nil) && (cmpnum(date,next->date)<0)){ X error("Revision %s has date %s.", X next->num, X date2str(next->date, datebuf) X ); X return nil; X } X if ((author!=nil)&&(strcmp(author,next->author)!=0)) { X error("Revision %s has author %s.",next->num,next->author); X return nil; X } X if ((state!=nil)&&(strcmp(state,next->state)!=0)) { X error("Revision %s has state %s.",next->num, X next->state==nil?"<empty>":next->state); X return nil; X } X *store=nil; X return next; X } X X norev: X bufautoend(&t); X return nil; X} X X X X X static struct hshentry * genbranch(bpoint, revno, length, date, author, state, store) X struct hshentry const *bpoint; X char const *revno; X unsigned length; X char const *date, *author, *state; X struct hshentries **store; X/* Function: given a branchpoint, a revision number, date, author, and state, X * genbranch finds the deltas necessary to reconstruct the given revision X * from the branch point on. X * Pointers to the found deltas are stored in a list beginning with store. X * revno must be on a side branch. X * return nil on error X */ X{ X unsigned field; X register struct hshentry * next, * trail; X register struct branchhead const *bhead; X int result; X struct buf t; X char datebuf[datesize]; X X field = 3; X bhead = bpoint->branches; X X do { X if (!bhead) { X bufautobegin(&t); X error("no side branches present for %s", partialno(&t,revno,field-1)); X bufautoend(&t); X return nil; X } X X /*find branch head*/ X /*branches are arranged in increasing order*/ X while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) { X bhead = bhead->nextbranch; X if (!bhead) { X bufautobegin(&t); X error("branch number %s too high",partialno(&t,revno,field)); X bufautoend(&t); X return nil; X } X } X X if (result<0) { X absent(revno, field); X return nil; X } X X next = bhead->hsh; X if (length==field) { X /* pick latest one on that branch */ X trail=nil; X do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) && X (author==nil?1:(strcmp(author,next->author)==0)) && X (state ==nil?1:(strcmp(state, next->state) ==0)) X ) trail = next; X next=next->next; X } while (next!=nil); X X if (trail==nil) { X cantfindbranch(revno, date, author, state); X return nil; X } else { /* print up to last one suitable */ X next = bhead->hsh; X while (next!=trail) { X store1(&store, next); X next=next->next; X } X store1(&store, next); X } X *store = nil; X return next; X } X X /* length > field */ X /* find revision */ X /* check low */ X if (cmpnumfld(revno,next->num,field+1)<0) { X bufautobegin(&t); X error("revision number %s too low", partialno(&t,revno,field+1)); X bufautoend(&t); X return(nil); X } X do { X store1(&store, next); X trail = next; X next = next->next; X } while ((next!=nil) && X (cmpnumfld(revno,next->num,field+1) >=0)); X X if ((length>field+1) && /*need exact hit */ X (cmpnumfld(revno,trail->num,field+1) !=0)){ X absent(revno, field+1); X return(nil); X } X if (length == field+1) { X if ((date!=nil) && (cmpnum(date,trail->date)<0)){ X error("Revision %s has date %s.", X trail->num, X date2str(trail->date, datebuf) X ); X return nil; X } X if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { X error("Revision %s has author %s.",trail->num,trail->author); X return nil; X } X if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { X error("Revision %s has state %s.",trail->num, X trail->state==nil?"<empty>":trail->state); X return nil; X } X } X bhead = trail->branches; X X } while ((field+=2) <= length); X * store = nil; X return trail; X} X X X static char const * lookupsym(id) X char const *id; X/* Function: looks up id in the list of symbolic names starting X * with pointer SYMBOLS, and returns a pointer to the corresponding X * revision number. Returns nil if not present. X */ X{ X register struct assoc const *next; X next = Symbols; X while (next!=nil) { X if (strcmp(id, next->symbol)==0) X return next->num; X else next=next->nextassoc; X } X return nil; X} X int expandsym(source, target) X char const *source; X struct buf *target; X/* Function: Source points to a revision number. Expandsym copies X * the number to target, but replaces all symbolic fields in the X * source number with their numeric values. X * Expand a branch followed by `.' to the latest revision on that branch. X * Ignore `.' after a revision. Remove leading zeros. X * returns false on error; X */ X{ X return fexpandsym(source, target, (RILE*)0); X} X X int fexpandsym(source, target, fp) X char const *source; X struct buf *target; X RILE *fp; X/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */ X{ X register char const *sp, *bp; X register char *tp; X char const *tlim; X register enum tokens d; X unsigned dots; X X sp = source; X bufalloc(target, 1); X tp = target->string; X if (!sp || !*sp) { /*accept nil pointer as a legal value*/ X *tp='\0'; X return true; X } X if (sp[0] == KDELIM && !sp[1]) { X if (!getoldkeys(fp)) X return false; X if (!*prevrev.string) { X error("working file lacks revision number"); X return false; X } X bufscpy(target, prevrev.string); X return true; X } X tlim = tp + target->size; X dots = 0; X X for (;;) { X switch (ctab[(unsigned char)*sp]) { X case DIGIT: X while (*sp=='0' && isdigit(sp[1])) X /* skip leading zeroes */ X sp++; X do { X if (tlim <= tp) X tp = bufenlarge(target, &tlim); X } while (isdigit(*tp++ = *sp++)); X --sp; X tp[-1] = '\0'; X break; X X case LETTER: X case Letter: X { X register char *p = tp; X register size_t s = tp - target->string; X do { X if (tlim <= p) X p = bufenlarge(target, &tlim); X *p++ = *sp++; X } while ((d=ctab[(unsigned char)*sp])==LETTER || X d==Letter || d==DIGIT || X (d==IDCHAR)); X if (tlim <= p) X p = bufenlarge(target, &tlim); X *p = 0; X tp = target->string + s; X } X bp = lookupsym(tp); X if (bp==nil) { X error("Symbolic number %s is undefined.", tp); X return false; X } X do { X if (tlim <= tp) X tp = bufenlarge(target, &tlim); X } while ((*tp++ = *bp++)); X break; X X default: X goto improper; X } X switch (*sp++) { X case '\0': return true; X case '.': break; X default: goto improper; X } X if (!*sp) { X if (dots & 1) X goto improper; X if (!(bp = branchtip(target->string))) X return false; X bufscpy(target, bp); X return true; X } X ++dots; X tp[-1] = '.'; X } X X improper: X error("improper revision number: %s", source); X return false; X} X X static char const * branchtip(branch) X char const *branch; X{ X struct hshentry *h; X struct hshentries *hs; X X h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs); X return h ? h->num : (char const*)0; X} X X char const * tiprev() X{ X return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0; X} X X X X#ifdef REVTEST X char const cmdid[] = "revtest"; X X int main(argc,argv) int argc; char * argv[]; X{ X static struct buf numricrevno; X char symrevno[100]; /* used for input of revision numbers */ X char author[20]; X char state[20]; X char date[20]; X struct hshentries *gendeltas; X struct hshentry * target; X int i; X X if (argc<2) { X aputs("No input file\n",stderr); X exitmain(EXIT_FAILURE); X } X if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { X faterror("can't open input file %s", argv[1]); X } X Lexinit(); X getadmin(); X X gettree(); X X getdesc(false); X X do { X /* all output goes to stderr, to have diagnostics and */ X /* errors in sequence. */ X aputs("\nEnter revision number or <return> or '.': ",stderr); X if (!gets(symrevno)) break; X if (*symrevno == '.') break; X aprintf(stderr,"%s;\n",symrevno); X expandsym(symrevno,&numricrevno); X aprintf(stderr,"expanded number: %s; ",numricrevno.string); X aprintf(stderr,"Date: "); X gets(date); aprintf(stderr,"%s; ",date); X aprintf(stderr,"Author: "); X gets(author); aprintf(stderr,"%s; ",author); X aprintf(stderr,"State: "); X gets(state); aprintf(stderr, "%s;\n", state); X target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil, X *state?state:(char*)nil, &gendeltas); X if (target!=nil) { X while (gendeltas) { X aprintf(stderr,"%s\n",gendeltas->first->num); X gendeltas = gendeltas->next; X } X } X } while (true); X aprintf(stderr,"done\n"); X exitmain(EXIT_SUCCESS); X} X exiting void exiterr() { _exit(EXIT_FAILURE); } X X#endif END_OF_FILE if test 21002 -ne `wc -c <'src/rcsrev.c'`; then echo shar: \"'src/rcsrev.c'\" unpacked with wrong size! fi # end of 'src/rcsrev.c' fi echo shar: End of archive 5 \(of 11\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 11 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0