rsalz@uunet.uu.net (Rich Salz) (02/22/91)
Submitted-by: Adam Hammer <hammer@cs.purdue.edu> Posting-number: Volume 24, Issue 4 Archive-name: rcs/part04 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: src/rcsutil.c src/rlog.c # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:57 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 4 (of 12)."' if test -f 'src/rcsutil.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rcsutil.c'\" else echo shar: Extracting \"'src/rcsutil.c'\" \(17610 characters\) sed "s/^X//" >'src/rcsutil.c' <<'END_OF_FILE' X/* X * RCS utilities X */ X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X XThis file is part of RCS. X XRCS is free software; you can redistribute it and/or modify Xit under the terms of the GNU General Public License as published by Xthe Free Software Foundation; either version 1, or (at your option) Xany later version. X XRCS is distributed in the hope that it will be useful, Xbut WITHOUT ANY WARRANTY; without even the implied warranty of XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the XGNU General Public License for more details. X XYou should have received a copy of the GNU General Public License Xalong with RCS; see the file COPYING. If not, write to Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X XReport problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X X/* $Log: rcsutil.c,v $ X * Revision 5.5 1990/12/04 05:18:49 eggert X * Don't output a blank line after a signal diagnostic. X * Use -I for prompts and -q for diagnostics. X * X * Revision 5.4 1990/11/01 05:03:53 eggert X * Remove unneeded setid check. Add awrite(), fremember(). X * X * Revision 5.3 1990/10/06 00:16:45 eggert X * Don't fread F if feof(F). X * X * Revision 5.2 1990/09/04 08:02:31 eggert X * Store fread()'s result in an fread_type object. X * X * Revision 5.1 1990/08/29 07:14:07 eggert X * Declare getpwuid() more carefully. X * X * Revision 5.0 1990/08/22 08:13:46 eggert X * Add setuid support. Permit multiple locks per user. X * Remove compile-time limits; use malloc instead. X * Switch to GMT. Permit dates past 1999/12/31. X * Add -V. Remove snooping. Ansify and Posixate. X * Tune. Some USG hosts define NSIG but not sys_siglist. X * Don't run /bin/sh if it's hopeless. X * Don't leave garbage behind if the output is an empty pipe. X * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. X * X * Revision 4.6 89/05/01 15:13:40 narten X * changed copyright header to reflect current distribution rules X * X * Revision 4.5 88/11/08 16:01:02 narten X * corrected use of varargs routines X * X * Revision 4.4 88/08/09 19:13:24 eggert X * Check for memory exhaustion. X * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. X * Use execv(), not system(); yield exit status like diff(1)'s. X * X * Revision 4.3 87/10/18 10:40:22 narten X * Updating version numbers. Changes relative to 1.1 actually X * relative to 4.1 X * X * Revision 1.3 87/09/24 14:01:01 narten X * Sources now pass through lint (if you ignore printf/sprintf/fprintf X * warnings) X * X * Revision 1.2 87/03/27 14:22:43 jenkins X * Port to suns X * X * Revision 4.1 83/05/10 15:53:13 wft X * Added getcaller() and findlock(). X * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal X * (needed for background jobs in older shells). Added restoreints(). X * Removed printing of full RCS path from logcommand(). X * X * Revision 3.8 83/02/15 15:41:49 wft X * Added routine fastcopy() to copy remainder of a file in blocks. X * X * Revision 3.7 82/12/24 15:25:19 wft X * added catchints(), ignoreints() for catching and ingnoring interrupts; X * fixed catchsig(). X * X * Revision 3.6 82/12/08 21:52:05 wft X * Using DATEFORM to format dates. X * X * Revision 3.5 82/12/04 18:20:49 wft X * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update X * lockedby-field. X * X * Revision 3.4 82/12/03 17:17:43 wft X * Added check to addlock() ensuring only one lock per person. X * Addlock also returns a pointer to the lock created. Deleted fancydate(). X * X * Revision 3.3 82/11/27 12:24:37 wft X * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. X * Introduced macro SNOOP so that snoop can be placed in directory other than X * TARGETDIR. Changed %02d to %.2d for compatibility reasons. X * X * Revision 3.2 82/10/18 21:15:11 wft X * added function getfullRCSname(). X * X * Revision 3.1 82/10/13 16:17:37 wft X * Cleanup message is now suppressed in quiet mode. X */ X X X X X#include "rcsbase.h" X X#if !MAKEDEPEND && defined(declare_getpwuid) X# include <pwd.h> X declare_getpwuid X#endif X XlibId(utilId, "$Id: rcsutil.c,v 5.5 1990/12/04 05:18:49 eggert Exp $") X X#if lint X malloc_type lintalloc; X#endif X X#if has_getuid X uid_t ruid; X#endif X#if SETID X static uid_t euid; X static gid_t egid, rgid; X#endif X X/* X * list of blocks allocated with ftestalloc() X * These blocks can be freed by ffree when we're done with the current file. X * We could put the free block inside struct alloclist, rather than a pointer X * to the free block, but that would be less portable. X */ Xstruct alloclist { X malloc_type alloc; X struct alloclist *nextalloc; X}; Xstatic struct alloclist *alloced; X X X static malloc_type Xokalloc(p) X malloc_type p; X{ X if (!p) X faterror("out of memory"); X return p; X} X X malloc_type Xtestalloc(size) X size_t size; X/* Allocate a block, testing that the allocation succeeded. */ X{ X return okalloc(malloc(size)); X} X X malloc_type Xtestrealloc(ptr, size) X malloc_type ptr; X size_t size; X/* Reallocate a block, testing that the allocation succeeded. */ X{ X return okalloc(realloc(ptr, size)); X} X X malloc_type Xfremember(ptr) X malloc_type ptr; X/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ X{ X register struct alloclist *q = talloc(struct alloclist); X q->nextalloc = alloced; X alloced = q; X return q->alloc = ptr; X} X X malloc_type Xftestalloc(size) X size_t size; X/* Allocate a block, putting it in 'alloced' so it can be freed later. */ X{ X return fremember(testalloc(size)); X} X X void Xffree() X/* Free all blocks allocated with ftestalloc(). */ X{ X register struct alloclist *p, *q; X for (p = alloced; p; p = q) { X q = p->nextalloc; X tfree(p->alloc); X tfree(p); X } X alloced = nil; X} X X void Xffree1(f) X register const char *f; X/* Free the block f, which was allocated by ftestalloc. */ X{ X register struct alloclist *p, **a = &alloced; X X while ((p = *a)->alloc != f) X a = &p->nextalloc; X *a = p->nextalloc; X tfree(p->alloc); X tfree(p); X} X X const char * Xstrsave(s) X const char *s; X/* Save s in permanently allocated storage. */ X{ X return strcpy(tnalloc(char, strlen(s)+1), s); X} X X const char * Xfstrsave(s) X const char *s; X/* Save s in storage that will be deallocated when we're done with this file. */ X{ X return strcpy(ftnalloc(char, strlen(s)+1), s); X} X X char * Xcgetenv(name) X const char *name; X/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ X{ X register char *p; X X return (p=getenv(name)) ? strsave(p) : p; X} X X X const char * Xgetcaller() X/* Function: gets the caller's login. X */ X{ X static char *name; X X if (!name) { X if (!( X /* Use getenv() if we're trustworthy; it's much faster. */ X#if SETID X euid==ruid && egid==rgid && X#endif X ( X (name = cgetenv("LOGNAME")) X || (name = cgetenv("USER")) X ) X X /* Follow a procedure recommended by Posix 1003.1-1988. */ X || (name = getlogin()) X )) { X#if has_getuid & defined(declare_getpwuid) X const struct passwd *pw = getpwuid(ruid); X if (!pw) X faterror("no password entry for userid %lu", X (unsigned long)ruid X ); X name = pw->pw_name; X#else X faterror("Who are you? Please set LOGNAME."); X#endif X } X checksid(name); X } X return name; X} X X X X int Xfindlock(delete, target) X int delete; X struct hshentry **target; X/* Finds the first lock held by caller and returns a pointer X * to the locked delta; also removes the lock if delete is set. X * Returns 0 for no locks, 1 for one, 2 for two or more. X * If one lock, puts it into *target. X */ X{ X register struct lock *next, **trail, **found = nil; X X for (trail = &Locks; (next = *trail); trail = &next->nextlock) X if (strcmp(getcaller(), next->login) == 0) { X if (found) { X error("multiple revisions locked by %s; please specify one", getcaller()); X return 2; X } X found = trail; X } X if (!found) X return 0; X next = *found; X *target = next->delta; X if (delete) { X next->delta->lockedby = nil; X *found = next->nextlock; X } X return 1; X} X X X X X X X X int Xaddlock(delta) Xstruct hshentry * delta; X/* Add a lock held by caller to delta and yield 1 if successful. X * Print an error message and yield -1 if no lock is added because X * the delta is locked by somebody other than caller. X * Yield 0 if the caller already holds the lock. */ X{ X struct lock * next; X X next=Locks; X while (next!=nil) { X if (cmpnum(delta->num,next->delta->num)==0) { X if (strcmp(getcaller(),next->login)==0) X return 0; X else { X error("revision %s already locked by %s", X delta->num, next->login); X return -1; X } X } X next = next->nextlock; X } X /* set up new lockblock */ X next = ftalloc(struct lock); X delta->lockedby=next->login=getcaller(); X next->delta= delta; X next->nextlock=Locks; X Locks=next; X return 1; X} X X X X int Xaddsymbol(num, name, rebind) X const char *num, *name; X int rebind; X/* Function: adds a new symbolic name and associates it with revision num. X * If name already exists and rebind is true, the name is associated X * with the new num; otherwise, an error message is printed and X * false returned. Returns true it successful. X */ X{ register struct assoc * next; X next=Symbols; X while (next!=nil) { X if (strcmp(name,next->symbol)==0) { X if (rebind) { X next->num = num; X return true; X } else { X error("symbolic name %s already bound to %s", X name, next->num); X return false; X } X } else next = next->nextassoc; X } X /* not found; insert new pair. */ X next = ftalloc(struct assoc); X next->symbol=name; X next->num = num; X next->nextassoc=Symbols; X Symbols = next; X return true; X} X X X X Xint checkaccesslist() X/* function: Returns true if caller is the superuser, the owner of the X * file, the access list is empty, or caller is on the access list. X * Prints an error message and returns false otherwise. X */ X{ X register const struct access *next; X X if (!AccessList || strcmp(getcaller(),"root")==0) X return true; X X next=AccessList; X do { X if (strcmp(getcaller(),next->login)==0) X return true; X next=next->nextaccess; X } while (next!=nil); X X#if has_getuid X { X struct stat statbuf; X VOID fstat(fileno(finptr),&statbuf); /* get owner of file */ X if (myself(statbuf.st_uid)) return true; X } X#endif X X error("user %s not on the access list", getcaller()); X return false; X} X X X/* X * Signal handling X * X * ANSI C places too many restrictions on signal handlers. X * We obey as many of them as we can. X * Posix places fewer restrictions, and we are Posix-compatible here. X */ X Xstatic volatile sig_atomic_t heldsignal, holdlevel; X X static signal_type Xcatchsig(s) X int s; X{ X const char *sname; X char buf[BUFSIZ]; X X#if sig_zaps_handler X /* If a signal arrives before we reset the signal handler, we lose. */ X VOID signal(s, SIG_IGN); X#endif X if (holdlevel) { X heldsignal = s; X return; X } X ignoreints(); X setrid(); X if (!quietflag) { X sname = nil; X#if has_sys_siglist & defined(NSIG) X if ((unsigned)s < NSIG) { X# ifndef sys_siglist X extern const char *sys_siglist[]; X# endif X sname = sys_siglist[s]; X } X#else X switch (s) { X#ifdef SIGHUP X case SIGHUP: sname = "Hangup"; break; X#endif X#ifdef SIGINT X case SIGINT: sname = "Interrupt"; break; X#endif X#ifdef SIGPIPE X case SIGPIPE: sname = "Broken pipe"; break; X#endif X#ifdef SIGQUIT X case SIGQUIT: sname = "Quit"; break; X#endif X#ifdef SIGTERM X case SIGTERM: sname = "Terminated"; break; X#endif X#ifdef SIGXCPU X case SIGXCPU: sname = "Cputime limit exceeded"; break; X#endif X#ifdef SIGXFSZ X case SIGXFSZ: sname = "Filesize limit exceeded"; break; X#endif X } X#endif X if (sname) X VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname); X else X VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s); X VOID write(STDERR_FILENO, buf, strlen(buf)); X } X exiterr(); X} X X void Xignoreints() X{ X ++holdlevel; X} X X void Xrestoreints() X{ X if (!--holdlevel && heldsignal) X VOID catchsig(heldsignal); X} X X Xstatic const sig[] = { X#ifdef SIGHUP X SIGHUP, X#endif X#ifdef SIGINT X SIGINT, X#endif X#ifdef SIGPIPE X SIGPIPE, X#endif X#ifdef SIGQUIT X SIGQUIT, X#endif X#ifdef SIGTERM X SIGTERM, X#endif X#ifdef SIGXCPU X SIGXCPU, X#endif X#ifdef SIGXFSZ X SIGXFSZ, X#endif X}; X#define SIGS (sizeof(sig)/sizeof(*sig)) X X X#if has_sigaction X X static void X checksig(r) X int r; X { X if (r < 0) X efaterror("signal"); X } X X void X catchints() X { X register int i; X sigset_t blocked; X struct sigaction act; X X checksig(sigemptyset(&blocked)); X for (i=SIGS; 0<=--i; ) X checksig(sigaddset(&blocked, sig[i])); X for (i=SIGS; 0<=--i; ) { X checksig(sigaction(sig[i], (struct sigaction*)nil, &act)); X if (act.sa_handler != SIG_IGN) { X act.sa_handler = catchsig; X act.sa_mask = blocked; X checksig(sigaction(sig[i], &act, (struct sigaction*)nil)); X } X } X } X X#else X#if has_sigblock X X void catchints() X { X register int i; X int mask; X X mask = 0; X for (i=SIGS; 0<=--i; ) X mask |= sigmask(sig[i]); X mask = sigblock(mask); X for (i=SIGS; 0<=--i; ) X if (signal(sig[i], catchsig) == SIG_IGN) X VOID signal(sig[i], SIG_IGN); X VOID sigsetmask(mask); X } X X#else X X void catchints() X { X register i; X for (i=SIGS; 0<=--i; ) X if (signal(sig[i], SIG_IGN) != SIG_IGN) X VOID signal(sig[i], catchsig); X } X X#endif X#endif X X X void Xfastcopy(inf,outf) XFILE * inf, * outf; X/* Function: copies the remainder of file inf to outf. X */ X{ char buf[BUFSIZ]; X register fread_type rcount; X X /*now read the rest of the file in blocks*/ X while (!feof(inf) && (rcount = fread(buf,sizeof(char),BUFSIZ,inf))) { X awrite(buf, rcount, outf); X } X} X X void Xawrite(buf, chars, f) X const char *buf; X fread_type chars; X FILE *f; X{ X if (fwrite(buf, sizeof(char), chars, f) != chars) X IOerror(); X} X X X X X X/* X* Print RCS format date and time in user-readable format. X*/ X void Xprintdate(f, date, separator) X register FILE *f; X const char *date, *separator; X{ X register const char *p = date; X X while (*p++ != '.') X ; X aprintf(f, "%s%.*s/%.2s/%.2s%s%.2s:%.2s:%s", X date[2]=='.' && VERSION(5)<=RCSversion ? "19" : "", X p-date-1, date, X p, p+3, separator, p+6, p+9, p+12 X ); X} X X X X Xstatic int fdreopen(fd, file, flags, mode) X int fd; X const char *file; X int flags; X mode_t mode; X{ X int newfd; X VOID close(fd); X newfd = X#if !open_can_creat X flags&O_CREAT ? creat(file,mode) : X#endif X open(file,flags,mode); X if (newfd < 0 || newfd == fd) X return newfd; X fd = dup2(newfd, fd); X VOID close(newfd); X return fd; X} X Xstatic void tryopen(fd,file,flags) X int fd, flags; X const char *file; X{ X if (file && fdreopen(fd,file,flags,S_IRUSR|S_IWUSR) != fd) { X VOID write(STDERR_FILENO, file, strlen(file)); X VOID write(STDERR_FILENO, ": can't open\n", 13); X _exit(EXIT_TROUBLE); X } X} X X/* X* Run a command specified by the strings in 'inoutargs'. X* inoutargs[0], if nonnil, is the name of the input file. X* inoutargs[1], if nonnil, is the name of the output file. X* inoutargs[2..] form the command to be run. X*/ X int Xrunv(inoutargs) X const char **inoutargs; X{ X int pid; X int wstatus, w; X register const char **p; X oflush(); X eflush(); X if (!(pid = vfork())) { X p = inoutargs; X tryopen(STDIN_FILENO, *p++, O_RDONLY); X tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY); X VOID EXECRCS(*p, p); X if (errno == ENOEXEC) { X *--p = "/bin/sh"; X VOID execv(*p, p); X } X VOID write(STDERR_FILENO, *p, strlen(*p)); X VOID write(STDERR_FILENO, ": not found\n", 12); X _exit(EXIT_TROUBLE); X } X if (pid < 0) X return pid; X do { X if ((w = wait(&wstatus)) < 0) X return w; X } while (w != pid); X return wstatus; X} X X#define CARGSMAX 20 X/* X* Run a command. X* The first two arguments are the input and output files (if nonnil); X* the rest specify the command and its arguments. X*/ X int X#if has_prototypes Xrun(const char *infile, const char *outfile, ...) X#else X /*VARARGS2*/ Xrun(infile, outfile, va_alist) X const char *infile; X const char *outfile; X va_dcl X#endif X{ X va_list ap; X const char *rgargs[CARGSMAX]; X register i = 0; X rgargs[0] = infile; X rgargs[1] = outfile; X vararg_start(ap, outfile); X for (i = 2; (rgargs[i++] = va_arg(ap, const char*)); ) X if (CARGSMAX <= i) X faterror("too many command arguments"); X va_end(ap); X return runv(rgargs); X} X X Xint RCSversion; X X void XsetRCSversion(str) X const char *str; X{ X static const char *oldversion; X X register const char *s = str + 2; X int v = VERSION_DEFAULT; X X if (oldversion) X redefined('V'); X oldversion = str; X X if (*s) { X v = 0; X while (isdigit(*s)) X v = 10*v + *s++ - '0'; X if (*s) X faterror("%s isn't a number", str); X if (v < VERSION_MIN || VERSION_MAX < v) X faterror("%s out of range %d..%d", str, VERSION_MIN, VERSION_MAX); X } X X RCSversion = VERSION(v); X} X X void Xinitid() X{ X#if SETID X egid = getegid(); X euid = geteuid(); X rgid = getgid(); X#endif X#if has_getuid X ruid = getuid(); X#endif X setrid(); X} X X X#if SETID X void Xseteid() X/* Become effective user and group. */ X{ X if (euid!=ruid && seteuid(euid)<0 || egid!=rgid && setegid(egid)<0) X efaterror("seteid"); X} X X void Xsetrid() X/* Become real user and group. */ X{ X if (euid!=ruid && seteuid(ruid)<0 || egid!=rgid && setegid(rgid)<0) X efaterror("setrid"); X} X#endif END_OF_FILE if test 17610 -ne `wc -c <'src/rcsutil.c'`; then echo shar: \"'src/rcsutil.c'\" unpacked with wrong size! fi # end of 'src/rcsutil.c' fi if test -f 'src/rlog.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/rlog.c'\" else echo shar: Extracting \"'src/rlog.c'\" \(32911 characters\) sed "s/^X//" >'src/rlog.c' <<'END_OF_FILE' X/* X * RLOG operation X */ X/***************************************************************************** X * print contents of RCS files X ***************************************************************************** X */ X X/* Copyright (C) 1982, 1988, 1989 Walter Tichy X Copyright 1990 by Paul Eggert X Distributed under license by the Free Software Foundation, Inc. X XThis file is part of RCS. X XRCS is free software; you can redistribute it and/or modify Xit under the terms of the GNU General Public License as published by Xthe Free Software Foundation; either version 1, or (at your option) Xany later version. X XRCS is distributed in the hope that it will be useful, Xbut WITHOUT ANY WARRANTY; without even the implied warranty of XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the XGNU General Public License for more details. X XYou should have received a copy of the GNU General Public License Xalong with RCS; see the file COPYING. If not, write to Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. X XReport problems and direct all questions to: X X rcs-bugs@cs.purdue.edu X X*/ X X X X X/* $Log: rlog.c,v $ X * Revision 5.5 1990/11/01 05:03:55 eggert X * Permit arbitrary data in logs and comment leaders. X * X * Revision 5.4 1990/10/04 06:30:22 eggert X * Accumulate exit status across files. X * X * Revision 5.3 1990/09/11 02:41:16 eggert X * Plug memory leak. X * X * Revision 5.2 1990/09/04 08:02:33 eggert X * Count RCS lines better. X * X * Revision 5.0 1990/08/22 08:13:48 eggert X * Remove compile-time limits; use malloc instead. Add setuid support. X * Switch to GMT. X * Report dates in long form, to warn about dates past 1999/12/31. X * Change "added/del" message to make room for the longer dates. X * Don't generate trailing white space. Add -V. Ansify and Posixate. X * X * Revision 4.7 89/05/01 15:13:48 narten X * changed copyright header to reflect current distribution rules X * X * Revision 4.6 88/08/09 19:13:28 eggert X * Check for memory exhaustion; don't access freed storage. X * Shrink stdio code size; remove lint. X * X * Revision 4.5 87/12/18 11:46:38 narten X * more lint cleanups (Guy Harris) X * X * Revision 4.4 87/10/18 10:41:12 narten X * Updating version numbers X * Changes relative to 1.1 actually relative to 4.2 X * X * Revision 1.3 87/09/24 14:01:10 narten X * Sources now pass through lint (if you ignore printf/sprintf/fprintf X * warnings) X * X * Revision 1.2 87/03/27 14:22:45 jenkins X * Port to suns X * X * Revision 4.2 83/12/05 09:18:09 wft X * changed rewriteflag to external. X * X * Revision 4.1 83/05/11 16:16:55 wft X * Added -b, updated getnumericrev() accordingly. X * Replaced getpwuid() with getcaller(). X * X * Revision 3.7 83/05/11 14:24:13 wft X * Added options -L and -R; X * Fixed selection bug with -l on multiple files. X * Fixed error on dates of the form -d'>date' (rewrote getdatepair()). X * X * Revision 3.6 82/12/24 15:57:53 wft X * shortened output format. X * X * Revision 3.5 82/12/08 21:45:26 wft X * removed call to checkaccesslist(); used DATEFORM to format all dates; X * removed unused variables. X * X * Revision 3.4 82/12/04 13:26:25 wft X * Replaced getdelta() with gettree(); removed updating of field lockedby. X * X * Revision 3.3 82/12/03 14:08:20 wft X * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE. X * Fixed printing of nil, removed printing of Suffix, X * added shortcut if no revisions are printed, disambiguated struct members. X * X * Revision 3.2 82/10/18 21:09:06 wft X * call to curdir replaced with getfullRCSname(), X * fixed call to getlogin(), cosmetic changes on output, X * changed conflicting long identifiers. X * X * Revision 3.1 82/10/13 16:07:56 wft X * fixed type of variables receiving from getc() (char -> int). X */ X X X X#include "rcsbase.h" X Xstruct lockers { /* lockers in locker option; stored */ X const char * login; /* lockerlist */ X struct lockers * lockerlink; X } ; X Xstruct stateattri { /* states in state option; stored in */ X const char * status; /* statelist */ X struct stateattri * nextstate; X } ; X Xstruct authors { /* login names in author option; */ X const char * login; /* stored in authorlist */ X struct authors * nextauthor; X } ; X Xstruct Revpairs{ /* revision or branch range in -r */ X unsigned numfld; /* option; stored in revlist */ X const char * strtrev; X const char * endrev; X struct Revpairs * rnext; X } ; X Xstruct Datepairs{ /* date range in -d option; stored in */ X char strtdate[datesize]; /* duelst and datelist */ X char enddate[datesize]; X struct Datepairs * dnext; X }; X Xstatic char extractdelta P((const struct hshentry*)); Xstatic int checkrevpair P((const char*,const char*)); Xstatic int readdeltalog P((void)); Xstatic void cleanup P((void)); Xstatic void extdate P((struct hshentry*)); Xstatic void exttree P((struct hshentry*)); Xstatic void getauthor P((char*)); Xstatic void getdatepair P((char*)); Xstatic void getlocker P((char*)); Xstatic void getnumericrev P((void)); Xstatic void getrevpairs P((char*)); Xstatic void getscript P((struct hshentry*)); Xstatic void getstate P((char*)); Xstatic void putabranch P((const struct hshentry*)); Xstatic void putadelta P((const struct hshentry*,const struct hshentry*,int)); Xstatic void putforest P((const struct branchhead*)); Xstatic void putree P((const struct hshentry*)); Xstatic void putrunk P((void)); Xstatic void recentdate P((const struct hshentry*,struct Datepairs*)); Xstatic void trunclocks P((void)); X Xstatic const char *insDelFormat; Xstatic int branchflag; /*set on -b */ Xstatic int exitstatus; Xstatic int lockflag; Xstatic int revno; /* number of revision chosen */ Xstatic struct Datepairs *datelist, *duelst; Xstatic struct Revpairs *revlist, *Revlst; Xstatic struct authors *authorlist; Xstatic struct lockers *lockerlist; Xstatic struct stateattri *statelist; X X XmainProg(rlogId, "rlog", "$Id: rlog.c,v 5.5 1990/11/01 05:03:55 eggert Exp $") X{ X static const char cmdusage[] = X "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ..."; X X struct Datepairs *currdate; X const char *accessListString, *accessFormat, *commentFormat; X const char *headFormat, *symbolFormat; X const struct access *curaccess; X const struct assoc *curassoc; X const struct lock *currlock; X int descflag, selectflag; X int onlylockflag; /* print only files with locks */ X int selectop; /* print only some revisions */ X int onlyRCSflag; /* print only RCS file name */ X X initid(); X X descflag = selectflag = true; X onlylockflag = selectop = onlyRCSflag = false; X X while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { X switch ((*argv)[1]) { X X case 'L': X onlylockflag = true; X break; X X case 'R': X onlyRCSflag =true; X break; X X case 'l': X selectop = true; X lockflag = true; X getlocker( (*argv)+2 ); X break; X X case 'b': X selectop = true; X branchflag = true; X break; X X case 'r': X selectop = true; X getrevpairs( (*argv)+2 ); X break; X X case 'd': X selectop = true; X getdatepair( (*argv)+2 ); X break; X X case 's': X selectop = true; X getstate( (*argv)+2); X break; X X case 'w': X selectop = true; X getauthor( (*argv)+2); X break; X X case 'h': X if ( ! selectflag ) warn("-t overrides -h."); X else descflag = false; X break; X X case 't': X selectflag = false; X if ( ! descflag ) warn("-t overrides -h."); X descflag = true; X break; X X case 'V': X setRCSversion(*argv); X break; X X default: X faterror("unknown option: %s%s", *argv, cmdusage); X X }; X } /* end of option processing */ X X if (argc<1) faterror("no input file%s", cmdusage); X X if (RCSversion < VERSION(5)) { X accessListString = "\naccess list: "; X accessFormat = " %s"; X commentFormat = "\ncomment leader: \""; X headFormat = "\nRCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; X insDelFormat = " lines added/del: %lu/%lu"; X symbolFormat = " %s: %s;"; X } else { X accessListString = "\naccess list:"; X accessFormat = "\n\t%s"; X commentFormat = "\ncomment leader: \""; X headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; X insDelFormat = " lines: +%lu -%lu"; X symbolFormat = "\n\t%s: %s"; X } X X /* now handle all filenames */ X do { X finptr = NULL; X ffree(); X X if (!pairfilenames(argc, argv, rcsreadopen, true, false)) X continue; X X /* now RCSfilename contains the name of the RCS file, and finptr X * the file descriptor. Workfilename contains the name of the X * working file. X */ X X /* Keep only those locks given by -l. */ X if (lockflag) X trunclocks(); X X /* do nothing if -L is given and there are no locks*/ X if (onlylockflag && !Locks) X continue; X X if ( onlyRCSflag ) { X aprintf(stdout, "%s\n", RCSfilename); X continue; X } X /* print RCS filename , working filename and optional X administrative information */ X /* could use getfullRCSname() here, but that is very slow */ X aprintf(stdout, headFormat, RCSfilename, workfilename, X Head ? " " : "", Head ? Head->num : "", X Dbranch ? " " : "", Dbranch ? Dbranch : "", X StrictLocks ? " strict" : "" X ); X currlock = Locks; X while( currlock ) { X aprintf(stdout, symbolFormat, currlock->login, X currlock->delta->num); X currlock = currlock->nextlock; X } X if (StrictLocks && RCSversion<VERSION(5)) X aputs(" strict", stdout); X X aputs(accessListString, stdout); /* print access list */ X curaccess = AccessList; X while(curaccess) { X aprintf(stdout, accessFormat, curaccess->login); X curaccess = curaccess->nextaccess; X } X X aputs("\nsymbolic names:", stdout); /* print symbolic names */ X for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) X aprintf(stdout, symbolFormat, curassoc->symbol, curassoc->num); X aputs(commentFormat, stdout); X awrite(Comment.string, Comment.size, stdout); X aputs("\"\n", stdout); X if (VERSION(5)<=RCSversion || Expand != KEYVAL_EXPAND) X aprintf(stdout, "keyword substitution: %s\n", X expand_names[Expand] X ); X X gettree(); X X aprintf(stdout, "total revisions: %d", TotalDeltas); X X if ( Head == nil || !selectflag || !descflag) { X afputc('\n',stdout); X if (descflag) aputs("description:\n", stdout); X getdesc(descflag); X goto rlogend; X } X X X getnumericrev(); /* get numeric revision or branch names */ X revno = 0; X X exttree(Head); X X /* get most recently date of the dates pointed by duelst */ X currdate = duelst; X while( currdate) { X recentdate(Head, currdate); X currdate = currdate->dnext; X } X X extdate(Head); X X /* reinitialize the date specification list */ X currdate = duelst; X while(currdate) { X VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0); X currdate = currdate->dnext; X } X X if ( selectop || ( selectflag && descflag) ) X aprintf(stdout, ";\tselected revisions: %d", revno); X afputc('\n', stdout); X if (descflag) aputs("description:\n", stdout); X getdesc(descflag); X if (selectflag && descflag && revno) { X while (readdeltalog()) X ; X putrunk(); X putree(Head); X if (nexttok != EOFILE) X fatserror("expecting EOF"); X } X rlogend: X aputs("=============================================================================\n",stdout); X } while (cleanup(), X ++argv, --argc >= 1); X exitmain(exitstatus); X} X X static void Xcleanup() X{ X if (nerror) exitstatus = EXIT_FAILURE; X if (finptr) ffclose(finptr); X} X X#if lint X# define exiterr rlogExit X#endif X exiting void Xexiterr() X{ X _exit(EXIT_FAILURE); X} X X X X static void Xputrunk() X/* function: print revisions chosen, which are in trunk */ X X{ X register const struct hshentry *ptr; X X for (ptr = Head; ptr; ptr = ptr->next) X putadelta(ptr, ptr->next, true); X} X X X X static void Xputree(root) X const struct hshentry *root; X/* function: print delta tree (not including trunk) in reverse X order on each branch */ X X{ X if ( root == nil ) return; X X putree(root->next); X X putforest(root->branches); X} X X X X X static void Xputforest(branchroot) X const struct branchhead *branchroot; X/* function: print branches that has the same direct ancestor */ X{ X X if ( branchroot == nil ) return; X X putforest(branchroot->nextbranch); X X putabranch(branchroot->hsh); X putree(branchroot->hsh); X} X X X X X static void Xputabranch(root) X const struct hshentry *root; X/* function : print one branch */ X X{ X X if ( root == nil) return; X X putabranch(root->next); X X putadelta(root, root, false); X} X X X X X X static void Xputadelta(node,editscript,trunk) X register const struct hshentry *node, *editscript; X int trunk; X/* function: Print delta node if node->selector is set. */ X/* editscript indicates where the editscript is stored */ X/* trunk indicated whether this node is in trunk */ X{ X const struct branchhead *newbranch; X struct buf branchnum; X X if (!node->selector) X return; X X aprintf(stdout, X "----------------------------\nrevision %s", node->num X ); X if ( node->lockedby ) X aprintf(stdout, "\tlocked by: %s;", node->lockedby); X X aputs("\ndate: ",stdout); X printdate(stdout, node->date, " "); X aprintf(stdout, "; author: %s; state: %s;", X node->author, node->state X ); X X if ( editscript ) X if(trunk) X aprintf(stdout, insDelFormat, X editscript->deletelns, editscript->insertlns); X else X aprintf(stdout, insDelFormat, X editscript->insertlns, editscript->deletelns); X X newbranch = node->branches; X if ( newbranch ) { X bufautobegin(&branchnum); X aputs("\nbranches:", stdout); X while( newbranch ) { X getbranchno(newbranch->hsh->num, &branchnum); X aprintf(stdout, " %s;", branchnum.string); X newbranch = newbranch->nextbranch; X } X bufautoend(&branchnum); X } X X afputc('\n', stdout); X awrite(node->log.string, node->log.size, stdout); X} X X X X X X static int Xreaddeltalog() X/* Function : get the log message and skip the text of a deltatext node. X * Return false if current block does not start with a number. X * Assumes the current lexeme is not yet in nexttok; does not X * advance nexttok. X */ X{ X register struct hshentry * Delta; X struct buf logbuf; X X nextlex(); X if ( !(Delta = getnum() )) return(false); X getkeystring(Klog); X bufautobegin(&logbuf); X Delta->log = savestring(&logbuf); X /* X * Do the following instead of bufautoend(&logbuf), X * because the buffer must survive until we are done with the file. X */ X Delta->log.string = (char *)fremember(testrealloc( X (malloc_type)logbuf.string, X Delta->log.size X )); X X nextlex(); X while (nexttok==ID && strcmp(NextString,Ktext)!=0) X ignorephrase(); X getkeystring(Ktext); X Delta->insertlns = Delta->deletelns = 0; X if ( Delta != Head) X getscript(Delta); X else X readstring(); X return true; X} X X X X static void Xgetscript(Delta) Xstruct hshentry * Delta; X/* function: read edit script of Delta and count how many lines added */ X/* and deleted in the script */ X X{ X int ed; /* editor command */ X register FILE * fin; X register int c; X register unsigned long i; X struct diffcmd dc; X X fin = finptr; X initdiffcmd(&dc); X while (0 <= (ed = getdiffcmd(fin,SDELIM,(FILE *)0,&dc))) X if (!ed) X Delta->deletelns += dc.nlines; X else { X /* skip scripted lines */ X i = dc.nlines; X Delta->insertlns += i; X do { X while ((c=getc(fin)) != '\n') X if (c==EOF || c==SDELIM && (c=getc(fin))!=SDELIM) { X if (c==EOF || i!=1) X fatserror("unexpected end to edit script"); X nextc = c; X return; X } X ++rcsline; X } while (--i); X } X nextc = getc(fin); X} X X X X X X X X static void Xexttree(root) Xstruct hshentry *root; X/* function: select revisions , starting with root */ X X{ X const struct branchhead *newbranch; X X if (root == nil) return; X X root->selector = extractdelta(root); X exttree(root->next); X X newbranch = root->branches; X while( newbranch ) { X exttree(newbranch->hsh); X newbranch = newbranch->nextbranch; X } X} X X X X X static void Xgetlocker(argv) Xchar * argv; X/* function : get the login names of lockers from command line */ X/* and store in lockerlist. */ X X{ X register char c; X struct lockers * newlocker; X argv--; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X if ( c == '\0') { X lockerlist=nil; X return; X } X X while( c != '\0' ) { X newlocker = talloc(struct lockers); X newlocker->lockerlink = lockerlist; X newlocker->login = argv; X lockerlist = newlocker; X while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' ' X && c != '\t' && c != '\n' && c != ';') ; X *argv = '\0'; X if ( c == '\0' ) return; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X } X} X X X X static void Xgetauthor(argv) Xchar *argv; X/* function: get the author's name from command line */ X/* and store in authorlist */ X X{ X register c; X struct authors * newauthor; X X argv--; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X if ( c == '\0' ) { X authorlist = talloc(struct authors); X authorlist->login = getcaller(); X authorlist->nextauthor = nil; X return; X } X X while( c != '\0' ) { X newauthor = talloc(struct authors); X newauthor->nextauthor = authorlist; X newauthor->login = argv; X authorlist = newauthor; X while( ( c = *++argv) != ',' && c != '\0' && c != ' ' X && c != '\t' && c != '\n' && c != ';') ; X * argv = '\0'; X if ( c == '\0') return; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X } X} X X X X X static void Xgetstate(argv) Xchar * argv; X/* function : get the states of revisions from command line */ X/* and store in statelist */ X X{ X register char c; X struct stateattri *newstate; X X argv--; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X if ( c == '\0'){ X warn("missing state attributes after -s options"); X return; X } X X while( c != '\0' ) { X newstate = talloc(struct stateattri); X newstate->nextstate = statelist; X newstate->status = argv; X statelist = newstate; X while( (c = (*++argv)) != ',' && c != '\0' && c != ' ' X && c != '\t' && c != '\n' && c != ';') ; X *argv = '\0'; X if ( c == '\0' ) return; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X } X} X X X X static void Xtrunclocks() X/* Function: Truncate the list of locks to those that are held by the */ X/* id's on lockerlist. Do not truncate if lockerlist empty. */ X X{ X const struct lockers *plocker; X struct lock * plocked, * nextlocked; X X if ( (lockerlist == nil) || (Locks == nil)) return; X X /* shorten Locks to those contained in lockerlist */ X plocked = Locks; X Locks = nil; X while( plocked != nil) { X plocker = lockerlist; X while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0)) X plocker = plocker->lockerlink; X nextlocked = plocked->nextlock; X if ( plocker != nil) { X plocked->nextlock = Locks; X Locks = plocked; X } X plocked = nextlocked; X } X} X X X X static void Xrecentdate(root, pd) X const struct hshentry *root; X struct Datepairs *pd; X/* function: Finds the delta that is closest to the cutoff date given by */ X/* pd among the revisions selected by exttree. */ X/* Successively narrows down the interval given by pd, */ X/* and sets the strtdate of pd to the date of the selected delta */ X{ X const struct branchhead *newbranch; X X if ( root == nil) return; X if (root->selector) { X if ( cmpnum(root->date, pd->strtdate) >= 0 && X cmpnum(root->date, pd->enddate) <= 0) X VOID strcpy(pd->strtdate, root->date); X } X X recentdate(root->next, pd); X newbranch = root->branches; X while( newbranch) { X recentdate(newbranch->hsh, pd); X newbranch = newbranch->nextbranch; X } X} X X X X X X X static void Xextdate(root) Xstruct hshentry * root; X/* function: select revisions which are in the date range specified */ X/* in duelst and datelist, start at root */ X X{ X const struct branchhead *newbranch; X const struct Datepairs *pdate; X X if ( root == nil) return; X X if ( datelist || duelst) { X pdate = datelist; X while( pdate ) { X if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){ X if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0) X break; X } X pdate = pdate->dnext; X } X if ( pdate == nil) { X pdate = duelst; X for (;;) { X if (!pdate) { X root->selector = false; X break; X } X if ( cmpnum(root->date, pdate->strtdate) == 0) X break; X pdate = pdate->dnext; X } X } X } X if (root->selector) X ++revno; X X extdate(root->next); X X newbranch = root->branches; X while( newbranch ) { X extdate(newbranch->hsh); X newbranch = newbranch->nextbranch; X } X} X X X X static char Xextractdelta(pdelta) X const struct hshentry *pdelta; X/* function: compare information of pdelta to the authorlist, lockerlist,*/ X/* statelist, revlist and yield true if pdelta is selected. */ X X{ X const struct lock *plock; X const struct stateattri *pstate; X const struct authors *pauthor; X const struct Revpairs *prevision; X unsigned length; X X if ((pauthor = authorlist)) /* only certain authors wanted */ X while (strcmp(pauthor->login, pdelta->author) != 0) X if (!(pauthor = pauthor->nextauthor)) X return false; X if ((pstate = statelist)) /* only certain states wanted */ X while (strcmp(pstate->status, pdelta->state) != 0) X if (!(pstate = pstate->nextstate)) X return false; X if (lockflag) /* only locked revisions wanted */ X for (plock = Locks; ; plock = plock->nextlock) X if (!plock) X return false; X else if (plock->delta == pdelta) X break; X if ((prevision = Revlst)) /* only certain revs or branches wanted */ X for (;;) { X length = prevision->numfld; X if ( X countnumflds(pdelta->num) == length+(length&1) && X 0 <= compartial(pdelta->num, prevision->strtrev, length) && X 0 <= compartial(prevision->endrev, pdelta->num, length) X ) X break; X if (!(prevision = prevision->rnext)) X return false; X } X return true; X} X X X X static void Xgetdatepair(argv) X char * argv; X/* function: get time range from command line and store in datelist if */ X/* a time range specified or in duelst if a time spot specified */ X X{ X register char c; X struct Datepairs * nextdate; X const char * rawdate; X int switchflag; X X argv--; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X if ( c == '\0' ) { X warn("missing date/time after -d"); X return; X } X X while( c != '\0' ) { X switchflag = false; X nextdate = talloc(struct Datepairs); X if ( c == '<' ) { /* case: -d <date */ X c = *++argv; X (nextdate->strtdate)[0] = '\0'; X } else if (c == '>') { /* case: -d'>date' */ X c = *++argv; X (nextdate->enddate)[0] = '\0'; X switchflag = true; X } else { X rawdate = argv; X while( c != '<' && c != '>' && c != ';' && c != '\0') X c = *++argv; X *argv = '\0'; X if ( c == '>' ) switchflag=true; X str2date(rawdate, X switchflag ? nextdate->enddate : nextdate->strtdate); X if ( c == ';' || c == '\0') { /* case: -d date */ X VOID strcpy(nextdate->enddate,nextdate->strtdate); X VOID sprintf(nextdate->strtdate,DATEFORM,0,0,0,0,0,0); X nextdate->dnext = duelst; X duelst = nextdate; X goto end; X } else { X /* case: -d date< or -d date>; see switchflag */ X while ( (c= *++argv) == ' ' || c=='\t' || c=='\n'); X if ( c == ';' || c == '\0') { X /* second date missing */ X if (switchflag) X *nextdate->strtdate= '\0'; X else X *nextdate->enddate= '\0'; X nextdate->dnext = datelist; X datelist = nextdate; X goto end; X } X } X } X rawdate = argv; X while( c != '>' && c != '<' && c != ';' && c != '\0') X c = *++argv; X *argv = '\0'; X str2date(rawdate, X switchflag ? nextdate->strtdate : nextdate->enddate); X nextdate->dnext = datelist; X datelist = nextdate; X end: X if ( c == '\0') return; X while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n'); X } X} X X X X static void Xgetnumericrev() X/* function: get the numeric name of revisions which stored in revlist */ X/* and then stored the numeric names in Revlst */ X/* if branchflag, also add default branch */ X X{ X struct Revpairs * ptr, *pt; X unsigned n; X struct buf s, e; X const struct buf *rstart, *rend; X X Revlst = nil; X ptr = revlist; X bufautobegin(&s); X bufautobegin(&e); X while( ptr ) { X n = 0; X rstart = &s; X rend = &e; X X switch (ptr->numfld) { X X case 1: /* -r rev */ X if (expandsym(ptr->strtrev, &s)) { X rend = &s; X n = countnumflds(s.string); X } X break; X X case 2: /* -r rev- */ X if (expandsym(ptr->strtrev, &s)) { X bufscpy(&e, s.string); X n = countnumflds(s.string); X (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; X } X break; X X case 3: /* -r -rev */ X if (expandsym(ptr->endrev, &e)) { X if ((n = countnumflds(e.string)) < 2) X bufscpy(&s, ".1"); X else { X bufscpy(&s, e.string); X VOID strcpy(strrchr(s.string,'.'), ".1"); X } X } X break; X X default: /* -r rev1-rev2 */ X if ( X expandsym(ptr->strtrev, &s) X && expandsym(ptr->endrev, &e) X && checkrevpair(s.string, e.string) X ) { X n = countnumflds(s.string); X /* Swap if out of order. */ X if (compartial(s.string,e.string,n) > 0) { X rstart = &e; X rend = &s; X } X } X break; X } X X if (n) { X pt = ftalloc(struct Revpairs); X pt->numfld = n; X pt->strtrev = fstrsave(rstart->string); X pt->endrev = fstrsave(rend->string); X pt->rnext = Revlst; X Revlst = pt; X } X ptr = ptr->rnext; X } X /* Now take care of branchflag */ X if (branchflag && (Dbranch||Head)) { X pt = ftalloc(struct Revpairs); X pt->strtrev = pt->endrev = X Dbranch ? Dbranch : fstrsave(partialno(&s,Head->num,1)); X pt->rnext=Revlst; Revlst=pt; X pt->numfld = countnumflds(pt->strtrev); X } X bufautoend(&s); X bufautoend(&e); X} X X X X static int Xcheckrevpair(num1,num2) X const char *num1, *num2; X/* function: check whether num1, num2 are legal pair,i.e. X only the last field are different and have same number of X fields( if length <= 2, may be different if first field) */ X X{ X unsigned length = countnumflds(num1); X X if ( X countnumflds(num2) != length X || 2 < length && compartial(num1, num2, length-1) != 0 X ) { X error("invalid branch or revision pair %s : %s", num1, num2); X return false; X } X X return true; X} X X X X static void Xgetrevpairs(argv) Xregister char * argv; X/* function: get revision or branch range from command line, and */ X/* store in revlist */ X X{ X register char c; X struct Revpairs * nextrevpair; X int flag; X X argv--; X while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') ; X if ( c == '\0' ) { X warn("missing revision or branch number after -r"); X return; X } X X while( c != '\0') { X while( c == ',' || c == ' ' || c == '\t' || X c == '\n' || c == ';') c = *++argv; X if (c == '\0') return; X nextrevpair = talloc(struct Revpairs); X nextrevpair->rnext = revlist; X revlist = nextrevpair; X nextrevpair->numfld = 0; X nextrevpair->strtrev = nil; X nextrevpair->endrev = nil; X flag = false; X if ( c == '<' || c == '-' ) { /* case: -r -rev or -r <rev */ X flag = true; X while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ; X } X else { X nextrevpair->strtrev = argv; X /* get a revision or branch name */ X while( c != ',' && c != ';' && c != ' ' && c != '\0' && c != '-' X && c != '\t' && c != '\n' && c != '<') c = *++argv; X X *argv = '\0'; X X if ( c != '<' && c != '-') { /* case: rev */ X nextrevpair->numfld = 1; X continue; X } X X if ( (c =(*++argv)) == ',' || c == '\0' || c == ' ' X || c == '\t' || c == '\n' || c == ';') {/* case: rev_ */ X nextrevpair->numfld = 2; X continue; X } X } X nextrevpair->endrev = argv; X while( c != ',' && c != ' ' && c != '\0' && c != '\t' && c != '<' X && c != '\n' && c != '-' && c != ';') c = *++argv; X X * argv = '\0'; X if ( c == '<'){ X error("separator expected near %s", nextrevpair->endrev); X while( (c = *++argv) != ',' && c != ' ' && c != '\0' && X c != '\t' && c != '\n' && c != ';' ) ; X revlist = nextrevpair->rnext; X continue; X } X else { X if (flag) /* case: -rev */ X nextrevpair->numfld = 3; X X else /* rev1-rev2 appears */ X nextrevpair->numfld = 4; X } X } X} END_OF_FILE if test 32911 -ne `wc -c <'src/rlog.c'`; then echo shar: \"'src/rlog.c'\" unpacked with wrong size! fi # end of 'src/rlog.c' fi echo shar: End of archive 4 \(of 12\). cp /dev/null ark4isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 12 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.