diff --git a/gnu/usr.bin/rcs/co/co.c b/gnu/usr.bin/rcs/co/co.c index fd6ce5cb8941..4165df2c02c0 100644 --- a/gnu/usr.bin/rcs/co/co.c +++ b/gnu/usr.bin/rcs/co/co.c @@ -1,831 +1,826 @@ /* Check out working files from revisions of RCS files. */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.18 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.17 1995/06/01 16:23:43 eggert * (main, preparejoin): Pass argument instead of using `join' static variable. * (main): Add -kb. * * Revision 5.16 1994/03/17 14:05:48 eggert * Move buffer-flushes out of critical sections, since they aren't critical. * Use ORCSerror to clean up after a fatal error. Remove lint. * Specify subprocess input via file descriptor, not file name. * * Revision 5.15 1993/11/09 17:40:15 eggert * -V now prints version on stdout and exits. Don't print usage twice. * * Revision 5.14 1993/11/03 17:42:27 eggert * Add -z. Generate a value for the Name keyword. * Don't arbitrarily limit the number of joins. * Improve quality of diagnostics. * * Revision 5.13 1992/07/28 16:12:44 eggert * Add -V. Check that working and RCS files are distinct. * * Revision 5.12 1992/02/17 23:02:08 eggert * Add -T. * * Revision 5.11 1992/01/24 18:44:19 eggert * Add support for bad_creat0. lint -> RCS_lint * * Revision 5.10 1992/01/06 02:42:34 eggert * Update usage string. * * Revision 5.9 1991/10/07 17:32:46 eggert * -k affects just working file, not RCS file. * * Revision 5.8 1991/08/19 03:13:55 eggert * Warn before removing somebody else's file. * Add -M. Fix co -j bugs. Tune. * * Revision 5.7 1991/04/21 11:58:15 eggert * Ensure that working file is newer than RCS file after co -[lu]. * Add -x, RCSINIT, MS-DOS support. * * Revision 5.6 1990/12/04 05:18:38 eggert * Don't checkaccesslist() unless necessary. * Use -I for prompts and -q for diagnostics. * * Revision 5.5 1990/11/01 05:03:26 eggert * Fix -j. Add -I. * * Revision 5.4 1990/10/04 06:30:11 eggert * Accumulate exit status across files. * * Revision 5.3 1990/09/11 02:41:09 eggert * co -kv yields a readonly working file. * * Revision 5.2 1990/09/04 08:02:13 eggert * Standardize yes-or-no procedure. * * Revision 5.0 1990/08/22 08:10:02 eggert * Permit multiple locks by same user. Add setuid support. * Remove compile-time limits; use malloc instead. * Permit dates past 1999/12/31. Switch to GMT. * Make lock and temp files faster and safer. * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. * * Revision 4.7 89/05/01 15:11:41 narten * changed copyright header to reflect current distribution rules * * Revision 4.6 88/08/09 19:12:15 eggert * Fix "co -d" core dump; rawdate wasn't always initialized. * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint * * Revision 4.5 87/12/18 11:35:40 narten * lint cleanups (from Guy Harris) * * Revision 4.4 87/10/18 10:20:53 narten * Updating version numbers changes relative to 1.1, are actually * relative to 4.2 * * Revision 1.3 87/09/24 13:58:30 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 1.2 87/03/27 14:21:38 jenkins * Port to suns * * Revision 4.2 83/12/05 13:39:48 wft * made rewriteflag external. * * Revision 4.1 83/05/10 16:52:55 wft * Added option -u and -f. * Added handling of default branch. * Replaced getpwuid() with getcaller(). * Removed calls to stat(); now done by pairfilenames(). * Changed and renamed rmoldfile() to rmworkfile(). * Replaced catchints() calls with restoreints(), unlink()--link() with rename(); * * Revision 3.7 83/02/15 15:27:07 wft * Added call to fastcopy() to copy remainder of RCS file. * * Revision 3.6 83/01/15 14:37:50 wft * Added ignoring of interrupts while RCS file is renamed; this avoids * deletion of RCS files during the unlink/link window. * * Revision 3.5 82/12/08 21:40:11 wft * changed processing of -d to use DATEFORM; removed actual from * call to preparejoin; re-fixed printing of done at the end. * * Revision 3.4 82/12/04 18:40:00 wft * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. * Fixed printing of "done". * * Revision 3.3 82/11/28 22:23:11 wft * Replaced getlogin() with getpwuid(), flcose() with ffclose(), * %02d with %.2d, mode generation for working file with WORKMODE. * Fixed nil printing. Fixed -j combined with -l and -p, and exit * for non-existing revisions in preparejoin(). * * Revision 3.2 82/10/18 20:47:21 wft * Mode of working file is now maintained even for co -l, but write permission * is removed. * The working file inherits its mode from the RCS file, plus write permission * for the owner. The write permission is not given if locking is strict and * co does not lock. * An existing working file without write permission is deleted automatically. * Otherwise, co asks (empty answer: abort co). * Call to getfullRCSname() added, check for write error added, call * for getlogin() fixed. * * Revision 3.1 82/10/13 16:01:30 wft * fixed type of variables receiving from getc() (char -> int). * removed unused variables. */ #include "rcsbase.h" static char *addjoin P((char*)); static char const *getancestor P((char const*,char const*)); static int buildjoin P((char const*)); static int preparejoin P((char*)); static int rmlock P((struct hshentry const*)); static int rmworkfile P((void)); static void cleanup P((void)); static char const quietarg[] = "-q"; -static char const *expandarg, *suffixarg, *versionarg, *zonearg, *incexcarg; +static char const *expandarg, *suffixarg, *versionarg, *zonearg; static char const **joinlist; /* revisions to be joined */ static int joinlength; 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; -mainProg(coId, "co", "$Id$") +mainProg(coId, "co", "$Id: co.c,v 1.8 1997/02/22 15:47:21 peter Exp $") { static char const cmdusage[] = "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ..."; char *a, *joinflag, **newargv; char const *author, *date, *rev, *state; char const *joinname, *newdate, *neworkname; int changelock; /* 1 if a lock has been changed, -1 if error */ int expmode, r, tostdout, workstatstat; int Ttimeflag; struct buf numericrev; /* expanded revision number */ char finaldate[datesize]; # if OPEN_O_BINARY int stdout_mode = 0; # endif setrid(); author = date = rev = state = 0; joinflag = 0; bufautobegin(&numericrev); expmode = -1; suffixes = X_DEFAULT; tostdout = false; Ttimeflag = false; argc = getRCSINIT(argc, argv, &newargv); argv = newargv; while (a = *++argv, 0<--argc && *a++=='-') { switch (*a++) { case 'r': revno: if (*a) { if (rev) warn("redefinition of revision number"); rev = a; } break; case 'f': forceflag=true; goto revno; case 'l': if (lockflag < 0) { warn("-u overridden by -l."); } lockflag = 1; goto revno; case 'u': if (0 < lockflag) { warn("-l overridden by -u."); } lockflag = -1; goto revno; case 'p': tostdout = true; goto revno; case 'I': interactiveflag = true; goto revno; case 'q': quietflag=true; goto revno; case 'd': if (date) redefined('d'); str2date(a, finaldate); date=finaldate; break; case 'j': if (*a) { if (joinflag) redefined('j'); joinflag = a; } break; case 'M': mtimeflag = true; goto revno; case 's': if (*a) { if (state) redefined('s'); state = a; } break; case 'T': if (*a) goto unknown; Ttimeflag = true; break; case 'w': if (author) redefined('w'); if (*a) author = a; else author = getcaller(); break; case 'x': suffixarg = *argv; suffixes = a; break; case 'V': versionarg = *argv; setRCSversion(versionarg); break; case 'z': zonearg = *argv; zone_set(a); break; - case 'K': /* set keyword inclusions/exclusions */ - incexcarg = *argv; - setIncExc(incexcarg); - break; - case 'k': /* set keyword expand mode */ expandarg = *argv; if (0 <= expmode) redefined('k'); if (0 <= (expmode = str2expmode(a))) break; /* fall into */ default: unknown: error("unknown option: %s%s", *argv, cmdusage); }; } /* end of option processing */ /* Now handle all pathnames. */ if (nerror) cleanup(); else if (argc < 1) faterror("no input file%s", cmdusage); else for (; 0 < argc; cleanup(), ++argv, --argc) { ffree(); if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) continue; /* * RCSname contains the name of the RCS file, and finptr * points at it. workname contains the name of the working file. * Also, RCSstat has been set. */ diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); workstatstat = -1; if (tostdout) { # if OPEN_O_BINARY int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0; if (stdout_mode != newmode) { stdout_mode = newmode; oflush(); VOID setmode(STDOUT_FILENO, newmode); } # endif neworkname = 0; neworkptr = workstdout = stdout; } else { workstatstat = stat(workname, &workstat); if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) { rcserror("RCS file is the same as working file %s.", workname ); continue; } neworkname = makedirtemp(1); if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) { if (errno == EACCES) workerror("permission denied on parent directory"); else eerror(neworkname); continue; } } gettree(); /* reads in the delta tree */ if (!Head) { /* no revisions; create empty file */ diagnose("no revisions present; generating empty revision 0.0\n"); if (lockflag) warn( "no revisions, so nothing can be %slocked", lockflag < 0 ? "un" : "" ); Ozclose(&fcopy); if (workstatstat == 0) if (!rmworkfile()) continue; changelock = 0; newdate = 0; } else { int locks = lockflag ? findlock(false, &targetdelta) : 0; if (rev) { /* expand symbolic revision number */ if (!expandsym(rev, &numericrev)) continue; } else { switch (locks) { default: continue; case 0: bufscpy(&numericrev, Dbranch?Dbranch:""); break; case 1: bufscpy(&numericrev, targetdelta->num); break; } } /* get numbers of deltas to be generated */ if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) continue; /* check reservations */ changelock = lockflag < 0 ? rmlock(targetdelta) : lockflag == 0 ? 0 : addlock(targetdelta, true); if ( changelock < 0 || (changelock && !checkaccesslist()) || dorewrite(lockflag, changelock) != 0 ) continue; if (0 <= expmode) Expand = expmode; if (0 < lockflag && Expand == VAL_EXPAND) { rcserror("cannot combine -kv and -l"); continue; } if (joinflag && !preparejoin(joinflag)) continue; diagnose("revision %s%s\n",targetdelta->num, 0name = namedrev(rev, targetdelta); joinname = buildrevision( gendeltas, targetdelta, joinflag&&tostdout ? (FILE*)0 : neworkptr, Expand < MIN_UNEXPAND ); # if !large_memory if (fcopy == neworkptr) fcopy = 0; /* Don't close it twice. */ # endif if_advise_access(changelock && gendeltas->first!=targetdelta, finptr, MADV_SEQUENTIAL ); if (donerewrite(changelock, Ttimeflag ? RCSstat.st_mtime : (time_t)-1 ) != 0) continue; if (changelock) { locks += lockflag; if (1 < locks) rcswarn("You now have %d locks.", locks); } newdate = targetdelta->date; if (joinflag) { newdate = 0; if (!joinname) { aflush(neworkptr); joinname = neworkname; } if (Expand == BINARY_EXPAND) workerror("merging binary files"); if (!buildjoin(joinname)) continue; } } if (!tostdout) { mode_t m = WORKMODE(RCSstat.st_mode, ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks)) ); time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1; aflush(neworkptr); ignoreints(); r = chnamemod(&neworkptr, neworkname, workname, 1, m, t); keepdirtemp(neworkname); restoreints(); if (r != 0) { eerror(workname); error("see %s", neworkname); continue; } diagnose("done\n"); } } tempunlink(); Ofclose(workstdout); exitmain(exitstatus); } /* end of main (co) */ static void cleanup() { if (nerror) exitstatus = EXIT_FAILURE; Izclose(&finptr); ORCSclose(); # if !large_memory if (fcopy!=workstdout) Ozclose(&fcopy); # endif if (neworkptr!=workstdout) Ozclose(&neworkptr); dirtempunlink(); } #if RCS_lint # define exiterr coExit #endif void exiterr() { ORCSerror(); dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); } /***************************************************************** * The following routines are auxiliary routines *****************************************************************/ static int rmworkfile() /* * Prepare to remove workname, if it exists, and if * it is read-only. * Otherwise (file writable): * if !quietmode asks the user whether to really delete it (default: fail); * otherwise failure. * Returns true if permission is gotten. */ { if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { /* File is writable */ if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", workname, myself(workstat.st_uid) ? "" : ", and you do not own it" )) { error(!quietflag && ttystdin() ? "checkout aborted" : "writable %s exists; checkout aborted", workname); return false; } } /* Actual unlink is done later by caller. */ return true; } static int rmlock(delta) struct hshentry const *delta; /* Function: removes the lock held by caller on delta. * Returns -1 if someone else holds the lock, * 0 if there is no lock on delta, * and 1 if a lock was found and removed. */ { register struct rcslock * next, * trail; char const *num; struct rcslock dummy; int whomatch, nummatch; num=delta->num; dummy.nextlock=next=Locks; trail = &dummy; while (next) { whomatch = strcmp(getcaller(), next->login); nummatch=strcmp(num,next->delta->num); if ((whomatch==0) && (nummatch==0)) break; /*found a lock on delta by caller*/ if ((whomatch!=0)&&(nummatch==0)) { rcserror("revision %s locked by %s; use co -r or rcs -u", num, next->login ); return -1; } trail=next; next=next->nextlock; } if (next) { /*found one; delete it */ trail->nextlock=next->nextlock; Locks=dummy.nextlock; next->delta->lockedby = 0; return 1; /*success*/ } else return 0; /*no lock on delta*/ } /***************************************************************** * The rest of the routines are for handling joins *****************************************************************/ static char * addjoin(joinrev) char *joinrev; /* Add joinrev's number to joinlist, yielding address of char past joinrev, * or 0 if no such revision exists. */ { register char *j; register struct hshentry *d; char terminator; struct buf numrev; struct hshentries *joindeltas; j = joinrev; for (;;) { switch (*j++) { default: continue; case 0: case ' ': case '\t': case '\n': case ':': case ',': case ';': break; } break; } terminator = *--j; *j = 0; bufautobegin(&numrev); d = 0; if (expandsym(joinrev, &numrev)) d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas); bufautoend(&numrev); *j = terminator; if (d) { joinlist[++lastjoin] = d->num; return j; } return 0; } static int preparejoin(j) register char *j; /* Parse join list J and place pointers to the * revision numbers into joinlist. */ { lastjoin= -1; for (;;) { while ((*j==' ')||(*j=='\t')||(*j==',')) j++; if (*j=='\0') break; if (lastjoin>=joinlength-2) { joinlist = (joinlength *= 2) == 0 ? tnalloc(char const *, joinlength = 16) : trealloc(char const *, joinlist, joinlength); } if (!(j = addjoin(j))) return false; while ((*j==' ') || (*j=='\t')) j++; if (*j == ':') { j++; while((*j==' ') || (*j=='\t')) j++; if (*j!='\0') { if (!(j = addjoin(j))) return false; } else { rcsfaterror("join pair incomplete"); } } else { if (lastjoin==0) { /* first pair */ /* common ancestor missing */ joinlist[1]=joinlist[0]; lastjoin=1; /*derive common ancestor*/ if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) return false; } else { rcsfaterror("join pair incomplete"); } } } if (lastjoin < 1) rcsfaterror("empty join"); return true; } static char const * getancestor(r1, r2) char const *r1, *r2; /* Yield the common ancestor of r1 and r2 if successful, 0 otherwise. * Work reliably only if r1 and r2 are not branch numbers. */ { static struct buf t1, t2; int l1, l2, l3; char const *r; l1 = countnumflds(r1); l2 = countnumflds(r2); if ((22 ? 2 : l1); VOID partialno(&t2, r2, l2>2 ? 2 : l2); r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) return r; } else if (cmpnumfld(r1, r2, l3+1)!=0) return partialno(&t1,r1,l3); } rcserror("common ancestor of %s and %s undefined", r1, r2); return 0; } static int buildjoin(initialfile) char const *initialfile; /* Function: merge pairs of elements in joinlist into initialfile * If workstdout is set, copy result to stdout. * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). */ { struct buf commarg; struct buf subs; char const *rev2, *rev3; int i; char const *cov[10], *mergev[11]; char const **p; bufautobegin(&commarg); bufautobegin(&subs); rev2 = maketemp(0); rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ cov[1] = CO; /* cov[2] setup below */ p = &cov[3]; if (expandarg) *p++ = expandarg; if (suffixarg) *p++ = suffixarg; if (versionarg) *p++ = versionarg; if (zonearg) *p++ = zonearg; *p++ = quietarg; *p++ = RCSname; *p = 0; mergev[1] = MERGE; mergev[2] = mergev[4] = "-L"; /* rest of mergev setup below */ i=0; while (inum); else { bufscat(&subs, ","); bufscat(&subs, joinlist[i-2]); bufscat(&subs, ":"); bufscat(&subs, joinlist[i-1]); } diagnose("revision %s\n",joinlist[i]); bufscpy(&commarg, "-p"); bufscat(&commarg, joinlist[i]); cov[2] = commarg.string; if (runv(-1, rev2, cov)) goto badmerge; diagnose("revision %s\n",joinlist[i+1]); bufscpy(&commarg, "-p"); bufscat(&commarg, joinlist[i+1]); cov[2] = commarg.string; if (runv(-1, rev3, cov)) goto badmerge; diagnose("merging...\n"); mergev[3] = subs.string; mergev[5] = joinlist[i+1]; p = &mergev[6]; if (quietflag) *p++ = quietarg; if (lastjoin<=i+2 && workstdout) *p++ = "-p"; *p++ = initialfile; *p++ = rev2; *p++ = rev3; *p = 0; switch (runv(-1, (char*)0, mergev)) { case DIFF_FAILURE: case DIFF_SUCCESS: break; default: goto badmerge; } i=i+2; } bufautoend(&commarg); bufautoend(&subs); return true; badmerge: nerror++; bufautoend(&commarg); bufautoend(&subs); return false; } diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile index 0170d4a2f712..d3792325a0fb 100644 --- a/gnu/usr.bin/rcs/lib/Makefile +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -1,14 +1,14 @@ # Define FSYNC_ALL to get slower but safer writes in case of crashes in # the middle of CVS/RCS changes #CFLAGS += -DFSYNC_ALL LIB = rcs SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcstime.c \ rcsutil.c merger.c version.c -NOPROFILE=noprofile - -install: +NOPROFILE= noprofile +INTERNALLIB= true +INTERNALSTATICLIB= true .include diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h index ee2eafbfa5ae..918f1b9a470d 100644 --- a/gnu/usr.bin/rcs/lib/rcsbase.h +++ b/gnu/usr.bin/rcs/lib/rcsbase.h @@ -1,757 +1,762 @@ /* RCS common definitions and data structures */ -#define RCSBASE "$Id$" +#define RCSBASE "$Id: rcsbase.h,v 1.8 1997/02/22 15:47:33 peter Exp $" /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.20 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.19 1995/06/01 16:23:43 eggert * (SIZEABLE_PATH): Don't depend on PATH_MAX: it's not worth configuring. * (Ioffset_type,BINARY_EXPAND,MIN_UNEXPAND,MIN_UNCHANGED_EXPAND): New macros. * (maps_memory): New macro; replaces many instances of `has_mmap'. * (cacheptr): Renamed from cachetell. * (struct RILE): New alternate name for RILE; the type is now recursive. * (deallocate): New member for RILE, used for generic buffer deallocation. * (cacheunget_): No longer take a failure arg; just call Ierror on failure. * (struct rcslock): Renamed from struct lock, to avoid collisions with * system headers on some hosts. All users changed. * (basefilename): Renamed from basename, likewise. * (dirtpname): Remove; no longer external. * (dirlen, dateform): Remove; no longer used. * (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New functions. * (zonelenmax): Increase to 9 for full ISO 8601 format. * (catchmmapints): Depend on has_NFS. * * Revision 5.18 1994/03/17 14:05:48 eggert * Add primitives for reading backwards from a RILE; * this is needed to go back and find the $Log prefix. * Specify subprocess input via file descriptor, not file name. Remove lint. * * Revision 5.17 1993/11/09 17:40:15 eggert * Move RCS-specific time handling into rcstime.c. * printf_string now takes two arguments, alas. * * Revision 5.16 1993/11/03 17:42:27 eggert * Don't arbitrarily limit the number of joins. Remove `nil'. * Add Name keyword. Don't discard ignored phrases. * Add support for merge -A vs -E, and allow up to three labels. * Improve quality of diagnostics and prototypes. * * Revision 5.15 1992/07/28 16:12:44 eggert * Statement macro names now end in _. * * Revision 5.14 1992/02/17 23:02:22 eggert * Add -T support. Work around NFS mmap SIGBUS problem. * * Revision 5.13 1992/01/24 18:44:19 eggert * Add support for bad_creat0. lint -> RCS_lint * * Revision 5.12 1992/01/06 02:42:34 eggert * while (E) ; -> while (E) continue; * * Revision 5.11 1991/10/07 17:32:46 eggert * Support piece tables even if !has_mmap. * * Revision 5.10 1991/09/24 00:28:39 eggert * Remove unexported functions. * * Revision 5.9 1991/08/19 03:13:55 eggert * Add piece tables and other tuneups, and NFS workarounds. * * Revision 5.8 1991/04/21 11:58:20 eggert * Add -x, RCSINIT, MS-DOS support. * * Revision 5.7 1991/02/28 19:18:50 eggert * Try setuid() if seteuid() doesn't work. * * Revision 5.6 1991/02/26 17:48:37 eggert * Support new link behavior. Move ANSI C / Posix declarations into conf.sh. * * Revision 5.5 1990/12/04 05:18:43 eggert * Use -I for prompts and -q for diagnostics. * * Revision 5.4 1990/11/01 05:03:35 eggert * Don't assume that builtins are functions; they may be macros. * Permit arbitrary data in logs. * * Revision 5.3 1990/09/26 23:36:58 eggert * Port wait() to non-Posix ANSI C hosts. * * Revision 5.2 1990/09/04 08:02:20 eggert * Don't redefine NAME_MAX, PATH_MAX. * Improve incomplete line handling. Standardize yes-or-no procedure. * * Revision 5.1 1990/08/29 07:13:53 eggert * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too. * * Revision 5.0 1990/08/22 08:12:44 eggert * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access(). * Remove compile-time limits; use malloc instead. * Ansify and Posixate. Add support for ISO 8859. * Remove snoop and v2 support. * * Revision 4.9 89/05/01 15:17:14 narten * botched previous USG fix * * Revision 4.8 89/05/01 14:53:05 narten * changed #include -> string.h for USG systems. * * Revision 4.7 88/11/08 15:58:45 narten * removed defs for functions loaded from libraries * * Revision 4.6 88/08/09 19:12:36 eggert * Shrink stdio code size; remove lint; permit -Dhshsize=nn. * * Revision 4.5 87/12/18 17:06:41 narten * made removed BSD ifdef, now uses V4_2BSD * * Revision 4.4 87/10/18 10:29:49 narten * Updating version numbers * Changes relative to 1.1 are actually relative to 4.2 * * Revision 1.3 87/09/24 14:02:25 narten * changes for lint * * Revision 1.2 87/03/27 14:22:02 jenkins * Port to suns * * Revision 4.2 83/12/20 16:04:20 wft * merged 3.6.1.1 and 4.1 (SMALLOG, logsize). * moved setting of STRICT_LOCKING to Makefile. * changed DOLLAR to UNKN (conflict with KDELIM). * * Revision 4.1 83/05/04 09:12:41 wft * Added markers Id and RCSfile. * Added Dbranch for default branches. * * Revision 3.6.1.1 83/12/02 21:56:22 wft * Increased logsize, added macro SMALLOG. * * Revision 3.6 83/01/15 16:43:28 wft * 4.2 prerelease * * Revision 3.6 83/01/15 16:43:28 wft * Replaced dbm.h with BYTESIZ, fixed definition of rindex(). * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD. * Added macro DELNUMFORM to have uniform format for printing delta text nodes. * Added macro DELETE to mark deleted deltas. * * Revision 3.5 82/12/10 12:16:56 wft * Added two forms of DATEFORM, one using %02d, the other %.2d. * * Revision 3.4 82/12/04 20:01:25 wft * added LOCKER, Locker, and USG (redefinition of rindex). * * Revision 3.3 82/12/03 12:22:04 wft * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3, * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength * using NCPPN. Changed putc() to abort on write error. * * Revision 3.2 82/10/18 15:03:52 wft * added macro STRICT_LOCKING, removed RCSUMASK. * renamed JOINFILE[1,2] to JOINFIL[1,2]. * * Revision 3.1 82/10/11 19:41:17 wft * removed NBPW, NBPC, NCPW. * added typdef int void to aid compiling */ #include "conf.h" #define EXIT_TROUBLE DIFF_TROUBLE #ifdef _POSIX_PATH_MAX # define SIZEABLE_PATH _POSIX_PATH_MAX #else # define SIZEABLE_PATH 255 /* size of a large path; not a hard limit */ #endif /* for traditional C hosts with unusual size arguments */ #define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f) #define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f) /* * Parameters */ /* backwards compatibility with old versions of RCS */ #define VERSION_min 3 /* old output RCS format supported */ #define VERSION_max 5 /* newest output RCS format supported */ #ifndef VERSION_DEFAULT /* default RCS output format */ # define VERSION_DEFAULT VERSION_max #endif #define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */ #ifndef STRICT_LOCKING #define STRICT_LOCKING 1 #endif /* 0 sets the default locking to non-strict; */ /* used in experimental environments. */ /* 1 sets the default locking to strict; */ /* used in production environments. */ #define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ #define datesize (yearlength+16) /* size of output of time2date */ #define RCSTMPPREFIX '_' /* prefix for temp files in working dir */ #define KDELIM '$' /* delimiter for keywords */ #define VDELIM ':' /* separates keywords from values */ #define DEFAULTSTATE "Exp" /* default state of revisions */ #define true 1 #define false 0 /* * RILE - readonly file * declarecache; - declares local cache for RILE variable(s) * setupcache - sets up the local RILE cache, but does not initialize it * cache, uncache - caches and uncaches the local RILE; * (uncache,cache) is needed around functions that advance the RILE pointer * Igeteof_(f,c,s) - get a char c from f, executing statement s at EOF * cachegeteof_(c,s) - Igeteof_ applied to the local RILE * Iget_(f,c) - like Igeteof_, except EOF is an error * cacheget_(c) - Iget_ applied to the local RILE * cacheunget_(f,c,s) - read c backwards from cached f, executing s at BOF * Ifileno, Ioffset_type, Irewind, Itell - analogs to stdio routines * * By conventions, macros whose names end in _ are statements, not expressions. * Following such macros with `; else' results in a syntax error. */ #define maps_memory (has_map_fd || has_mmap) #if large_memory typedef unsigned char const *Iptr_type; typedef struct RILE { Iptr_type ptr, lim; unsigned char *base; /* not Iptr_type for lint's sake */ unsigned char *readlim; int fd; # if maps_memory void (*deallocate) P((struct RILE *)); # else FILE *stream; # endif } RILE; # if maps_memory # define declarecache register Iptr_type ptr, lim # define setupcache(f) (lim = (f)->lim) # define Igeteof_(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++; # define cachegeteof_(c,s) if (ptr==lim) s else (c)= *ptr++; # else int Igetmore P((RILE*)); # define declarecache register Iptr_type ptr; register RILE *rRILE # define setupcache(f) (rRILE = (f)) # define Igeteof_(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++; # define cachegeteof_(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++; # endif # define uncache(f) ((f)->ptr = ptr) # define cache(f) (ptr = (f)->ptr) # define Iget_(f,c) Igeteof_(f,c,Ieof();) # define cacheget_(c) cachegeteof_(c,Ieof();) # define cacheunget_(f,c) (c)=(--ptr)[-1]; # define Ioffset_type size_t # define Itell(f) ((f)->ptr - (f)->base) # define Irewind(f) ((f)->ptr = (f)->base) # define cacheptr() ptr # define Ifileno(f) ((f)->fd) #else # define RILE FILE # define declarecache register FILE *ptr # define setupcache(f) (ptr = (f)) # define uncache(f) # define cache(f) # define Igeteof_(f,c,s) {if(((c)=getc(f))==EOF){testIerror(f);if(feof(f))s}} # define cachegeteof_(c,s) Igeteof_(ptr,c,s) # define Iget_(f,c) { if (((c)=getc(f))==EOF) testIeof(f); } # define cacheget_(c) Iget_(ptr,c) # define cacheunget_(f,c) if(fseek(ptr,-2L,SEEK_CUR))Ierror();else cacheget_(c) # define Ioffset_type long # define Itell(f) ftell(f) # define Ifileno(f) fileno(f) #endif /* Print a char, but abort on write error. */ #define aputc_(c,o) { if (putc(c,o)==EOF) testOerror(o); } /* Get a character from an RCS file, perhaps copying to a new RCS file. */ #define GETCeof_(o,c,s) { cachegeteof_(c,s) if (o) aputc_(c,o) } #define GETC_(o,c) { cacheget_(c) if (o) aputc_(c,o) } #define WORKMODE(RCSmode, writable) (((RCSmode)&(mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH)) | ((writable)?S_IWUSR:0)) /* computes mode of working file: same as RCSmode, but write permission */ /* determined by writable */ /* character classes and token codes */ enum tokens { /* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter, PERIOD, SBEGIN, SPACE, UNKN, /* tokens */ COLON, ID, NUM, SEMI, STRING }; #define SDELIM '@' /* the actual character is needed for string handling*/ /* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN. * there should be no overlap among SDELIM, KDELIM, and VDELIM */ #define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ /*************************************** * Data structures for the symbol table ***************************************/ /* Buffer of arbitrary data */ struct buf { char *string; size_t size; }; struct cbuf { char const *string; size_t size; }; /* Hash table entry */ struct hshentry { char const * num; /* pointer to revision number (ASCIZ) */ char const * date; /* pointer to date of checkin */ char const * author; /* login of person checking in */ char const * lockedby; /* who locks the revision */ char const * state; /* state of revision (Exp by default) */ char const * name; /* name (if any) by which retrieved */ struct cbuf log; /* log message requested at checkin */ struct branchhead * branches; /* list of first revisions on branches*/ struct cbuf ig; /* ignored phrases in admin part */ struct cbuf igtext; /* ignored phrases in deltatext part */ struct hshentry * next; /* next revision on same branch */ struct hshentry * nexthsh; /* next revision with same hash value */ long insertlns;/* lines inserted (computed by rlog) */ long deletelns;/* lines deleted (computed by rlog) */ char selector; /* true if selected, false if deleted */ }; /* list of hash entries */ struct hshentries { struct hshentries *rest; struct hshentry *first; }; /* list element for branch lists */ struct branchhead { struct hshentry * hsh; struct branchhead * nextbranch; }; /* accesslist element */ struct access { char const * login; struct access * nextaccess; }; /* list element for locks */ struct rcslock { char const * login; struct hshentry * delta; struct rcslock * nextlock; }; /* list element for symbolic names */ struct assoc { char const * symbol; char const * num; struct assoc * nextassoc; }; #define mainArgs (argc,argv) int argc; char **argv; #if RCS_lint # define libId(name,rcsid) # define mainProg(name,cmd,rcsid) int name mainArgs #else # define libId(name,rcsid) char const name[] = rcsid; # define mainProg(n,c,i) char const Copyright[] = "Copyright 1982,1988,1989 Walter F. Tichy, Purdue CS\nCopyright 1990,1991,1992,1993,1994,1995 Paul Eggert", baseid[] = RCSBASE, cmdid[] = c; libId(n,i) int main P((int,char**)); int main mainArgs #endif /* * Markers for keyword expansion (used in co and ident) * Every byte must have class LETTER or Letter. */ #define AUTHOR "Author" #define DATE "Date" #define HEADER "Header" #define IDH "Id" #define LOCKER "Locker" #define LOG "Log" #define NAME "Name" #define RCSFILE "RCSfile" #define REVISION "Revision" #define SOURCE "Source" #define STATE "State" -#define FREEBSD "FreeBSD" -#define keylength 8 /* max length of any of the above keywords */ +#define CVSHEADER "CVSHeader" +#define keylength 9 /* max length of any of the above keywords */ enum markers { Nomatch, Author, Date, Header, Id, - Locker, Log, Name, RCSfile, Revision, Source, State, FreeBSD }; + Locker, Log, Name, RCSfile, Revision, Source, State, CVSHeader, + LocalId }; /* This must be in the same order as rcskeys.c's Keyword[] array. */ #define DELNUMFORM "\n\n%s\n%s\n" /* used by putdtext and scanlogtext */ #define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */ /* main program */ extern char const cmdid[]; void exiterr P((void)) exiting; /* merge */ int merge P((int,char const*,char const*const[3],char const*const[3])); /* rcsedit */ #define ciklogsize 23 /* sizeof("checked in with -k by ") */ extern FILE *fcopy; extern char const *resultname; extern char const ciklog[ciklogsize]; extern int locker_expansion; RILE *rcswriteopen P((struct buf*,struct stat*,int)); char const *makedirtemp P((int)); char const *getcaller P((void)); int addlock P((struct hshentry*,int)); int addsymbol P((char const*,char const*,int)); int checkaccesslist P((void)); int chnamemod P((FILE**,char const*,char const*,int,mode_t,time_t)); int donerewrite P((int,time_t)); int dorewrite P((int,int)); int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int)); int findlock P((int,struct hshentry**)); int setmtime P((char const*,time_t)); void ORCSclose P((void)); void ORCSerror P((void)); 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*)); #if has_NFS || bad_unlink int un_link P((char const*)); #else # define un_link(s) unlink(s) #endif #if large_memory void edit_string P((void)); # define editstring(delta) edit_string() #else void editstring P((struct hshentry const*)); #endif /* rcsfcmp */ int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); /* rcsfnms */ #define bufautobegin(b) clear_buf(b) #define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0)) extern FILE *workstdout; extern char *workname; extern char const *RCSname; extern char const *suffixes; extern int fdlock; extern struct stat RCSstat; RILE *rcsreadopen P((struct buf*,struct stat*,int)); char *bufenlarge P((struct buf*,char const**)); char const *basefilename P((char const*)); char const *getfullRCSname P((void)); +char const *getfullCVSname P((void)); char const *maketemp P((int)); char const *rcssuffix P((char const*)); int pairnames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); 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)); /* rcsgen */ extern int interactiveflag; extern struct buf curlogbuf; char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int)); int getcstdin P((void)); int putdtext P((struct hshentry const*,char const*,FILE*,int)); int ttystdin P((void)); int yesorno P((int,char const*,...)) printf_string(2,3); struct cbuf cleanlogmsg P((char*,size_t)); struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*)); void putdesc P((int,char*)); void putdftext P((struct hshentry const*,RILE*,FILE*,int)); /* rcskeep */ extern int prevkeys; extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate; int getoldkeys P((RILE*)); /* rcskeys */ -extern char const *const Keyword[]; +extern char const *Keyword[]; +extern enum markers LocalIdMode; enum markers trymatch P((char const*)); +void setRCSLocalId(char const *); +void setIncExc(char const *); /* 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 long rcsline; char const *getid P((void)); void efaterror P((char const*)) exiting; void enfaterror P((int,char const*)) exiting; void fatcleanup P((int)) exiting; void faterror P((char const*,...)) printf_string_exiting(1,2); void fatserror P((char const*,...)) printf_string_exiting(1,2); void rcsfaterror P((char const*,...)) printf_string_exiting(1,2); void Ieof P((void)) exiting; void Ierror P((void)) exiting; void Oerror P((void)) exiting; char *checkid P((char*,int)); char *checksym 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 Orewind P((FILE*)); void Ozclose P((FILE**)); void aflush P((FILE*)); void afputc P((int,FILE*)); void aprintf P((FILE*,char const*,...)) printf_string(2,3); void aputs P((char const*,FILE*)); void checksid P((char*)); void checkssym P((char*)); void diagnose P((char const*,...)) printf_string(1,2); void eerror P((char const*)); void eflush P((void)); void enerror P((int,char const*)); void error P((char const*,...)) printf_string(1,2); 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 rcserror P((char const*,...)) printf_string(1,2); void rcswarn P((char const*,...)) printf_string(1,2); void testIerror P((FILE*)); void testOerror P((FILE*)); void warn P((char const*,...)) printf_string(1,2); void warnignore P((void)); void workerror P((char const*,...)) printf_string(1,2); void workwarn P((char const*,...)) printf_string(1,2); #if has_madvise && has_mmap && large_memory void advise_access P((RILE*,int)); # define if_advise_access(p,f,advice) if (p) advise_access(f,advice) #else # define advise_access(f,advice) # define if_advise_access(p,f,advice) #endif #if large_memory && maps_memory RILE *I_open P((char const*,struct stat*)); # define Iopen(f,m,s) I_open(f,s) #else RILE *Iopen P((char const*,char const*,struct stat*)); #endif #if !large_memory void testIeof P((FILE*)); void Irewind P((RILE*)); #endif /* rcsmap */ extern enum tokens const ctab[]; /* rcsrev */ char *partialno P((struct buf*,char const*,int)); char const *namedrev P((char const*,struct hshentry*)); char const *tiprev P((void)); int cmpdate P((char const*,char const*)); int cmpnum P((char const*,char const*)); int cmpnumfld P((char const*,char const*,int)); int compartial P((char const*,char const*,int)); 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**)); int countnumflds P((char const*)); void getbranchno P((char const*,struct buf*)); /* rcssyn */ /* These expand modes must agree with Expand_names[] in rcssyn.c. */ #define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */ #define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */ #define KEY_EXPAND 2 /* -kk `$Keyword$' */ #define VAL_EXPAND 3 /* -kv `value' */ #define OLD_EXPAND 4 /* -ko use old string, omitting expansion */ #define BINARY_EXPAND 5 /* -kb like -ko, but use binary mode I/O */ #define MIN_UNEXPAND OLD_EXPAND /* min value for no logical expansion */ #define MIN_UNCHANGED_EXPAND (OPEN_O_BINARY ? BINARY_EXPAND : OLD_EXPAND) /* min value guaranteed to yield an identical file */ struct diffcmd { long line1, /* number of first line */ nlines, /* number of lines affected */ adprev, /* previous 'a' line1+1 or 'd' line1 */ dafter; /* sum of previous 'd' line1 and previous 'd' nlines */ }; extern char const * Dbranch; extern struct access * AccessList; extern struct assoc * Symbols; extern struct cbuf Comment; extern struct cbuf Ignored; extern struct rcslock *Locks; extern struct hshentry * Head; extern int Expand; extern int StrictLocks; extern int TotalDeltas; extern char const *const expand_names[]; extern char const Kaccess[], Kauthor[], Kbranch[], Kcomment[], Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[], Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[]; void unexpected_EOF P((void)) exiting; int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*)); int str2expmode P((char const*)); void getadmin P((void)); void getdesc P((int)); void gettree P((void)); void ignorephrases P((char const*)); void initdiffcmd P((struct diffcmd*)); void putadmin P((void)); void putstring P((FILE*,int,struct cbuf,int)); void puttree P((struct hshentry const*,FILE*)); /* rcstime */ #define zonelenmax 9 /* maxiumum length of time zone string, e.g. "+12:34:56" */ char const *date2str P((char const[datesize],char[datesize + zonelenmax])); time_t date2time P((char const[datesize])); void str2date P((char const*,char[datesize])); void time2date P((time_t,char[datesize])); void zone_set P((char const*)); /* rcsutil */ extern int RCSversion; FILE *fopenSafer P((char const*,char const*)); char *cgetenv P((char const*)); char *fstr_save P((char const*)); char *str_save P((char const*)); char const *getusername P((int)); int fdSafer P((int)); int getRCSINIT P((int,char**,char***)); int run P((int,char const*,...)); int runv P((int,char const*,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)); #define ftalloc(T) ftnalloc(T,1) #define talloc(T) tnalloc(T,1) #if RCS_lint extern malloc_type lintalloc; # define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0) # define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0) # define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p) # define tfree(p) #else # define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n))) # define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n))) # define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n))) # define tfree(p) free((malloc_type)(p)) #endif time_t now P((void)); 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*)); #if has_signal void catchints P((void)); void ignoreints P((void)); void restoreints P((void)); #else # define catchints() # define ignoreints() # define restoreints() #endif #if has_mmap && large_memory # if has_NFS && mmap_signal void catchmmapints P((void)); void readAccessFilenameBuffer P((char const*,unsigned char const*)); # else # define catchmmapints() # endif #endif #if has_getuid uid_t ruid P((void)); # define myself(u) ((u) == ruid()) #else # define myself(u) true #endif #if has_setuid uid_t euid P((void)); void nosetid P((void)); void seteid P((void)); void setrid P((void)); #else # define nosetid() # define seteid() # define setrid() #endif /* version */ extern char const RCS_version_string[]; diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c index dd13f0bf6a5f..e3f714bc840f 100644 --- a/gnu/usr.bin/rcs/lib/rcsedit.c +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -1,1949 +1,1952 @@ /* RCS stream editor */ /****************************************************************************** * edits the input file according to a * script from stdin, generated by diff -n * performs keyword expansion ****************************************************************************** */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.19 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.18 1995/06/01 16:23:43 eggert * (dirtpname): No longer external. * (do_link): Simplify logic. * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. * (fopen_update_truncate): Replace `#if' with `if'. * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. * * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output * at the end of incomplete lines. * * (keyreplace): Do not assume that seeking backwards * at the start of a file will fail; on some systems it succeeds. * Convert C- and Pascal-style comment starts to ` *' in comment leader. * * (rcswriteopen): Use fdSafer to get safer file descriptor. * Open RCS file with FOPEN_RB. * * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. * Fall back on chmod if fchmod fails, since it might be ENOSYS. * * (aflush): Move to rcslex.c. * * Revision 5.17 1994/03/20 04:52:58 eggert * Normally calculate the $Log prefix from context, not from RCS file. * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. * * Revision 5.16 1993/11/03 17:42:27 eggert * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. * Escape white space, $, and \ in keyword string file names. * Don't output 2 spaces between date and time after Log. * * Revision 5.15 1992/07/28 16:12:44 eggert * Some hosts have readlink but not ELOOP. Avoid `unsigned'. * Preserve dates more systematically. Statement macro names now end in _. * * Revision 5.14 1992/02/17 23:02:24 eggert * Add -T support. * * Revision 5.13 1992/01/24 18:44:19 eggert * Add support for bad_chmod_close, bad_creat0. * * Revision 5.12 1992/01/06 02:42:34 eggert * Add setmode parameter to chnamemod. addsymbol now reports changes. * while (E) ; -> while (E) continue; * * Revision 5.11 1991/11/03 01:11:44 eggert * Move the warning about link breaking to where they're actually being broken. * * Revision 5.10 1991/10/07 17:32:46 eggert * Support piece tables even if !has_mmap. Fix rare NFS bugs. * * Revision 5.9 1991/09/17 19:07:40 eggert * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. * * Revision 5.8 1991/08/19 03:13:55 eggert * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. * * Revision 5.7 1991/04/21 11:58:21 eggert * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. * * Revision 5.6 1991/02/25 07:12:40 eggert * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. * * Revision 5.5 1990/12/30 05:07:35 eggert * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). * * Revision 5.4 1990/11/01 05:03:40 eggert * Permit arbitrary data in comment leaders. * * Revision 5.3 1990/09/11 02:41:13 eggert * Tune expandline(). * * Revision 5.2 1990/09/04 08:02:21 eggert * Count RCS lines better. Improve incomplete line handling. * * Revision 5.1 1990/08/29 07:13:56 eggert * Add -kkvl. * Fix bug when getting revisions to files ending in incomplete lines. * Fix bug in comment leader expansion. * * Revision 5.0 1990/08/22 08:12:47 eggert * Don't require final newline. * Don't append "checked in with -k by " to logs, * so that checking in a program with -k doesn't change it. * Don't generate trailing white space for empty comment leader. * Remove compile-time limits; use malloc instead. Add -k, -V. * Permit dates past 1999/12/31. Make lock and temp files faster and safer. * Ansify and Posixate. Check diff's output. * * Revision 4.8 89/05/01 15:12:35 narten * changed copyright header to reflect current distribution rules * * Revision 4.7 88/11/08 13:54:14 narten * misplaced semicolon caused infinite loop * * Revision 4.6 88/08/09 19:12:45 eggert * Shrink stdio code size; allow cc -R. * * Revision 4.5 87/12/18 11:38:46 narten * Changes from the 43. version. Don't know the significance of the * first change involving "rewind". Also, additional "lint" cleanup. * (Guy Harris) * * Revision 4.4 87/10/18 10:32:21 narten * Updating version numbers. Changes relative to version 1.1 actually * relative to 4.1 * * Revision 1.4 87/09/24 13:59:29 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 1.3 87/09/15 16:39:39 shepler * added an initializatin of the variables editline and linecorr * this will be done each time a file is processed. * (there was an obscure bug where if co was used to retrieve multiple files * it would dump) * fix attributed to Roy Morris @FileNet Corp ...!felix!roy * * Revision 1.2 87/03/27 14:22:17 jenkins * Port to suns * * Revision 4.1 83/05/12 13:10:30 wft * Added new markers Id and RCSfile; added locker to Header and Id. * Overhauled expandline completely() (problem with $01234567890123456789@). * Moved trymatch() and marker table to rcskeys.c. * * Revision 3.7 83/05/12 13:04:39 wft * Added retry to expandline to resume after failed match which ended in $. * Fixed truncation problem for $19chars followed by@@. * Log no longer expands full path of RCS file. * * Revision 3.6 83/05/11 16:06:30 wft * added retry to expandline to resume after failed match which ended in $. * Fixed truncation problem for $19chars followed by@@. * * Revision 3.5 82/12/04 13:20:56 wft * Added expansion of keyword Locker. * * Revision 3.4 82/12/03 12:26:54 wft * Added line number correction in case editing does not start at the * beginning of the file. * Changed keyword expansion to always print a space before closing KDELIM; * Expansion for Header shortened. * * Revision 3.3 82/11/14 14:49:30 wft * removed Suffix from keyword expansion. Replaced fclose with ffclose. * keyreplace() gets log message from delta, not from curlogmsg. * fixed expression overflow in while(c=putc(GETC.... * checked nil printing. * * Revision 3.2 82/10/18 21:13:39 wft * I added checks for write errors during the co process, and renamed * expandstring() to xpandstring(). * * Revision 3.1 82/10/13 15:52:55 wft * changed type of result of getc() from char to int. * made keyword expansion loop in expandline() portable to machines * without sign-extension. */ #include "rcsbase.h" -libId(editId, "$Id$") +libId(editId, "$Id: rcsedit.c,v 1.8 1997/02/22 15:47:35 peter Exp $") static void editEndsPrematurely P((void)) exiting; static void editLineNumberOverflow P((void)) exiting; static void escape_string P((FILE*,char const*)); static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); FILE *fcopy; /* result file descriptor */ char const *resultname; /* result pathname */ int locker_expansion; /* should the locker name be appended to Id val? */ #if !large_memory static RILE *fedit; /* edit file descriptor */ static char const *editname; /* edit pathname */ #endif static long editline; /* edit line counter; #lines before cursor */ static long linecorr; /* #adds - #deletes in each edit run. */ /*used to correct editline in case file is not rewound after */ /* applying one delta */ /* indexes into dirtpname */ #define lockdirtp_index 0 #define newRCSdirtp_index bad_creat0 #define newworkdirtp_index (newRCSdirtp_index+1) #define DIRTEMPNAMES (newworkdirtp_index + 1) enum maker {notmade, real, effective}; static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ #define lockname (dirtpname[lockdirtp_index].string) #define newRCSname (dirtpname[newRCSdirtp_index].string) #if has_NFS || bad_unlink int un_link(s) char const *s; /* * Remove S, even if it is unwritable. * Ignore unlink() ENOENT failures; NFS generates bogus ones. */ { # if bad_unlink if (unlink(s) == 0) return 0; else { int e = errno; /* * Forge ahead even if errno == ENOENT; some completely * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT * even for existing unwritable files. */ if (chmod(s, S_IWUSR) != 0) { errno = e; return -1; } } # endif # if has_NFS return unlink(s)==0 || errno==ENOENT ? 0 : -1; # else return unlink(s); # endif } #endif #if !has_rename # if !has_NFS # define do_link(s,t) link(s,t) # else static int do_link P((char const*,char const*)); static int do_link(s, t) char const *s, *t; /* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ { int r = link(s, t); if (r != 0 && errno == EEXIST) { struct stat sb, tb; if ( stat(s, &sb) == 0 && stat(t, &tb) == 0 && same_file(sb, tb, 0) ) r = 0; errno = EEXIST; } return r; } # endif #endif static void editEndsPrematurely() { fatserror("edit script ends prematurely"); } static void editLineNumberOverflow() { fatserror("edit script refers to line past end of file"); } #if large_memory #if has_memmove # define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) #else static void movelines P((Iptr_type*,Iptr_type const*,long)); static void movelines(s1, s2, n) register Iptr_type *s1; register Iptr_type const *s2; register long n; { if (s1 < s2) do { *s1++ = *s2++; } while (--n); else { s1 += n; s2 += n; do { *--s1 = *--s2; } while (--n); } } #endif static void deletelines P((long,long)); static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); static void insertline P((long,Iptr_type)); static void snapshotline P((FILE*,Iptr_type)); /* * `line' contains pointers to the lines in the currently `edited' file. * It is a 0-origin array that represents linelim-gapsize lines. * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. * line[gap .. gap+gapsize-1] contains garbage. * * Any @s in lines are duplicated. * Lines are terminated by \n, or (for a last partial line only) by single @. */ static Iptr_type *line; static size_t gap, gapsize, linelim; static void insertline(n, l) long n; Iptr_type l; /* Before line N, insert line L. N is 0-origin. */ { if (linelim-gapsize < n) editLineNumberOverflow(); if (!gapsize) line = !linelim ? tnalloc(Iptr_type, linelim = gapsize = 1024) : ( gap = gapsize = linelim, trealloc(Iptr_type, line, linelim <<= 1) ); if (n < gap) movelines(line+n+gapsize, line+n, gap-n); else if (gap < n) movelines(line+gap, line+gap+gapsize, n-gap); line[n] = l; gap = n + 1; gapsize--; } static void deletelines(n, nlines) long n, nlines; /* Delete lines N through N+NLINES-1. N is 0-origin. */ { long l = n + nlines; if (linelim-gapsize < l || l < n) editLineNumberOverflow(); if (l < gap) movelines(line+l+gapsize, line+l, gap-l); else if (gap < n) movelines(line+gap, line+gap+gapsize, n-gap); gap = n; gapsize += nlines; } static void snapshotline(f, l) register FILE *f; register Iptr_type l; { register int c; do { if ((c = *l++) == SDELIM && *l++ != SDELIM) return; aputc_(c, f) } while (c != '\n'); } void snapshotedit(f) FILE *f; /* Copy the current state of the edits to F. */ { register Iptr_type *p, *lim, *l=line; for (p=l, lim=l+gap; pptr = l; if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) faterror("finisheditline internal error"); } void finishedit(delta, outfile, done) struct hshentry const *delta; FILE *outfile; int done; /* * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. * But do nothing unless DONE is set (which means we are on the last pass). */ { if (done) { openfcopy(outfile); outfile = fcopy; if (!delta) snapshotedit(outfile); else { register Iptr_type *p, *lim, *l = line; register RILE *fin = finptr; Iptr_type here = fin->ptr; for (p=l, lim=l+gap; pptr = here; } } } /* Open a temporary NAME for output, truncating any previous contents. */ # define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) #else /* !large_memory */ static FILE * fopen_update_truncate P((char const*)); static FILE * fopen_update_truncate(name) char const *name; { if (bad_fopen_wplus && un_link(name) != 0) efaterror(name); return fopenSafer(name, FOPEN_WPLUS_WORK); } #endif void openfcopy(f) FILE *f; { if (!(fcopy = f)) { if (!resultname) resultname = maketemp(2); if (!(fcopy = fopen_update_truncate(resultname))) efaterror(resultname); } } #if !large_memory static void swapeditfiles P((FILE*)); static void swapeditfiles(outfile) FILE *outfile; /* Function: swaps resultname and editname, assigns fedit=fcopy, * and rewinds fedit for reading. Set fcopy to outfile if nonnull; * otherwise, set fcopy to be resultname opened for reading and writing. */ { char const *tmpptr; editline = 0; linecorr = 0; Orewind(fcopy); fedit = fcopy; tmpptr=editname; editname=resultname; resultname=tmpptr; openfcopy(outfile); } void snapshotedit(f) FILE *f; /* Copy the current state of the edits to F. */ { finishedit((struct hshentry *)0, (FILE*)0, false); fastcopy(fedit, f); Irewind(fedit); } void finishedit(delta, outfile, done) struct hshentry const *delta; FILE *outfile; int done; /* copy the rest of the edit file and close it (if it exists). * if delta, perform keyword substitution at the same time. * If DONE is set, we are finishing the last pass. */ { register RILE *fe; register FILE *fc; fe = fedit; if (fe) { fc = fcopy; if (delta) { while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) ; } else { fastcopy(fe,fc); } Ifclose(fe); } if (!done) swapeditfiles(outfile); } #endif #if large_memory # define copylines(upto,delta) (editline = (upto)) #else static void copylines P((long,struct hshentry const*)); static void copylines(upto, delta) register long upto; struct hshentry const *delta; /* * Copy input lines editline+1..upto from fedit to fcopy. * If delta, keyword expansion is done simultaneously. * editline is updated. Rewinds a file only if necessary. */ { register int c; declarecache; register FILE *fc; register RILE *fe; if (upto < editline) { /* swap files */ finishedit((struct hshentry *)0, (FILE*)0, false); /* assumes edit only during last pass, from the beginning*/ } fe = fedit; fc = fcopy; if (editline < upto) if (delta) do { if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) editLineNumberOverflow(); } while (++editline < upto); else { setupcache(fe); cache(fe); do { do { cachegeteof_(c, editLineNumberOverflow();) aputc_(c, fc) } while (c != '\n'); } while (++editline < upto); uncache(fe); } } #endif void xpandstring(delta) struct hshentry const *delta; /* Function: Reads a string terminated by SDELIM from finptr and writes it * to fcopy. Double SDELIM is replaced with single SDELIM. * Keyword expansion is performed with data from delta. * If foutptr is nonnull, the string is also copied unchanged to foutptr. */ { while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) continue; } void copystring() /* Function: copies a string terminated with a single SDELIM from finptr to * fcopy, replacing all double SDELIM with a single SDELIM. * If foutptr is nonnull, the string also copied unchanged to foutptr. * editline is incremented by the number of lines copied. * Assumption: next character read is first string character. */ { register c; declarecache; register FILE *frew, *fcop; register int amidline; register RILE *fin; fin = finptr; setupcache(fin); cache(fin); frew = foutptr; fcop = fcopy; amidline = false; for (;;) { GETC_(frew,c) switch (c) { case '\n': ++editline; ++rcsline; amidline = false; break; case SDELIM: GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; editline += amidline; uncache(fin); return; } /* fall into */ default: amidline = true; break; } aputc_(c,fcop) } } void enterstring() /* Like copystring, except the string is put into the edit data structure. */ { #if !large_memory editname = 0; fedit = 0; editline = linecorr = 0; resultname = maketemp(1); if (!(fcopy = fopen_update_truncate(resultname))) efaterror(resultname); copystring(); #else register int c; declarecache; register FILE *frew; register long e, oe; register int amidline, oamidline; register Iptr_type optr; register RILE *fin; e = 0; gap = 0; gapsize = linelim; fin = finptr; setupcache(fin); cache(fin); advise_access(fin, MADV_NORMAL); frew = foutptr; amidline = false; for (;;) { optr = cacheptr(); GETC_(frew,c) oamidline = amidline; oe = e; switch (c) { case '\n': ++e; ++rcsline; amidline = false; break; case SDELIM: GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; editline = e + amidline; linecorr = 0; uncache(fin); return; } /* fall into */ default: amidline = true; break; } if (!oamidline) insertline(oe, optr); } #endif } void #if large_memory edit_string() #else editstring(delta) struct hshentry const *delta; #endif /* * Read an edit script from finptr and applies it to the edit file. #if !large_memory * The result is written to fcopy. * If delta, keyword expansion is performed simultaneously. * If running out of lines in fedit, fedit and fcopy are swapped. * editname is the name of the file that goes with fedit. #endif * If foutptr is set, the edit script is also copied verbatim to foutptr. * Assumes that all these files are open. * resultname is the name of the file that goes with fcopy. * Assumes the next input character from finptr is the first character of * the edit script. Resets nextc on exit. */ { int ed; /* editor command */ register int c; declarecache; register FILE *frew; # if !large_memory register FILE *f; long line_lim = LONG_MAX; register RILE *fe; # endif register long i; register RILE *fin; # if large_memory register long j; # endif struct diffcmd dc; editline += linecorr; linecorr=0; /*correct line number*/ frew = foutptr; fin = finptr; setupcache(fin); initdiffcmd(&dc); while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) #if !large_memory if (line_lim <= dc.line1) editLineNumberOverflow(); else #endif if (!ed) { copylines(dc.line1-1, delta); /* skip over unwanted lines */ i = dc.nlines; linecorr -= i; editline += i; # if large_memory deletelines(editline+linecorr, i); # else fe = fedit; do { /*skip next line*/ do { Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) } while (c != '\n'); } while (--i); # endif } else { /* Copy lines without deleting any. */ copylines(dc.line1, delta); i = dc.nlines; # if large_memory j = editline+linecorr; # endif linecorr += i; #if !large_memory f = fcopy; if (delta) do { switch (expandline(fin,f,delta,true,frew,true)){ case 0: case 1: if (i==1) return; /* fall into */ case -1: editEndsPrematurely(); } } while (--i); else #endif { cache(fin); do { # if large_memory insertline(j++, cacheptr()); # endif for (;;) { GETC_(frew, c) if (c==SDELIM) { GETC_(frew, c) if (c!=SDELIM) { if (--i) editEndsPrematurely(); nextc = c; uncache(fin); return; } } # if !large_memory aputc_(c, f) # endif if (c == '\n') break; } ++rcsline; } while (--i); uncache(fin); } } } /* The rest is for keyword expansion */ int expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) RILE *infile; FILE *outfile, *frewfile; struct hshentry const *delta; int delimstuffed, dolog; /* * Read a line from INFILE and write it to OUTFILE. * Do keyword expansion with data from DELTA. * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. * If FREWFILE is set, copy the line unchanged to FREWFILE. * DELIMSTUFFED must be true if FREWFILE is set. * Append revision history to log only if DOLOG is set. * Yields -1 if no data is copied, 0 if an incomplete line is copied, * 2 if a complete line is copied; adds 1 to yield if expansion occurred. */ { register c; declarecache; register FILE *out, *frew; register char * tp; register int e, ds, r; char const *tlim; static struct buf keyval; enum markers matchresult; setupcache(infile); cache(infile); out = outfile; frew = frewfile; ds = delimstuffed; bufalloc(&keyval, keylength+3); e = 0; r = -1; for (;;) { if (ds) GETC_(frew, c) else cachegeteof_(c, goto uncache_exit;) for (;;) { switch (c) { case SDELIM: if (ds) { GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; goto uncache_exit; } } /* fall into */ default: aputc_(c,out) r = 0; break; case '\n': rcsline += ds; aputc_(c,out) r = 2; goto uncache_exit; case KDELIM: r = 0; /* check for keyword */ /* first, copy a long enough string into keystring */ tp = keyval.string; *tp++ = KDELIM; for (;;) { if (ds) GETC_(frew, c) else cachegeteof_(c, goto keystring_eof;) if (tp <= &keyval.string[keylength]) switch (ctab[c]) { case LETTER: case Letter: *tp++ = c; continue; default: break; } break; } *tp++ = c; *tp = '\0'; matchresult = trymatch(keyval.string+1); if (matchresult==Nomatch) { tp[-1] = 0; aputs(keyval.string, out); continue; /* last c handled properly */ } /* Now we have a keyword terminated with a K/VDELIM */ if (c==VDELIM) { /* try to find closing KDELIM, and replace value */ tlim = keyval.string + keyval.size; for (;;) { if (ds) GETC_(frew, c) else cachegeteof_(c, goto keystring_eof;) if (c=='\n' || c==KDELIM) break; *tp++ =c; if (tlim <= tp) tp = bufenlarge(&keyval, &tlim); if (c==SDELIM && ds) { /*skip next SDELIM */ GETC_(frew, c) if (c != SDELIM) { /* end of string before closing KDELIM or newline */ nextc = c; goto keystring_eof; } } } if (c!=KDELIM) { /* couldn't find closing KDELIM -- give up */ *tp = 0; aputs(keyval.string, out); continue; /* last c handled properly */ } } /* now put out the new keyword value */ uncache(infile); keyreplace(matchresult, delta, ds, infile, out, dolog); cache(infile); e = 1; break; } break; } } keystring_eof: *tp = 0; aputs(keyval.string, out); uncache_exit: uncache(infile); return r + e; } static void escape_string(out, s) register FILE *out; register char const *s; /* Output to OUT the string S, escaping chars that would break `ci -k'. */ { register char c; for (;;) switch ((c = *s++)) { case 0: return; case '\t': aputs("\\t", out); break; case '\n': aputs("\\n", out); break; case ' ': aputs("\\040", out); break; case KDELIM: aputs("\\044", out); break; case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} /* fall into */ default: aputc_(c, out) break; } } char const ciklog[ciklogsize] = "checked in with -k by "; static void keyreplace(marker, delta, delimstuffed, infile, out, dolog) enum markers marker; register struct hshentry const *delta; int delimstuffed; RILE *infile; register FILE *out; int dolog; /* function: outputs the keyword value(s) corresponding to marker. * Attributes are derived from delta. */ { register char const *sp, *cp, *date; register int c; register size_t cs, cw, ls; char const *sp1; char datebuf[datesize + zonelenmax]; int RCSv; int exp; sp = Keyword[(int)marker]; exp = Expand; date = delta->date; RCSv = RCSversion; if (exp != VAL_EXPAND) aprintf(out, "%c%s", KDELIM, sp); if (exp != KEY_EXPAND) { if (exp != VAL_EXPAND) aprintf(out, "%c%c", VDELIM, marker==Log && RCSvauthor, out); break; case Date: aputs(date2str(date,datebuf), out); break; - case FreeBSD: case Id: + case LocalId: case Header: - escape_string(out, - marker==Id || marker==FreeBSD || RCSvnum, date2str(date, datebuf), delta->author, RCSv==VERSION(3) && delta->lockedby ? "Locked" : delta->state ); if (delta->lockedby) if (VERSION(5) <= RCSv) { if (locker_expansion || exp==KEYVALLOCK_EXPAND) aprintf(out, " %s", delta->lockedby); } else if (RCSv == VERSION(4)) aprintf(out, " Locker: %s", delta->lockedby); break; case Locker: if (delta->lockedby) if ( locker_expansion || exp == KEYVALLOCK_EXPAND || RCSv <= VERSION(4) ) aputs(delta->lockedby, out); break; case Log: case RCSfile: escape_string(out, basefilename(RCSname)); break; case Name: if (delta->name) aputs(delta->name, out); break; case Revision: aputs(delta->num, out); break; case Source: escape_string(out, getfullRCSname()); break; case State: aputs(delta->state, out); break; default: break; } if (exp != VAL_EXPAND) afputc(' ', out); } if (exp != VAL_EXPAND) afputc(KDELIM, out); if (marker == Log && dolog) { struct buf leader; sp = delta->log.string; ls = delta->log.size; if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) return; bufautobegin(&leader); if (RCSversion < VERSION(5)) { cp = Comment.string; cs = Comment.size; } else { int kdelim_found = 0; Ioffset_type chars_read = Itell(infile); declarecache; setupcache(infile); cache(infile); c = 0; /* Pacify `gcc -Wall'. */ /* * Back up to the start of the current input line, * setting CS to the number of characters before `$Log'. */ cs = 0; for (;;) { if (!--chars_read) goto done_backing_up; cacheunget_(infile, c) if (c == '\n') break; if (c == SDELIM && delimstuffed) { if (!--chars_read) break; cacheunget_(infile, c) if (c != SDELIM) { cacheget_(c) break; } } cs += kdelim_found; kdelim_found |= c==KDELIM; } cacheget_(c) done_backing_up:; /* Copy characters before `$Log' into LEADER. */ bufalloc(&leader, cs); cp = leader.string; for (cw = 0; cw < cs; cw++) { leader.string[cw] = c; if (c == SDELIM && delimstuffed) cacheget_(c) cacheget_(c) } /* Convert traditional C or Pascal leader to ` *'. */ for (cw = 0; cw < cs; cw++) if (ctab[(unsigned char) cp[cw]] != SPACE) break; if ( cw+1 < cs && cp[cw+1] == '*' && (cp[cw] == '/' || cp[cw] == '(') ) { size_t i = cw+1; for (;;) if (++i == cs) { warn( "`%c* $Log' is obsolescent; use ` * $Log'.", cp[cw] ); leader.string[cw] = ' '; break; } else if (ctab[(unsigned char) cp[i]] != SPACE) break; } /* Skip `$Log ... $' string. */ do { cacheget_(c) } while (c != KDELIM); uncache(infile); } afputc('\n', out); awrite(cp, cs, out); sp1 = date2str(date, datebuf); if (VERSION(5) <= RCSv) { aprintf(out, "Revision %s %s %s", delta->num, sp1, delta->author ); } else { /* oddity: 2 spaces between date and time, not 1 as usual */ sp1 = strchr(sp1, ' '); aprintf(out, "Revision %s %.*s %s %s", delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author ); } /* Do not include state: it may change and is not updated. */ cw = cs; if (VERSION(5) <= RCSv) for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) continue; for (;;) { afputc('\n', out); awrite(cp, cw, out); if (!ls) break; --ls; c = *sp++; if (c != '\n') { awrite(cp+cw, cs-cw, out); do { afputc(c,out); if (!ls) break; --ls; c = *sp++; } while (c != '\n'); } } bufautoend(&leader); } } #if has_readlink static int resolve_symlink P((struct buf*)); static int resolve_symlink(L) struct buf *L; /* * If L is a symbolic link, resolve it to the name that it points to. * If unsuccessful, set errno and yield -1. * If it points to an existing file, yield 1. * Otherwise, set errno=ENOENT and yield 0. */ { char *b, a[SIZEABLE_PATH]; int e; size_t s; ssize_t r; struct buf bigbuf; int linkcount = MAXSYMLINKS; b = a; s = sizeof(a); bufautobegin(&bigbuf); while ((r = readlink(L->string,b,s)) != -1) if (r == s) { bufalloc(&bigbuf, s<<1); b = bigbuf.string; s = bigbuf.size; } else if (!linkcount--) { # ifndef ELOOP /* * Some pedantic Posix 1003.1-1990 hosts have readlink * but not ELOOP. Approximate ELOOP with EMLINK. */ # define ELOOP EMLINK # endif errno = ELOOP; return -1; } else { /* Splice symbolic link into L. */ b[r] = '\0'; L->string[ ROOTPATH(b) ? 0 : basefilename(L->string) - L->string ] = '\0'; bufscat(L, b); } e = errno; bufautoend(&bigbuf); errno = e; switch (e) { case readlink_isreg_errno: return 1; case ENOENT: return 0; default: return -1; } } #endif RILE * rcswriteopen(RCSbuf, status, mustread) struct buf *RCSbuf; struct stat *status; int mustread; /* * Create the lock file corresponding to RCSBUF. * Then try to open RCSBUF for reading and yield its RILE* descriptor. * Put its status into *STATUS too. * MUSTREAD is true if the file must already exist, too. * If all goes well, discard any previously acquired locks, * and set fdlock to the file descriptor of the RCS lockfile. */ { register char *tp; register char const *sp, *RCSpath, *x; RILE *f; size_t l; int e, exists, fdesc, fdescSafer, r, waslocked; struct buf *dirt; struct stat statbuf; waslocked = 0 <= fdlock; exists = # if has_readlink resolve_symlink(RCSbuf); # else stat(RCSbuf->string, &statbuf) == 0 ? 1 : errno==ENOENT ? 0 : -1; # endif if (exists < (mustread|waslocked)) /* * There's an unusual problem with the RCS file; * or the RCS file doesn't exist, * and we must read or we already have a lock elsewhere. */ return 0; RCSpath = RCSbuf->string; sp = basefilename(RCSpath); l = sp - RCSpath; dirt = &dirtpname[waslocked]; bufscpy(dirt, RCSpath); tp = dirt->string + l; x = rcssuffix(RCSpath); # if has_readlink if (!x) { error("symbolic link to non RCS file `%s'", RCSpath); errno = EINVAL; return 0; } # endif if (*sp == *x) { error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); errno = EINVAL; return 0; } /* Create a lock filename that is a function of the RCS filename. */ if (*x) { /* * The suffix is nonempty. * The lock filename is the first char of of the suffix, * followed by the RCS filename with last char removed. E.g.: * foo,v RCS filename with suffix ,v * ,foo, lock filename */ *tp++ = *x; while (*sp) *tp++ = *sp++; *--tp = 0; } else { /* * The suffix is empty. * The lock filename is the RCS filename * with last char replaced by '_'. */ while ((*tp++ = *sp++)) continue; tp -= 2; if (*tp == '_') { error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); errno = EINVAL; return 0; } *tp = '_'; } sp = dirt->string; f = 0; /* * good news: * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) * is atomic according to Posix 1003.1-1990. * bad news: * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. * good news: * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity * even with NFS. * bad news: * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't * guarantee atomicity. * good news: * Root-over-the-wire NFS access is rare for security reasons. * This bug has never been reported in practice with RCS. * So we don't worry about this bug. * * An even rarer NFS bug can occur when clients retry requests. * This can happen in the usual case of NFS over UDP. * Suppose client A releases a lock by renaming ",f," to "f,v" at * about the same time that client B obtains a lock by creating ",f,", * and suppose A's first rename request is delayed, so A reissues it. * The sequence of events might be: * A sends rename(",f,", "f,v") * B sends create(",f,") * A sends retry of rename(",f,", "f,v") * server receives, does, and acknowledges A's first rename() * A receives acknowledgment, and its RCS program exits * server receives, does, and acknowledges B's create() * server receives, does, and acknowledges A's retry of rename() * This not only wrongly deletes B's lock, it removes the RCS file! * Most NFS implementations have idempotency caches that usually prevent * this scenario, but such caches are finite and can be overrun. * This problem afflicts not only RCS, which uses open() and rename() * to get and release locks; it also afflicts the traditional * Unix method of using link() and unlink() to get and release locks, * and the less traditional method of using mkdir() and rmdir(). * There is no easy workaround. * Any new method based on lockf() seemingly would be incompatible with * the old methods; besides, lockf() is notoriously buggy under NFS. * Since this problem afflicts scads of Unix programs, but is so rare * that nobody seems to be worried about it, we won't worry either. */ # if !open_can_creat # define create(f) creat(f, OPEN_CREAT_READONLY) # else # define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) # endif catchints(); ignoreints(); /* * Create a lock file for an RCS file. This should be atomic, i.e. * if two processes try it simultaneously, at most one should succeed. */ seteid(); fdesc = create(sp); fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ e = errno; setrid(); if (0 <= fdesc) dirtpmaker[0] = effective; if (fdescSafer < 0) { if (e == EACCES && stat(sp,&statbuf) == 0) /* The RCS file is busy. */ e = EEXIST; } else { e = ENOENT; if (exists) { f = Iopen(RCSpath, FOPEN_RB, status); e = errno; if (f && waslocked) { /* Discard the previous lock in favor of this one. */ ORCSclose(); seteid(); r = un_link(lockname); e = errno; setrid(); if (r != 0) enfaterror(e, lockname); bufscpy(&dirtpname[lockdirtp_index], sp); } } fdlock = fdescSafer; } restoreints(); errno = e; return f; } void keepdirtemp(name) char const *name; /* Do not unlink name, either because it's not there any more, * or because it has already been unlinked. */ { register int i; for (i=DIRTEMPNAMES; 0<=--i; ) if (dirtpname[i].string == name) { dirtpmaker[i] = notmade; return; } faterror("keepdirtemp"); } char const * makedirtemp(isworkfile) int isworkfile; /* * Create a unique pathname and store it into dirtpname. * Because of storage in tpnames, dirtempunlink() can unlink the file later. * Return a pointer to the pathname created. * If ISWORKFILE is 1, put it into the working file's directory; * if 0, put the unique file in RCSfile's directory. */ { register char *tp, *np; register size_t dl; register struct buf *bn; register char const *name = isworkfile ? workname : RCSname; dl = basefilename(name) - name; bn = &dirtpname[newRCSdirtp_index + isworkfile]; bufalloc(bn, # if has_mktemp dl + 9 # else strlen(name) + 3 # endif ); bufscpy(bn, name); np = tp = bn->string; tp += dl; *tp++ = '_'; *tp++ = '0'+isworkfile; catchints(); # if has_mktemp VOID strcpy(tp, "XXXXXX"); if (!mktemp(np) || !*np) faterror("can't make temporary pathname `%.*s_%cXXXXXX'", (int)dl, name, '0'+isworkfile ); # else /* * Posix 1003.1-1990 has no reliable way * to create a unique file in a named directory. * We fudge here. If the filename is abcde, * the temp filename is _Ncde where N is a digit. */ name += dl; if (*name) name++; if (*name) name++; VOID strcpy(tp, name); # endif dirtpmaker[newRCSdirtp_index + isworkfile] = real; return np; } void dirtempunlink() /* Clean up makedirtemp() files. May be invoked by signal handler. */ { register int i; enum maker m; for (i = DIRTEMPNAMES; 0 <= --i; ) if ((m = dirtpmaker[i]) != notmade) { if (m == effective) seteid(); VOID un_link(dirtpname[i].string); if (m == effective) setrid(); dirtpmaker[i] = notmade; } } int #if has_prototypes chnamemod( FILE **fromp, char const *from, char const *to, int set_mode, mode_t mode, time_t mtime ) /* The `#if has_prototypes' is needed because mode_t might promote to int. */ #else chnamemod(fromp, from, to, set_mode, mode, mtime) FILE **fromp; char const *from,*to; int set_mode; mode_t mode; time_t mtime; #endif /* * Rename a file (with stream pointer *FROMP) from FROM to TO. * FROM already exists. * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. * If MTIME is not -1, change its mtime to MTIME before renaming. * Close and clear *FROMP before renaming it. * Unlink TO if it already exists. * Return -1 on error (setting errno), 0 otherwise. */ { mode_t mode_while_renaming = mode; int fchmod_set_mode = 0; # if bad_a_rename || bad_NFS_rename struct stat st; if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { if (fstat(fileno(*fromp), &st) != 0) return -1; if (bad_a_rename && set_mode <= 0) mode = st.st_mode; } # endif # if bad_a_rename /* * There's a short window of inconsistency * during which the lock file is writable. */ mode_while_renaming = mode|S_IWUSR; if (mode != mode_while_renaming) set_mode = 1; # endif # if has_fchmod if (0nextlock) if (strcmp(getcaller(), next->login) == 0) { if (found) { rcserror("multiple revisions locked by %s; please specify one", getcaller()); return 2; } found = trail; } if (!found) return 0; next = *found; *target = next->delta; if (delete) { next->delta->lockedby = 0; *found = next->nextlock; } return 1; } int addlock(delta, verbose) struct hshentry * delta; int verbose; /* * Add a lock held by caller to DELTA and yield 1 if successful. * Print an error message if verbose and yield -1 if no lock is added because * DELTA is locked by somebody other than caller. * Return 0 if the caller already holds the lock. */ { register struct rcslock *next; for (next = Locks; next; next = next->nextlock) if (cmpnum(delta->num, next->delta->num) == 0) if (strcmp(getcaller(), next->login) == 0) return 0; else { if (verbose) rcserror("Revision %s is already locked by %s.", delta->num, next->login ); return -1; } next = ftalloc(struct rcslock); delta->lockedby = next->login = getcaller(); next->delta = delta; next->nextlock = Locks; Locks = next; return 1; } int addsymbol(num, name, rebind) char const *num, *name; int rebind; /* * Associate with revision NUM the new symbolic NAME. * If NAME already exists and REBIND is set, associate NAME with NUM; * otherwise, print an error message and return false; * Return -1 if unsuccessful, 0 if no change, 1 if change. */ { register struct assoc *next; for (next = Symbols; next; next = next->nextassoc) if (strcmp(name, next->symbol) == 0) if (strcmp(next->num,num) == 0) return 0; else if (rebind) { next->num = num; return 1; } else { rcserror("symbolic name %s already bound to %s", name, next->num ); return -1; } next = ftalloc(struct assoc); next->symbol = name; next->num = num; next->nextassoc = Symbols; Symbols = next; return 1; } char const * getcaller() /* Get the caller's login name. */ { # if has_setuid return getusername(euid()!=ruid()); # else return getusername(false); # endif } int checkaccesslist() /* * Return true if caller is the superuser, the owner of the * file, the access list is empty, or caller is on the access list. * Otherwise, print an error message and return false. */ { register struct access const *next; if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) return true; next = AccessList; do { if (strcmp(getcaller(), next->login) == 0) return true; } while ((next = next->nextaccess)); rcserror("user %s not on the access list", getcaller()); return false; } int dorewrite(lockflag, changed) int lockflag, changed; /* * Do nothing if LOCKFLAG is zero. * Prepare to rewrite an RCS file if CHANGED is positive. * Stop rewriting if CHANGED is zero, because there won't be any changes. * Fail if CHANGED is negative. * Return 0 on success, -1 on failure. */ { int r = 0, e; if (lockflag) if (changed) { if (changed < 0) return -1; putadmin(); puttree(Head, frewrite); aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); foutptr = frewrite; } else { # if bad_creat0 int nr = !!frewrite, ne = 0; # endif ORCSclose(); seteid(); ignoreints(); # if bad_creat0 if (nr) { nr = un_link(newRCSname); ne = errno; keepdirtemp(newRCSname); } # endif r = un_link(lockname); e = errno; keepdirtemp(lockname); restoreints(); setrid(); if (r != 0) enerror(e, lockname); # if bad_creat0 if (nr != 0) { enerror(ne, newRCSname); r = -1; } # endif } return r; } int donerewrite(changed, newRCStime) int changed; time_t newRCStime; /* * Finish rewriting an RCS file if CHANGED is nonzero. * Set its mode if CHANGED is positive. * Set its modification time to NEWRCSTIME unless it is -1. * Return 0 on success, -1 on failure. */ { int r = 0, e = 0; # if bad_creat0 int lr, le; # endif if (changed && !nerror) { if (finptr) { fastcopy(finptr, frewrite); Izclose(&finptr); } if (1 < RCSstat.st_nlink) rcswarn("breaking hard link"); aflush(frewrite); seteid(); ignoreints(); r = chnamemod( &frewrite, newRCSname, RCSname, changed, RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), newRCStime ); e = errno; keepdirtemp(newRCSname); # if bad_creat0 lr = un_link(lockname); le = errno; keepdirtemp(lockname); # endif restoreints(); setrid(); if (r != 0) { enerror(e, RCSname); error("saved in %s", newRCSname); } # if bad_creat0 if (lr != 0) { enerror(le, lockname); r = -1; } # endif } return r; } void ORCSclose() { if (0 <= fdlock) { if (close(fdlock) != 0) efaterror(lockname); fdlock = -1; } Ozclose(&frewrite); } void ORCSerror() /* * Like ORCSclose, except we are cleaning up after an interrupt or fatal error. * Do not report errors, since this may loop. This is needed only because * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. * This isn't a completely reliable away to work around brain-damaged hosts, * because of the gap between actual file opening and setting frewrite etc., * but it's better than nothing. */ { if (0 <= fdlock) VOID close(fdlock); if (frewrite) /* Avoid fclose, since stdio may not be reentrant. */ VOID close(fileno(frewrite)); } diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c index 8ca86e44b3e4..c44d114803a2 100644 --- a/gnu/usr.bin/rcs/lib/rcsfnms.c +++ b/gnu/usr.bin/rcs/lib/rcsfnms.c @@ -1,1086 +1,1119 @@ /* RCS filename and pathname handling */ /**************************************************************************** * creation and deletion of /tmp temporaries * pairing of RCS pathnames and working pathnames. * Testprogram: define PAIRTEST **************************************************************************** */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.16 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.15 1995/06/01 16:23:43 eggert * (basefilename): Renamed from basename to avoid collisions. * (dirlen): Remove (for similar reasons). * (rcsreadopen): Open with FOPEN_RB. * (SLASHSLASH_is_SLASH): Default is 0. * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug. * * Revision 5.14 1994/03/17 14:05:48 eggert * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint. * * Revision 5.13 1993/11/03 17:42:27 eggert * Determine whether a file name is too long indirectly, * by examining inode numbers, instead of trying to use operating system * primitives like pathconf, which are not trustworthy in general. * File names may now hold white space or $. * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks. * Add getabsname hook. Improve quality of diagnostics. * * Revision 5.12 1992/07/28 16:12:44 eggert * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug. * Check that $PWD is really ".". Be consistent about pathnames vs filenames. * * Revision 5.11 1992/02/17 23:02:25 eggert * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'. * * Revision 5.10 1992/01/24 18:44:19 eggert * Fix bug: Expand and Ignored weren't reinitialized. * Avoid `char const c=ch;' compiler bug. * Add support for bad_creat0. * * Revision 5.9 1992/01/06 02:42:34 eggert * Shorten long (>31 chars) name. * while (E) ; -> while (E) continue; * * Revision 5.8 1991/09/24 00:28:40 eggert * Don't export bindex(). * * Revision 5.7 1991/08/19 03:13:55 eggert * Fix messages when rcswriteopen fails. * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune. * * Revision 5.6 1991/04/21 11:58:23 eggert * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. * * Revision 5.5 1991/02/26 17:48:38 eggert * Fix setuid bug. Support new link behavior. * Define more portable getcwd(). * * Revision 5.4 1990/11/01 05:03:43 eggert * Permit arbitrary data in comment leaders. * * Revision 5.3 1990/09/14 22:56:16 hammer * added more filename extensions and their comment leaders * * Revision 5.2 1990/09/04 08:02:23 eggert * Fix typo when !RCSSEP. * * Revision 5.1 1990/08/29 07:13:59 eggert * Work around buggy compilers with defective argument promotion. * * Revision 5.0 1990/08/22 08:12:50 eggert * Ignore signals when manipulating the semaphore file. * Modernize list of filename extensions. * Permit paths of arbitrary length. Beware filenames beginning with "-". * Remove compile-time limits; use malloc instead. * Permit dates past 1999/12/31. Make lock and temp files faster and safer. * Ansify and Posixate. * Don't use access(). Fix test for non-regular files. Tune. * * Revision 4.8 89/05/01 15:09:41 narten * changed getwd to not stat empty directories. * * Revision 4.7 88/08/09 19:12:53 eggert * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. * * Revision 4.6 87/12/18 11:40:23 narten * additional file types added from 4.3 BSD version, and SPARC assembler * comment character added. Also, more lint cleanups. (Guy Harris) * * Revision 4.5 87/10/18 10:34:16 narten * Updating version numbers. Changes relative to 1.1 actually relative * to verion 4.3 * * Revision 1.3 87/03/27 14:22:21 jenkins * Port to suns * * Revision 1.2 85/06/26 07:34:28 svb * Comment leader '% ' for '*.tex' files added. * * Revision 4.3 83/12/15 12:26:48 wft * Added check for KDELIM in filenames to pairfilenames(). * * Revision 4.2 83/12/02 22:47:45 wft * Added csh, red, and sl filename suffixes. * * Revision 4.1 83/05/11 16:23:39 wft * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): * 1. added copying of path from workfile to RCS file, if RCS file is omitted; * 2. added getting the file status of RCS and working files; * 3. added ignoring of directories. * * Revision 3.7 83/05/11 15:01:58 wft * Added comtable[] which pairs filename suffixes with comment leaders; * updated InitAdmin() accordingly. * * Revision 3.6 83/04/05 14:47:36 wft * fixed Suffix in InitAdmin(). * * Revision 3.5 83/01/17 18:01:04 wft * Added getwd() and rename(); these can be removed by defining * V4_2BSD, since they are not needed in 4.2 bsd. * Changed sys/param.h to sys/types.h. * * Revision 3.4 82/12/08 21:55:20 wft * removed unused variable. * * Revision 3.3 82/11/28 20:31:37 wft * Changed mktempfile() to store the generated filenames. * Changed getfullRCSname() to store the file and pathname, and to * delete leading "../" and "./". * * Revision 3.2 82/11/12 14:29:40 wft * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), * checksuffix(), checkfullpath(). Semaphore name generation updated. * mktempfile() now checks for nil path; freefilename initialized properly. * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. * * Revision 3.1 82/10/18 14:51:28 wft * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). * renamed checkpath() to checkfullpath(). */ #include "rcsbase.h" -libId(fnmsId, "$Id$") +libId(fnmsId, "$Id: rcsfnms.c,v 1.6 1997/02/22 15:47:36 peter Exp $") static char const *bindex P((char const*,int)); static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int)); static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int)); static int suffix_matches P((char const*,char const*)); static size_t dir_useful_len P((char const*)); static size_t suffixlen P((char const*)); static void InitAdmin P((void)); char const *RCSname; char *workname; int fdlock; FILE *workstdout; struct stat RCSstat; char const *suffixes; static char const rcsdir[] = "RCS"; #define rcslen (sizeof(rcsdir)-1) static struct buf RCSbuf, RCSb; static int RCSerrno; /* Temp names to be unlinked when done, if they are not 0. */ #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ static char *volatile tpnames[TEMPNAMES]; struct compair { char const *suffix, *comlead; }; /* * This table is present only for backwards compatibility. * Normally we ignore this table, and use the prefix of the `$Log' line instead. */ static struct compair const comtable[] = { { "a" , "-- " }, /* Ada */ { "ada" , "-- " }, { "adb" , "-- " }, { "ads" , "-- " }, { "asm" , ";; " }, /* assembler (MS-DOS) */ { "bat" , ":: " }, /* batch (MS-DOS) */ { "body", "-- " }, /* Ada */ { "c" , " * " }, /* C */ { "c++" , "// " }, /* C++ in all its infinite guises */ { "cc" , "// " }, { "cpp" , "// " }, { "cxx" , "// " }, { "cl" , ";;; "}, /* Common Lisp */ { "cmd" , ":: " }, /* command (OS/2) */ { "cmf" , "c " }, /* CM Fortran */ { "cs" , " * " }, /* C* */ { "el" , "; " }, /* Emacs Lisp */ { "f" , "c " }, /* Fortran */ { "for" , "c " }, { "h" , " * " }, /* C-header */ { "hpp" , "// " }, /* C++ header */ { "hxx" , "// " }, { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */ { "lisp", ";;; "}, /* Lucid Lisp */ { "lsp" , ";; " }, /* Microsoft Lisp */ { "m" , "// " }, /* Objective C */ { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ { "me" , ".\\\" "}, /* troff -me */ { "ml" , "; " }, /* mocklisp */ { "mm" , ".\\\" "}, /* troff -mm */ { "ms" , ".\\\" "}, /* troff -ms */ { "p" , " * " }, /* Pascal */ { "pas" , " * " }, { "ps" , "% " }, /* PostScript */ { "spec", "-- " }, /* Ada */ { "sty" , "% " }, /* LaTeX style */ { "tex" , "% " }, /* TeX */ { "y" , " * " }, /* yacc */ { 0 , "# " } /* default for unknown suffix; must be last */ }; #if has_mktemp static char const *tmp P((void)); static char const * tmp() /* Yield the name of the tmp directory. */ { static char const *s; if (!s && !(s = cgetenv("TMPDIR")) /* Unix tradition */ && !(s = cgetenv("TMP")) /* DOS tradition */ && !(s = cgetenv("TEMP")) /* another DOS tradition */ ) s = TMPDIR; return s; } #endif char const * maketemp(n) int n; /* Create a unique pathname using n and the process id and store it * into the nth slot in tpnames. * Because of storage in tpnames, tempunlink() can unlink the file later. * Return a pointer to the pathname created. */ { char *p; char const *t = tpnames[n]; if (t) return t; catchints(); { # if has_mktemp char const *tp = tmp(); size_t tplen = dir_useful_len(tp); p = testalloc(tplen + 10); VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); if (!mktemp(p) || !*p) faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", (int)tplen, tp, SLASH, '0'+n ); # else static char tpnamebuf[TEMPNAMES][L_tmpnam]; p = tpnamebuf[n]; if (!tmpnam(p) || !*p) # ifdef P_tmpdir faterror("can't make temporary pathname `%s...'",P_tmpdir); # else faterror("can't make temporary pathname"); # endif # endif } tpnames[n] = p; return p; } void tempunlink() /* Clean up maketemp() files. May be invoked by signal handler. */ { register int i; register char *p; for (i = TEMPNAMES; 0 <= --i; ) if ((p = tpnames[i])) { VOID unlink(p); /* * We would tfree(p) here, * but this might dump core if we're handing a signal. * We're about to exit anyway, so we won't bother. */ tpnames[i] = 0; } } static char const * bindex(sp, c) register char const *sp; register int c; /* Function: Finds the last occurrence of character c in string sp * and returns a pointer to the character just beyond it. If the * character doesn't occur in the string, sp is returned. */ { register char const *r; r = sp; while (*sp) { if (*sp++ == c) r=sp; } return r; } static int suffix_matches(suffix, pattern) register char const *suffix, *pattern; { register int c; if (!pattern) return true; for (;;) switch (*suffix++ - (c = *pattern++)) { case 0: if (!c) return true; break; case 'A'-'a': if (ctab[c] == Letter) break; /* fall into */ default: return false; } } static void InitAdmin() /* function: initializes an admin node */ { register char const *Suffix; register int i; Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; StrictLocks=STRICT_LOCKING; /* guess the comment leader from the suffix*/ Suffix = bindex(workname, '.'); if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) continue; Comment.string = comtable[i].comlead; Comment.size = strlen(comtable[i].comlead); Expand = KEYVAL_EXPAND; clear_buf(&Ignored); Lexinit(); /* note: if !finptr, reads nothing; only initializes */ } void bufalloc(b, size) register struct buf *b; size_t size; /* Ensure *B is a name buffer of at least SIZE bytes. * *B's old contents can be freed; *B's new contents are undefined. */ { if (b->size < size) { if (b->size) tfree(b->string); else b->size = sizeof(malloc_type); while (b->size < size) b->size <<= 1; b->string = tnalloc(char, b->size); } } void bufrealloc(b, size) register struct buf *b; size_t size; /* like bufalloc, except *B's old contents, if any, are preserved */ { if (b->size < size) { if (!b->size) bufalloc(b, size); else { while ((b->size <<= 1) < size) continue; b->string = trealloc(char, b->string, b->size); } } } void bufautoend(b) struct buf *b; /* Free an auto buffer at block exit. */ { if (b->size) tfree(b->string); } struct cbuf bufremember(b, s) struct buf *b; size_t s; /* * Free the buffer B with used size S. * Yield a cbuf with identical contents. * The cbuf will be reclaimed when this input file is finished. */ { struct cbuf cb; if ((cb.size = s)) cb.string = fremember(trealloc(char, b->string, s)); else { bufautoend(b); /* not really auto */ cb.string = ""; } return cb; } char * bufenlarge(b, alim) register struct buf *b; char const **alim; /* Make *B larger. Set *ALIM to its new limit, and yield the relocated value * of its old limit. */ { size_t s = b->size; bufrealloc(b, s + 1); *alim = b->string + b->size; return b->string + s; } void bufscat(b, s) struct buf *b; char const *s; /* Concatenate S to B's end. */ { size_t blen = b->string ? strlen(b->string) : 0; bufrealloc(b, blen+strlen(s)+1); VOID strcpy(b->string+blen, s); } void bufscpy(b, s) struct buf *b; char const *s; /* Copy S into B. */ { bufalloc(b, strlen(s)+1); VOID strcpy(b->string, s); } char const * basefilename(p) char const *p; /* Yield the address of the base filename of the pathname P. */ { register char const *b = p, *q = p; for (;;) switch (*q++) { case SLASHes: b = q; break; case 0: return b; } } static size_t suffixlen(x) char const *x; /* Yield the length of X, an RCS pathname suffix. */ { register char const *p; p = x; for (;;) switch (*p) { case 0: case SLASHes: return p - x; default: ++p; continue; } } char const * rcssuffix(name) char const *name; /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ { char const *x, *p, *nz; size_t nl, xl; nl = strlen(name); nz = name + nl; x = suffixes; do { if ((xl = suffixlen(x))) { if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) return p; } else for (p = name; p < nz - rcslen; p++) if ( isSLASH(p[rcslen]) && (p==name || isSLASH(p[-1])) && memcmp(p, rcsdir, rcslen) == 0 ) return nz; x += xl; } while (*x++); return 0; } /*ARGSUSED*/ RILE * rcsreadopen(RCSpath, status, mustread) struct buf *RCSpath; struct stat *status; int mustread; /* Open RCSPATH for reading and yield its FILE* descriptor. * If successful, set *STATUS to its status. * Pass this routine to pairnames() for read-only access to the file. */ { return Iopen(RCSpath->string, FOPEN_RB, status); } static int finopen(rcsopen, mustread) RILE *(*rcsopen)P((struct buf*,struct stat*,int)); int mustread; /* * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. * Set finptr to the result and yield true if successful. * RCSb holds the file's name. * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. * Yield true if successful or if an unusual failure. */ { int interesting, preferold; /* * We prefer an old name to that of a nonexisting new RCS file, * unless we tried locking the old name and failed. */ preferold = RCSbuf.string[0] && (mustread||0<=fdlock); finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); interesting = finptr || errno!=ENOENT; if (interesting || !preferold) { /* Use the new name. */ RCSerrno = errno; bufscpy(&RCSbuf, RCSb.string); } return interesting; } static int fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) char const *d, *base, *x; size_t dlen, baselen, xlen; RILE *(*rcsopen)P((struct buf*,struct stat*,int)); int mustread; /* * D is a directory name with length DLEN (including trailing slash). * BASE is a filename with length BASELEN. * X is an RCS pathname suffix with length XLEN. * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. * Yield true if successful. * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. * Put these potential names in RCSb. * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. * Yield true if successful or if an unusual failure. */ { register char *p; bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); /* Try dRCS/basex. */ VOID memcpy(p = RCSb.string, d, dlen); VOID memcpy(p += dlen, rcsdir, rcslen); p += rcslen; *p++ = SLASH; VOID memcpy(p, base, baselen); VOID memcpy(p += baselen, x, xlen); p[xlen] = 0; if (xlen) { if (finopen(rcsopen, mustread)) return true; /* Try dbasex. */ /* Start from scratch, because finopen() may have changed RCSb. */ VOID memcpy(p = RCSb.string, d, dlen); VOID memcpy(p += dlen, base, baselen); VOID memcpy(p += baselen, x, xlen); p[xlen] = 0; } return finopen(rcsopen, mustread); } int pairnames(argc, argv, rcsopen, mustread, quiet) int argc; char **argv; RILE *(*rcsopen)P((struct buf*,struct stat*,int)); int mustread, quiet; /* * Pair the pathnames pointed to by argv; argc indicates * how many there are. * Place a pointer to the RCS pathname into RCSname, * and a pointer to the pathname of the working file into workname. * If both are given, and workstdout * is set, a warning is printed. * * If the RCS file exists, places its status into RCSstat. * * If the RCS file exists, it is RCSOPENed for reading, the file pointer * is placed into finptr, and the admin-node is read in; returns 1. * If the RCS file does not exist and MUSTREAD, * print an error unless QUIET and return 0. * Otherwise, initialize the admin node and return -1. * * 0 is returned on all errors, e.g. files that are not regular files. */ { static struct buf tempbuf; register char *p, *arg, *RCS1; char const *base, *RCSbase, *x; int paired; size_t arglen, dlen, baselen, xlen; fdlock = -1; if (!(arg = *argv)) return 0; /* already paired pathname */ if (*arg == '-') { error("%s option is ignored after pathnames", arg); return 0; } base = basefilename(arg); paired = false; /* first check suffix to see whether it is an RCS file or not */ if ((x = rcssuffix(arg))) { /* RCS pathname given */ RCS1 = arg; RCSbase = base; baselen = x - base; if ( 1 < argc && !rcssuffix(workname = p = argv[1]) && baselen <= (arglen = strlen(p)) && ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && memcmp(base, p, baselen) == 0 ) { argv[1] = 0; paired = true; } else { bufscpy(&tempbuf, base); workname = p = tempbuf.string; p[baselen] = 0; } } else { /* working file given; now try to find RCS file */ workname = arg; baselen = strlen(base); /* Derive RCS pathname. */ if ( 1 < argc && (x = rcssuffix(RCS1 = argv[1])) && baselen <= x - RCS1 && ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && memcmp(base, RCSbase, baselen) == 0 ) { argv[1] = 0; paired = true; } else RCSbase = RCS1 = 0; } /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ /* Second, try to find the right RCS file */ if (RCSbase!=RCS1) { /* a path for RCSfile is given; single RCS file to look for */ bufscpy(&RCSbuf, RCS1); finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); RCSerrno = errno; } else { bufscpy(&RCSbuf, ""); if (RCS1) /* RCS filename was given without path. */ VOID fin2open(arg, (size_t)0, RCSbase, baselen, x, strlen(x), rcsopen, mustread ); else { /* No RCS pathname was given. */ /* Try each suffix in turn. */ dlen = base-arg; x = suffixes; while (! fin2open(arg, dlen, base, baselen, x, xlen=suffixlen(x), rcsopen, mustread )) { x += xlen; if (!*x++) break; } } } RCSname = p = RCSbuf.string; if (finptr) { if (!S_ISREG(RCSstat.st_mode)) { error("%s isn't a regular file -- ignored", p); return 0; } Lexinit(); getadmin(); } else { if (RCSerrno!=ENOENT || mustread || fdlock<0) { if (RCSerrno == EEXIST) error("RCS file %s is in use", p); else if (!quiet || RCSerrno!=ENOENT) enerror(RCSerrno, p); return 0; } InitAdmin(); }; if (paired && workstdout) workwarn("Working file ignored due to -p option"); prevkeys = false; return finptr ? 1 : -1; } char const * getfullRCSname() /* * Return a pointer to the full pathname of the RCS file. * Remove leading `./'. */ { if (ROOTPATH(RCSname)) { return RCSname; } else { static struct buf rcsbuf; # if needs_getabsname bufalloc(&rcsbuf, SIZEABLE_PATH + 1); while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0) if (errno == ERANGE) bufalloc(&rcsbuf, rcsbuf.size<<1); else efaterror("getabsname"); # else static char const *wdptr; static struct buf wdbuf; static size_t wdlen; register char const *r; register size_t dlen; register char *d; register char const *wd; if (!(wd = wdptr)) { /* Get working directory for the first time. */ char *PWD = cgetenv("PWD"); struct stat PWDstat, dotstat; if (! ( (d = PWD) && ROOTPATH(PWD) && stat(PWD, &PWDstat) == 0 && stat(".", &dotstat) == 0 && same_file(PWDstat, dotstat, 1) )) { bufalloc(&wdbuf, SIZEABLE_PATH + 1); # if has_getcwd || !has_getwd while (!(d = getcwd(wdbuf.string, wdbuf.size))) if (errno == ERANGE) bufalloc(&wdbuf, wdbuf.size<<1); else if ((d = PWD)) break; else efaterror("getcwd"); # else d = getwd(wdbuf.string); if (!d && !(d = PWD)) efaterror("getwd"); # endif } wdlen = dir_useful_len(d); d[wdlen] = 0; wdptr = wd = d; } /* * Remove leading `./'s from RCSname. * Do not try to handle `../', since removing it may yield * the wrong answer in the presence of symbolic links. */ for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2) /* `.////' is equivalent to `./'. */ while (isSLASH(r[2])) r++; /* Build full pathname. */ dlen = wdlen; bufalloc(&rcsbuf, dlen + strlen(r) + 2); d = rcsbuf.string; VOID memcpy(d, wd, dlen); d += dlen; *d++ = SLASH; VOID strcpy(d, r); # endif return rcsbuf.string; } } + char const * +getfullCVSname() +/* + * Return a pointer to the fill pathname of the RCS file, but trim $CVSROOT. + */ +{ + char const *CVSname; + char const *cvsroot; + int rootlen; + + CVSname = getfullRCSname(); + cvsroot = getenv("CVSROOT"); + + if (cvsroot) { + rootlen = strlen(cvsroot); + /* ignore trailing '/' chars from $CVSROOT */ + while (rootlen > 0) { + if (cvsroot[rootlen - 1] == '/') + rootlen--; + else + break; + } + if (strncmp(CVSname, cvsroot, rootlen) == 0) { + CVSname += rootlen; + /* skip any leading '/' chars */ + while (*CVSname == '/') + CVSname++; + return CVSname; + } + } + return CVSname; +} + static size_t dir_useful_len(d) char const *d; /* * D names a directory; yield the number of characters of D's useful part. * To create a file in D, append a SLASH and a file name to D's useful part. * Ignore trailing slashes if possible; not only are they ugly, * but some non-Posix systems misbehave unless the slashes are omitted. */ { # ifndef SLASHSLASH_is_SLASH # define SLASHSLASH_is_SLASH 0 # endif size_t dlen = strlen(d); if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) --dlen; else while (dlen && isSLASH(d[dlen-1])) --dlen; return dlen; } #ifndef isSLASH int isSLASH(c) int c; { switch (c) { case SLASHes: return true; default: return false; } } #endif #if !has_getcwd && !has_getwd char * getcwd(path, size) char *path; size_t size; { static char const usrbinpwd[] = "/usr/bin/pwd"; # define binpwd (usrbinpwd+4) register FILE *fp; register int c; register char *p, *lim; int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; pid_t child; if (!size) { errno = EINVAL; return 0; } if (pipe(fd) != 0) return 0; # if bad_wait_if_SIGCHLD_ignored # ifndef SIGCHLD # define SIGCHLD SIGCLD # endif VOID signal(SIGCHLD, SIG_DFL); # endif if (!(child = vfork())) { if ( close(fd[0]) == 0 && (fd[1] == STDOUT_FILENO || # ifdef F_DUPFD (VOID close(STDOUT_FILENO), fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) # else dup2(fd[1], STDOUT_FILENO) # endif == STDOUT_FILENO && close(fd[1]) == 0 ) ) { VOID close(STDERR_FILENO); VOID execl(binpwd, binpwd, (char *)0); VOID execl(usrbinpwd, usrbinpwd, (char *)0); } _exit(EXIT_FAILURE); } e = errno; closeerror = close(fd[1]); closeerrno = errno; fp = 0; readerror = toolong = wstatus = 0; p = path; if (0 <= child) { fp = fdopen(fd[0], "r"); e = errno; if (fp) { lim = p + size; for (p = path; ; *p++ = c) { if ((c=getc(fp)) < 0) { if (feof(fp)) break; if (ferror(fp)) { readerror = 1; e = errno; break; } } if (p == lim) { toolong = 1; break; } } } # if has_waitpid if (waitpid(child, &wstatus, 0) < 0) wstatus = 1; # else { pid_t w; do { if ((w = wait(&wstatus)) < 0) { wstatus = 1; break; } } while (w != child); } # endif } if (!fp) { VOID close(fd[0]); errno = e; return 0; } if (fclose(fp) != 0) return 0; if (readerror) { errno = e; return 0; } if (closeerror) { errno = closeerrno; return 0; } if (toolong) { errno = ERANGE; return 0; } if (wstatus || p == path || *--p != '\n') { errno = EACCES; return 0; } *p = '\0'; return path; } #endif #ifdef PAIRTEST /* test program for pairnames() and getfullRCSname() */ char const cmdid[] = "pair"; main(argc, argv) int argc; char *argv[]; { int result; int initflag; quietflag = initflag = false; while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { switch ((*argv)[1]) { case 'p': workstdout = stdout; break; case 'i': initflag=true; break; case 'q': quietflag=true; break; default: error("unknown option: %s", *argv); break; } } do { RCSname = workname = 0; result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); if (result!=0) { diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", RCSname, workname, getfullRCSname() ); } switch (result) { case 0: continue; /* already paired file */ case 1: if (initflag) { rcserror("already exists"); } else { diagnose("RCS file %s exists\n", RCSname); } Ifclose(finptr); break; case -1:diagnose("RCS file doesn't exist\n"); break; } } while (++argv, --argc>=1); } void exiterr() { dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcskeep.c b/gnu/usr.bin/rcs/lib/rcskeep.c index 126e2889a12b..ebc369e3a043 100644 --- a/gnu/usr.bin/rcs/lib/rcskeep.c +++ b/gnu/usr.bin/rcs/lib/rcskeep.c @@ -1,451 +1,452 @@ /* Extract RCS keyword string values from working files. */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.10 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.9 1995/06/01 16:23:43 eggert * (getoldkeys): Don't panic if a Name: is empty. * * Revision 5.8 1994/03/17 14:05:48 eggert * Remove lint. * * Revision 5.7 1993/11/09 17:40:15 eggert * Use simpler timezone parsing strategy now that we're using ISO 8601 format. * * Revision 5.6 1993/11/03 17:42:27 eggert * Scan for Name keyword. Improve quality of diagnostics. * * Revision 5.5 1992/07/28 16:12:44 eggert * Statement macro names now end in _. * * Revision 5.4 1991/08/19 03:13:55 eggert * Tune. * * Revision 5.3 1991/04/21 11:58:25 eggert * Shorten names to keep them distinct on shortname hosts. * * Revision 5.2 1990/10/04 06:30:20 eggert * Parse time zone offsets; future RCS versions may output them. * * Revision 5.1 1990/09/20 02:38:56 eggert * ci -k now checks dates more thoroughly. * * Revision 5.0 1990/08/22 08:12:53 eggert * Retrieve old log message if there is one. * Don't require final newline. * Remove compile-time limits; use malloc instead. Tune. * Permit dates past 1999/12/31. Ansify and Posixate. * * Revision 4.6 89/05/01 15:12:56 narten * changed copyright header to reflect current distribution rules * * Revision 4.5 88/08/09 19:13:03 eggert * Remove lint and speed up by making FILE *fp local, not global. * * Revision 4.4 87/12/18 11:44:21 narten * more lint cleanups (Guy Harris) * * Revision 4.3 87/10/18 10:35:50 narten * Updating version numbers. Changes relative to 1.1 actually relative * to 4.1 * * Revision 1.3 87/09/24 14:00:00 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 1.2 87/03/27 14:22:29 jenkins * Port to suns * * Revision 4.1 83/05/10 16:26:44 wft * Added new markers Id and RCSfile; extraction added. * Marker matching with trymatch(). * * Revision 3.2 82/12/24 12:08:26 wft * added missing #endif. * * Revision 3.1 82/12/04 13:22:41 wft * Initial revision. * */ #include "rcsbase.h" -libId(keepId, "$Id$") +libId(keepId, "$Id: rcskeep.c,v 1.6 1997/02/22 15:47:38 peter Exp $") static int badly_terminated P((void)); static int checknum P((char const*)); static int get0val P((int,RILE*,struct buf*,int)); static int getval P((RILE*,struct buf*,int)); static int keepdate P((RILE*)); static int keepid P((int,RILE*,struct buf*)); static int keeprev P((RILE*)); int prevkeys; struct buf prevauthor, prevdate, prevname, prevrev, prevstate; int getoldkeys(fp) register RILE *fp; /* Function: Tries to read keyword values for author, date, * revision number, and state out of the file fp. * If fp is null, workname is opened and closed instead of using fp. * The results are placed into * prevauthor, prevdate, prevname, prevrev, prevstate. * Aborts immediately if it finds an error and returns false. * If it returns true, it doesn't mean that any of the * values were found; instead, check to see whether the corresponding arrays * contain the empty string. */ { register int c; char keyword[keylength+1]; register char * tp; int needs_closing; int prevname_found; if (prevkeys) return true; needs_closing = false; if (!fp) { if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { eerror(workname); return false; } needs_closing = true; } /* initialize to empty */ bufscpy(&prevauthor, ""); bufscpy(&prevdate, ""); bufscpy(&prevname, ""); prevname_found = 0; bufscpy(&prevrev, ""); bufscpy(&prevstate, ""); c = '\0'; /* anything but KDELIM */ for (;;) { if ( c==KDELIM) { do { /* try to get keyword */ tp = keyword; for (;;) { Igeteof_(fp, c, goto ok;) switch (c) { default: if (keyword+keylength <= tp) break; *tp++ = c; continue; case '\n': case KDELIM: case VDELIM: break; } break; } } while (c==KDELIM); if (c!=VDELIM) continue; *tp = c; Igeteof_(fp, c, break;) switch (c) { case ' ': case '\t': break; default: continue; } switch (trymatch(keyword)) { case Author: if (!keepid(0, fp, &prevauthor)) return false; c = 0; break; case Date: if (!(c = keepdate(fp))) return false; break; case Header: case Id: + case LocalId: if (!( getval(fp, (struct buf*)0, false) && keeprev(fp) && (c = keepdate(fp)) && keepid(c, fp, &prevauthor) && keepid(0, fp, &prevstate) )) return false; /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ if (getval(fp, (struct buf*)0, true) && getval(fp, (struct buf*)0, true)) c = 0; else if (nerror) return false; else c = KDELIM; break; case Locker: (void) getval(fp, (struct buf*)0, false); c = 0; break; case Log: case RCSfile: case Source: if (!getval(fp, (struct buf*)0, false)) return false; c = 0; break; case Name: if (getval(fp, &prevname, false)) { if (*prevname.string) checkssym(prevname.string); prevname_found = 1; } c = 0; break; case Revision: if (!keeprev(fp)) return false; c = 0; break; case State: if (!keepid(0, fp, &prevstate)) return false; c = 0; break; default: continue; } if (!c) Igeteof_(fp, c, c=0;) if (c != KDELIM) { workerror("closing %c missing on keyword", KDELIM); return false; } if (prevname_found && *prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string ) break; } Igeteof_(fp, c, break;) } ok: if (needs_closing) Ifclose(fp); else Irewind(fp); prevkeys = true; return true; } static int badly_terminated() { workerror("badly terminated keyword value"); return false; } static int getval(fp, target, optional) register RILE *fp; struct buf *target; int optional; /* Reads a keyword value from FP into TARGET. * Returns true if one is found, false otherwise. * Does not modify target if it is 0. * Do not report an error if OPTIONAL is set and KDELIM is found instead. */ { int c; Igeteof_(fp, c, return badly_terminated();) return get0val(c, fp, target, optional); } static int get0val(c, fp, target, optional) register int c; register RILE *fp; struct buf *target; int optional; /* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly. * Same as getval, except C is the lookahead character. */ { register char * tp; char const *tlim; register int got1; if (target) { bufalloc(target, 1); tp = target->string; tlim = tp + target->size; } else tlim = tp = 0; got1 = false; for (;;) { switch (c) { default: got1 = true; if (tp) { *tp++ = c; if (tlim <= tp) tp = bufenlarge(target, &tlim); } break; case ' ': case '\t': if (tp) { *tp = 0; # ifdef KEEPTEST VOID printf("getval: %s\n", target); # endif } return got1; case KDELIM: if (!got1 && optional) return false; /* fall into */ case '\n': case 0: return badly_terminated(); } Igeteof_(fp, c, return badly_terminated();) } } static int keepdate(fp) RILE *fp; /* Function: reads a date prevdate; checks format * Return 0 on error, lookahead character otherwise. */ { struct buf prevday, prevtime; register int c; c = 0; bufautobegin(&prevday); if (getval(fp,&prevday,false)) { bufautobegin(&prevtime); if (getval(fp,&prevtime,false)) { Igeteof_(fp, c, c=0;) if (c) { register char const *d = prevday.string, *t = prevtime.string; bufalloc(&prevdate, strlen(d) + strlen(t) + 9); VOID sprintf(prevdate.string, "%s%s %s%s", /* Parse dates put out by old versions of RCS. */ isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) ? "19" : "", d, t, strchr(t,'-') || strchr(t,'+') ? "" : "+0000" ); } } bufautoend(&prevtime); } bufautoend(&prevday); return c; } static int keepid(c, fp, b) int c; RILE *fp; struct buf *b; /* Get previous identifier from C+FP into B. */ { if (!c) Igeteof_(fp, c, return false;) if (!get0val(c, fp, b, false)) return false; checksid(b->string); return !nerror; } static int keeprev(fp) RILE *fp; /* Get previous revision from FP into prevrev. */ { return getval(fp,&prevrev,false) && checknum(prevrev.string); } static int checknum(s) char const *s; { register char const *sp; register int dotcount = 0; for (sp=s; ; sp++) { switch (*sp) { case 0: if (dotcount & 1) return true; else break; case '.': dotcount++; continue; default: if (isdigit(*sp)) continue; break; } break; } workerror("%s is not a revision number", s); return false; } #ifdef KEEPTEST /* Print the keyword values found. */ char const cmdid[] ="keeptest"; int main(argc, argv) int argc; char *argv[]; { while (*(++argv)) { workname = *argv; getoldkeys((RILE*)0); VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n", *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string); } exitmain(EXIT_SUCCESS); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c index 4cbcda73e03f..6d0afb328489 100644 --- a/gnu/usr.bin/rcs/lib/rcskeys.c +++ b/gnu/usr.bin/rcs/lib/rcskeys.c @@ -1,145 +1,186 @@ /* RCS keyword table and match operation */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.4 1995/06/16 06:19:24 eggert * Update FSF address. * * Revision 5.3 1993/11/03 17:42:27 eggert * Add Name keyword. * * Revision 5.2 1991/08/19 03:13:55 eggert * Say `T const' instead of `const T'; it's less confusing for pointer types. * (This change was made in other source files too.) * * Revision 5.1 1991/04/21 11:58:25 eggert * Don't put , just before } in initializer. * * Revision 5.0 1990/08/22 08:12:54 eggert * Add -k. Ansify and Posixate. * * Revision 4.3 89/05/01 15:13:02 narten * changed copyright header to reflect current distribution rules * * Revision 4.2 87/10/18 10:36:33 narten * Updating version numbers. Changes relative to 1.1 actuallyt * relative to 4.1 * * Revision 1.2 87/09/24 14:00:10 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 4.1 83/05/04 10:06:53 wft * Initial revision. * */ #include "rcsbase.h" -libId(keysId, "$Id$") +libId(keysId, "$Id: rcskeys.c,v 1.10 1997/02/22 15:47:38 peter Exp $") -char const *const Keyword[] = { +char const *Keyword[] = { /* This must be in the same order as rcsbase.h's enum markers type. */ 0, AUTHOR, DATE, HEADER, IDH, - LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, - FREEBSD + LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, CVSHEADER, + NULL }; /* Expand all keywords by default */ - static int ExpandKeyword[] = { false, true, true, true, true, - true, true, true, true, true, true, true, - false + true, true, true, true, true, true, true, true, + true }; +enum markers LocalIdMode = Id; enum markers trymatch(string) char const *string; /* function: Checks whether string starts with a keyword followed * by a KDELIM or a VDELIM. * If successful, returns the appropriate marker, otherwise Nomatch. */ { register int j; register char const *p, *s; for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { if (!ExpandKeyword[j]) continue; /* try next keyword */ p = Keyword[j]; + if (p == NULL) + continue; s = string; while (*p++ == *s++) { if (!*p) switch (*s) { case KDELIM: case VDELIM: return (enum markers)j; default: return Nomatch; } } } return(Nomatch); } + void setIncExc(arg) - char *arg; + char const *arg; /* Sets up the ExpandKeyword table according to command-line flags */ { char *key; + char *copy, *next; int include = 0, j; - arg += 2; - switch (*arg++) { + copy = strdup(arg); + next = copy; + switch (*next++) { case 'e': include = false; break; case 'i': include = true; break; default: - return(false); + free(copy); + return; } if (include) for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) ExpandKeyword[j] = false; - key = strtok(arg, ","); + key = strtok(next, ","); while (key) { - for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { + if (Keyword[j] == NULL) + continue; if (!strcmp(key, Keyword[j])) ExpandKeyword[j] = include; + } key = strtok(NULL, ","); } - return(true); + free(copy); + return; +} + + void +setRCSLocalId(string) + char const *string; +/* function: sets local RCS id and RCSLOCALID envariable */ +{ + static char local_id[keylength+1]; + char *copy, *next, *key; + int j; + + copy = strdup(string); + next = copy; + key = strtok(next, "="); + if (strlen(key) > keylength) + error("LocalId is too long"); + VOID strcpy(local_id, key); + Keyword[LocalId] = local_id; + + /* options? */ + while (key = strtok(NULL, ",")) { + if (!strcmp(key, Keyword[Id])) + LocalIdMode=Id; + else if (!strcmp(key, Keyword[Header])) + LocalIdMode=Header; + else if (!strcmp(key, Keyword[CVSHeader])) + LocalIdMode=CVSHeader; + else + error("Unknown LocalId mode"); + } + free(copy); } diff --git a/gnu/usr.bin/rcs/lib/rcsutil.c b/gnu/usr.bin/rcs/lib/rcsutil.c index 58aef7386f29..3b2a46fcdb39 100644 --- a/gnu/usr.bin/rcs/lib/rcsutil.c +++ b/gnu/usr.bin/rcs/lib/rcsutil.c @@ -1,1391 +1,1398 @@ /* RCS utility functions */ /* Copyright 1982, 1988, 1989 Walter Tichy Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. 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. 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. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ /* * Revision 5.20 1995/06/16 06:19:24 eggert * (catchsig): Remove `return'. * Update FSF address. * * Revision 5.19 1995/06/02 18:19:00 eggert * (catchsigaction): New name for `catchsig', for sa_sigaction signature. * Use nRCS even if !has_psiginfo, to remove unused variable warning. * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. * Use ENOTSUP only if defined. * * Revision 5.18 1995/06/01 16:23:43 eggert * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, * to determine whether to use SA_SIGINFO feature, * but also check at runtime whether the feature works. * (catchsig): If an mmap_signal occurs, report the affected file name. * (unsupported_SA_SIGINFO, accessName): New variables. * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. * If SA_SIGINFO fails, fall back on sa_handler method. * * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. * (concatenate): Remove. * * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. * Remove reference to OPEN_O_WORK. * * Revision 5.17 1994/03/20 04:52:58 eggert * Specify subprocess input via file descriptor, not file name. * Avoid messing with I/O buffers in the child process. * Define dup in terms of F_DUPFD if it exists. * Move setmtime to rcsedit.c. Remove lint. * * Revision 5.16 1993/11/09 17:40:15 eggert * -V now prints version on stdout and exits. * * Revision 5.15 1993/11/03 17:42:27 eggert * Use psiginfo and setreuid if available. Move date2str to maketime.c. * * Revision 5.14 1992/07/28 16:12:44 eggert * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. * Add mmap_signal, which minimizes signal handling for non-mmap hosts. * * Revision 5.13 1992/02/17 23:02:28 eggert * Work around NFS mmap SIGBUS problem. Add -T support. * * Revision 5.12 1992/01/24 18:44:19 eggert * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint * * Revision 5.11 1992/01/06 02:42:34 eggert * O_BINARY -> OPEN_O_WORK * while (E) ; -> while (E) continue; * * Revision 5.10 1991/10/07 17:32:46 eggert * Support piece tables even if !has_mmap. * * Revision 5.9 1991/08/19 03:13:55 eggert * Add spawn() support. Explicate assumptions about getting invoker's name. * Standardize user-visible dates. Tune. * * Revision 5.8 1991/04/21 11:58:30 eggert * Plug setuid security hole. * * Revision 5.6 1991/02/26 17:48:39 eggert * Fix setuid bug. Use fread, fwrite more portably. * Support waitpid. Don't assume -1 is acceptable to W* macros. * strsave -> str_save (DG/UX name clash) * * Revision 5.5 1990/12/04 05:18:49 eggert * Don't output a blank line after a signal diagnostic. * Use -I for prompts and -q for diagnostics. * * Revision 5.4 1990/11/01 05:03:53 eggert * Remove unneeded setid check. Add awrite(), fremember(). * * Revision 5.3 1990/10/06 00:16:45 eggert * Don't fread F if feof(F). * * Revision 5.2 1990/09/04 08:02:31 eggert * Store fread()'s result in an fread_type object. * * Revision 5.1 1990/08/29 07:14:07 eggert * Declare getpwuid() more carefully. * * Revision 5.0 1990/08/22 08:13:46 eggert * Add setuid support. Permit multiple locks per user. * Remove compile-time limits; use malloc instead. * Switch to GMT. Permit dates past 1999/12/31. * Add -V. Remove snooping. Ansify and Posixate. * Tune. Some USG hosts define NSIG but not sys_siglist. * Don't run /bin/sh if it's hopeless. * Don't leave garbage behind if the output is an empty pipe. * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. * * Revision 4.6 89/05/01 15:13:40 narten * changed copyright header to reflect current distribution rules * * Revision 4.5 88/11/08 16:01:02 narten * corrected use of varargs routines * * Revision 4.4 88/08/09 19:13:24 eggert * Check for memory exhaustion. * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. * Use execv(), not system(); yield exit status like diff(1)'s. * * Revision 4.3 87/10/18 10:40:22 narten * Updating version numbers. Changes relative to 1.1 actually * relative to 4.1 * * Revision 1.3 87/09/24 14:01:01 narten * Sources now pass through lint (if you ignore printf/sprintf/fprintf * warnings) * * Revision 1.2 87/03/27 14:22:43 jenkins * Port to suns * * Revision 4.1 83/05/10 15:53:13 wft * Added getcaller() and findlock(). * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal * (needed for background jobs in older shells). Added restoreints(). * Removed printing of full RCS path from logcommand(). * * Revision 3.8 83/02/15 15:41:49 wft * Added routine fastcopy() to copy remainder of a file in blocks. * * Revision 3.7 82/12/24 15:25:19 wft * added catchints(), ignoreints() for catching and ingnoring interrupts; * fixed catchsig(). * * Revision 3.6 82/12/08 21:52:05 wft * Using DATEFORM to format dates. * * Revision 3.5 82/12/04 18:20:49 wft * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update * lockedby-field. * * Revision 3.4 82/12/03 17:17:43 wft * Added check to addlock() ensuring only one lock per person. * Addlock also returns a pointer to the lock created. Deleted fancydate(). * * Revision 3.3 82/11/27 12:24:37 wft * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. * Introduced macro SNOOP so that snoop can be placed in directory other than * TARGETDIR. Changed %02d to %.2d for compatibility reasons. * * Revision 3.2 82/10/18 21:15:11 wft * added function getfullRCSname(). * * Revision 3.1 82/10/13 16:17:37 wft * Cleanup message is now suppressed in quiet mode. */ #include "rcsbase.h" -libId(utilId, "$Id$") +libId(utilId, "$Id: rcsutil.c,v 1.6 1997/02/22 15:47:43 peter Exp $") #if !has_memcmp int memcmp(s1, s2, n) void const *s1, *s2; size_t n; { register unsigned char const *p1 = (unsigned char const*)s1, *p2 = (unsigned char const*)s2; register size_t i = n; register int r = 0; while (i-- && !(r = (*p1++ - *p2++))) ; return r; } #endif #if !has_memcpy void * memcpy(s1, s2, n) void *s1; void const *s2; size_t n; { register char *p1 = (char*)s1; register char const *p2 = (char const*)s2; while (n--) *p1++ = *p2++; return s1; } #endif #if RCS_lint malloc_type lintalloc; #endif /* * list of blocks allocated with ftestalloc() * These blocks can be freed by ffree when we're done with the current file. * We could put the free block inside struct alloclist, rather than a pointer * to the free block, but that would be less portable. */ struct alloclist { malloc_type alloc; struct alloclist *nextalloc; }; static struct alloclist *alloced; static malloc_type okalloc P((malloc_type)); static malloc_type okalloc(p) malloc_type p; { if (!p) faterror("out of memory"); return p; } malloc_type testalloc(size) size_t size; /* Allocate a block, testing that the allocation succeeded. */ { return okalloc(malloc(size)); } malloc_type testrealloc(ptr, size) malloc_type ptr; size_t size; /* Reallocate a block, testing that the allocation succeeded. */ { return okalloc(realloc(ptr, size)); } malloc_type fremember(ptr) malloc_type ptr; /* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ { register struct alloclist *q = talloc(struct alloclist); q->nextalloc = alloced; alloced = q; return q->alloc = ptr; } malloc_type ftestalloc(size) size_t size; /* Allocate a block, putting it in 'alloced' so it can be freed later. */ { return fremember(testalloc(size)); } void ffree() /* Free all blocks allocated with ftestalloc(). */ { register struct alloclist *p, *q; for (p = alloced; p; p = q) { q = p->nextalloc; tfree(p->alloc); tfree(p); } alloced = 0; } void ffree1(f) register char const *f; /* Free the block f, which was allocated by ftestalloc. */ { register struct alloclist *p, **a = &alloced; while ((p = *a)->alloc != f) a = &p->nextalloc; *a = p->nextalloc; tfree(p->alloc); tfree(p); } char * str_save(s) char const *s; /* Save s in permanently allocated storage. */ { return strcpy(tnalloc(char, strlen(s)+1), s); } char * fstr_save(s) char const *s; /* Save s in storage that will be deallocated when we're done with this file. */ { return strcpy(ftnalloc(char, strlen(s)+1), s); } char * cgetenv(name) char const *name; /* Like getenv(), but yield a copy; getenv() can overwrite old results. */ { register char *p; return (p=getenv(name)) ? str_save(p) : p; } char const * getusername(suspicious) int suspicious; /* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ { static char *name; if (!name) { if ( /* Prefer getenv() unless suspicious; it's much faster. */ # if getlogin_is_secure (suspicious || ( !(name = cgetenv("LOGNAME")) && !(name = cgetenv("USER")) )) && !(name = getlogin()) # else suspicious || ( !(name = cgetenv("LOGNAME")) && !(name = cgetenv("USER")) && !(name = getlogin()) ) # endif ) { #if has_getuid && has_getpwuid struct passwd const *pw = getpwuid(ruid()); if (!pw) faterror("no password entry for userid %lu", (unsigned long)ruid() ); name = pw->pw_name; #else #if has_setuid faterror("setuid not supported"); #else faterror("Who are you? Please setenv LOGNAME."); #endif #endif } checksid(name); } return name; } #if has_signal /* * Signal handling * * Standard C places too many restrictions on signal handlers. * We obey as many of them as we can. * Posix places fewer restrictions, and we are Posix-compatible here. */ static sig_atomic_t volatile heldsignal, holdlevel; #ifdef SA_SIGINFO static int unsupported_SA_SIGINFO; static siginfo_t bufsiginfo; static siginfo_t *volatile heldsiginfo; #endif #if has_NFS && has_mmap && large_memory && mmap_signal static char const *accessName; void readAccessFilenameBuffer(filename, p) char const *filename; unsigned char const *p; { unsigned char volatile t; accessName = filename; t = *p; accessName = 0; } #else # define accessName ((char const *) 0) #endif #if !has_psignal # define psignal my_psignal static void my_psignal P((int,char const*)); static void my_psignal(sig, s) int sig; char const *s; { char const *sname = "Unknown signal"; # if has_sys_siglist && defined(NSIG) if ((unsigned)sig < NSIG) sname = sys_siglist[sig]; # else switch (sig) { # ifdef SIGHUP case SIGHUP: sname = "Hangup"; break; # endif # ifdef SIGINT case SIGINT: sname = "Interrupt"; break; # endif # ifdef SIGPIPE case SIGPIPE: sname = "Broken pipe"; break; # endif # ifdef SIGQUIT case SIGQUIT: sname = "Quit"; break; # endif # ifdef SIGTERM case SIGTERM: sname = "Terminated"; break; # endif # ifdef SIGXCPU case SIGXCPU: sname = "Cputime limit exceeded"; break; # endif # ifdef SIGXFSZ case SIGXFSZ: sname = "Filesize limit exceeded"; break; # endif # if has_mmap && large_memory # if defined(SIGBUS) && mmap_signal==SIGBUS case SIGBUS: sname = "Bus error"; break; # endif # if defined(SIGSEGV) && mmap_signal==SIGSEGV case SIGSEGV: sname = "Segmentation fault"; break; # endif # endif } # endif /* Avoid calling sprintf etc., in case they're not reentrant. */ { char const *p; char buf[BUFSIZ], *b = buf; for (p = s; *p; *b++ = *p++) continue; *b++ = ':'; *b++ = ' '; for (p = sname; *p; *b++ = *p++) continue; *b++ = '\n'; VOID write(STDERR_FILENO, buf, b - buf); } } #endif static signal_type catchsig P((int)); #ifdef SA_SIGINFO static signal_type catchsigaction P((int,siginfo_t*,void*)); #endif static signal_type catchsig(s) int s; #ifdef SA_SIGINFO { catchsigaction(s, (siginfo_t *)0, (void *)0); } static signal_type catchsigaction(s, i, c) int s; siginfo_t *i; void *c; #endif { # if sig_zaps_handler /* If a signal arrives before we reset the handler, we lose. */ VOID signal(s, SIG_IGN); # endif # ifdef SA_SIGINFO if (!unsupported_SA_SIGINFO) i = 0; # endif if (holdlevel) { heldsignal = s; # ifdef SA_SIGINFO if (i) { bufsiginfo = *i; heldsiginfo = &bufsiginfo; } # endif return; } ignoreints(); setrid(); if (!quietflag) { /* Avoid calling sprintf etc., in case they're not reentrant. */ char const *p; char buf[BUFSIZ], *b = buf; if ( ! ( # if has_mmap && large_memory && mmap_signal /* Check whether this signal was planned. */ s == mmap_signal && accessName # else 0 # endif )) { char const *nRCS = "\nRCS"; # if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal if (s == mmap_signal && i && i->si_errno) { errno = i->si_errno; perror(nRCS++); } # endif # if defined(SA_SIGINFO) && has_psiginfo if (i) psiginfo(i, nRCS); else psignal(s, nRCS); # else psignal(s, nRCS); # endif } for (p = "RCS: "; *p; *b++ = *p++) continue; # if has_mmap && large_memory && mmap_signal if (s == mmap_signal) { p = accessName; if (!p) p = "Was a file changed by some other process? "; else { char const *p1; for (p1 = p; *p1; p1++) continue; VOID write(STDERR_FILENO, buf, b - buf); VOID write(STDERR_FILENO, p, p1 - p); b = buf; p = ": Permission denied. "; } while (*p) *b++ = *p++; } # endif for (p = "Cleaning up.\n"; *p; *b++ = *p++) continue; VOID write(STDERR_FILENO, buf, b - buf); } exiterr(); } void ignoreints() { ++holdlevel; } void restoreints() { if (!--holdlevel && heldsignal) # ifdef SA_SIGINFO VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); # else VOID catchsig(heldsignal); # endif } static void setup_catchsig P((int const*,int)); #if has_sigaction static void check_sig P((int)); static void check_sig(r) int r; { if (r != 0) efaterror("signal handling"); } static void setup_catchsig(sig, sigs) int const *sig; int sigs; { register int i, j; struct sigaction act; for (i=sigs; 0<=--i; ) { check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); if (act.sa_handler != SIG_IGN) { act.sa_handler = catchsig; # ifdef SA_SIGINFO if (!unsupported_SA_SIGINFO) { # if has_sa_sigaction act.sa_sigaction = catchsigaction; # else act.sa_handler = catchsigaction; # endif act.sa_flags |= SA_SIGINFO; } # endif for (j=sigs; 0<=--j; ) check_sig(sigaddset(&act.sa_mask, sig[j])); if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { # if defined(SA_SIGINFO) && defined(ENOTSUP) if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { /* Turn off use of SA_SIGINFO and try again. */ unsupported_SA_SIGINFO = 1; i++; continue; } # endif check_sig(-1); } } } } #else #if has_sigblock static void setup_catchsig(sig, sigs) int const *sig; int sigs; { register int i; int mask; mask = 0; for (i=sigs; 0<=--i; ) mask |= sigmask(sig[i]); mask = sigblock(mask); for (i=sigs; 0<=--i; ) if ( signal(sig[i], catchsig) == SIG_IGN && signal(sig[i], SIG_IGN) != catchsig ) faterror("signal catcher failure"); VOID sigsetmask(mask); } #else static void setup_catchsig(sig, sigs) int const *sig; int sigs; { register i; for (i=sigs; 0<=--i; ) if ( signal(sig[i], SIG_IGN) != SIG_IGN && signal(sig[i], catchsig) != SIG_IGN ) faterror("signal catcher failure"); } #endif #endif static int const regsigs[] = { # ifdef SIGHUP SIGHUP, # endif # ifdef SIGINT SIGINT, # endif # ifdef SIGPIPE SIGPIPE, # endif # ifdef SIGQUIT SIGQUIT, # endif # ifdef SIGTERM SIGTERM, # endif # ifdef SIGXCPU SIGXCPU, # endif # ifdef SIGXFSZ SIGXFSZ, # endif }; void catchints() { static int catching_ints; if (!catching_ints) { catching_ints = true; setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); } } #if has_mmap && large_memory && mmap_signal /* * If you mmap an NFS file, and someone on another client removes the last * link to that file, and you later reference an uncached part of that file, * you'll get a SIGBUS or SIGSEGV (depending on the operating system). * Catch the signal and report the problem to the user. * Unfortunately, there's no portable way to differentiate between this * problem and actual bugs in the program. * This NFS problem is rare, thank goodness. * * This can also occur if someone truncates the file, even without NFS. */ static int const mmapsigs[] = { mmap_signal }; void catchmmapints() { static int catching_mmap_ints; if (!catching_mmap_ints) { catching_mmap_ints = true; setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); } } #endif #endif /* has_signal */ void fastcopy(inf,outf) register RILE *inf; FILE *outf; /* Function: copies the remainder of file inf to outf. */ { #if large_memory # if maps_memory awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); inf->ptr = inf->lim; # else for (;;) { awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); inf->ptr = inf->readlim; if (inf->ptr == inf->lim) break; VOID Igetmore(inf); } # endif #else char buf[BUFSIZ*8]; register fread_type rcount; /*now read the rest of the file in blocks*/ while (!feof(inf)) { if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { testIerror(inf); return; } awrite(buf, (size_t)rcount, outf); } #endif } #ifndef SSIZE_MAX /* This does not work in #ifs, but it's good enough for us. */ /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ # define SSIZE_MAX ((unsigned)-1 >> 1) #endif void awrite(buf, chars, f) char const *buf; size_t chars; FILE *f; { /* Posix 1003.1-1990 ssize_t hack */ while (SSIZE_MAX < chars) { if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) Oerror(); buf += SSIZE_MAX; chars -= SSIZE_MAX; } if (Fwrite(buf, sizeof(*buf), chars, f) != chars) Oerror(); } /* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ static int dupSafer P((int)); static int dupSafer(fd) int fd; { # ifdef F_DUPFD return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); # else int e, f, i, used = 0; while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) used |= 1<string); bufrealloc(b, bl + sl + 4); p = b->string + bl; *p++ = c; *p++ = '\''; while (*s) { if (*s == '\'') { *p++ = '\''; *p++ = '\\'; *p++ = '\''; } *p++ = *s++; } *p++ = '\''; *p = 0; } #endif #if !has_spawn && has_fork /* * Output the string S to stderr, without touching any I/O buffers. * This is useful if you are a child process, whose buffers are usually wrong. * Exit immediately if the write does not completely succeed. */ static void write_stderr P((char const *)); static void write_stderr(s) char const *s; { size_t slen = strlen(s); if (write(STDERR_FILENO, s, slen) != slen) _exit(EXIT_TROUBLE); } #endif /* * Run a command. * infd, if not -1, is the input file descriptor. * outname, if nonzero, is the name of the output file. * args[1..] form the command to be run; args[0] might be modified. */ int runv(infd, outname, args) int infd; char const *outname, **args; { int wstatus; #if bad_wait_if_SIGCHLD_ignored static int fixed_SIGCHLD; if (!fixed_SIGCHLD) { fixed_SIGCHLD = true; # ifndef SIGCHLD # define SIGCHLD SIGCLD # endif VOID signal(SIGCHLD, SIG_DFL); } #endif oflush(); eflush(); { #if has_spawn int in, out; char const *file; in = -1; if (infd != -1 && infd != STDIN_FILENO) { if ((in = dup(STDIN_FILENO)) < 0) { if (errno != EBADF) efaterror("spawn input setup"); in = -2; } else { # ifdef F_DUPFD if (close(STDIN_FILENO) != 0) efaterror("spawn input close"); # endif } if ( # ifdef F_DUPFD fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO # else dup2(infd, STDIN_FILENO) != STDIN_FILENO # endif ) efaterror("spawn input redirection"); } out = -1; if (outname) { if ((out = dup(STDOUT_FILENO)) < 0) { if (errno != EBADF) efaterror("spawn output setup"); out = -2; } if (fdreopen( STDOUT_FILENO, outname, O_CREAT | O_TRUNC | O_WRONLY ) < 0) efaterror(outname); } wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); # ifdef RCS_SHELL if (wstatus == -1 && errno == ENOEXEC) { args[0] = RCS_SHELL; wstatus = spawnv(0, args[0], (char**)args); } # endif redirect(in, STDIN_FILENO); redirect(out, STDOUT_FILENO); #else #if has_fork pid_t pid; if (!(pid = vfork())) { char const *notfound; if (infd != -1 && infd != STDIN_FILENO && ( # ifdef F_DUPFD (VOID close(STDIN_FILENO), fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) # else dup2(infd, STDIN_FILENO) != STDIN_FILENO # endif )) { /* Avoid perror since it may misuse buffers. */ write_stderr(args[1]); write_stderr(": I/O redirection failed\n"); _exit(EXIT_TROUBLE); } if (outname) if (fdreopen( STDOUT_FILENO, outname, O_CREAT | O_TRUNC | O_WRONLY ) < 0) { /* Avoid perror since it may misuse buffers. */ write_stderr(args[1]); write_stderr(": "); write_stderr(outname); write_stderr(": cannot create\n"); _exit(EXIT_TROUBLE); } VOID exec_RCS(args[1], (char**)(args + 1)); notfound = args[1]; # ifdef RCS_SHELL if (errno == ENOEXEC) { args[0] = notfound = RCS_SHELL; VOID execv(args[0], (char**)args); } # endif /* Avoid perror since it may misuse buffers. */ write_stderr(notfound); write_stderr(": not found\n"); _exit(EXIT_TROUBLE); } if (pid < 0) efaterror("fork"); # if has_waitpid if (waitpid(pid, &wstatus, 0) < 0) efaterror("waitpid"); # else { pid_t w; do { if ((w = wait(&wstatus)) < 0) efaterror("wait"); } while (w != pid); } # endif #else static struct buf b; char const *p; /* Use system(). On many hosts system() discards signals. Yuck! */ p = args + 1; bufscpy(&b, *p); while (*++p) bufargcat(&b, ' ', *p); if (infd != -1 && infd != STDIN_FILENO) { char redirection[32]; VOID sprintf(redirection, "<&%d", infd); bufscat(&b, redirection); } if (outname) bufargcat(&b, '>', outname); wstatus = system(b.string); #endif #endif } if (!WIFEXITED(wstatus)) { if (WIFSIGNALED(wstatus)) { psignal(WTERMSIG(wstatus), args[1]); fatcleanup(1); } faterror("%s failed for unknown reason", args[1]); } return WEXITSTATUS(wstatus); } #define CARGSMAX 20 /* * Run a command. * infd, if not -1, is the input file descriptor. * outname, if nonzero, is the name of the output file. * The remaining arguments specify the command and its arguments. */ int #if has_prototypes run(int infd, char const *outname, ...) #else /*VARARGS2*/ run(infd, outname, va_alist) int infd; char const *outname; va_dcl #endif { va_list ap; char const *rgargs[CARGSMAX]; register int i; vararg_start(ap, outname); for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) if (CARGSMAX <= i) faterror("too many command arguments"); va_end(ap); return runv(infd, outname, rgargs); } int RCSversion; void setRCSversion(str) char const *str; { static int oldversion; register char const *s = str + 2; if (*s) { int v = VERSION_DEFAULT; if (oldversion) redefined('V'); oldversion = true; v = 0; while (isdigit(*s)) v = 10*v + *s++ - '0'; if (*s) error("%s isn't a number", str); else if (v < VERSION_min || VERSION_max < v) error("%s out of range %d..%d", str, VERSION_min, VERSION_max ); RCSversion = VERSION(v); } else { printf("RCS version %s\n", RCS_version_string); exit(0); } } int getRCSINIT(argc, argv, newargv) int argc; char **argv, ***newargv; { register char *p, *q, **pp; + char const *ev; size_t n; + if ((ev = cgetenv("RCSLOCALID"))) + setRCSLocalId(ev); + + if ((ev = cgetenv("RCSINCEXC"))) + setIncExc(ev); + if (!(q = cgetenv("RCSINIT"))) *newargv = argv; else { n = argc + 2; /* * Count spaces in RCSINIT to allocate a new arg vector. * This is an upper bound, but it's OK even if too large. */ for (p = q; ; ) { switch (*p++) { default: continue; case ' ': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': n++; continue; case '\0': break; } break; } *newargv = pp = tnalloc(char*, n); *pp++ = *argv++; /* copy program name */ for (p = q; ; ) { for (;;) { switch (*q) { case '\0': goto copyrest; case ' ': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': q++; continue; } break; } *pp++ = p; ++argc; for (;;) { switch ((*p++ = *q++)) { case '\0': goto copyrest; case '\\': if (!*q) goto copyrest; p[-1] = *q++; continue; default: continue; case ' ': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': break; } break; } p[-1] = '\0'; } copyrest: while ((*pp++ = *argv++)) continue; } return argc; } #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i #if has_getuid uid_t ruid() { cacheid(getuid()); } #endif #if has_setuid uid_t euid() { cacheid(geteuid()); } #endif #if has_setuid /* * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), * because it lets us switch back and forth between arbitrary users. * If seteuid() doesn't work, we fall back on setuid(), * which works if saved setuid is supported, * unless the real or effective user is root. * This area is such a mess that we always check switches at runtime. */ static void #if has_prototypes set_uid_to(uid_t u) #else set_uid_to(u) uid_t u; #endif /* Become user u. */ { static int looping; if (euid() == ruid()) return; #if (has_fork||has_spawn) && DIFF_ABSOLUTE # if has_setreuid if (setreuid(u==euid() ? ruid() : euid(), u) != 0) efaterror("setuid"); # else if (seteuid(u) != 0) efaterror("setuid"); # endif #endif if (geteuid() != u) { if (looping) return; looping = true; faterror("root setuid not supported" + (u?5:0)); } } static int stick_with_euid; void /* Ignore all calls to seteid() and setrid(). */ nosetid() { stick_with_euid = true; } void seteid() /* Become effective user. */ { if (!stick_with_euid) set_uid_to(euid()); } void setrid() /* Become real user. */ { if (!stick_with_euid) set_uid_to(ruid()); } #endif time_t now() { static time_t t; if (!t && time(&t) == -1) efaterror("time"); return t; }