v24i004: RCS source control system, Part04/12
Rich Salz
rsalz at uunet.uu.net
Fri Feb 22 06:58:55 AEST 1991
Submitted-by: Adam Hammer <hammer at 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 at uunet.uu.net if you want that tool.
# Contents: src/rcsutil.c src/rlog.c
# Wrapped by rsalz at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list