[comp.sources.unix] v24i004: RCS source control system, Part04/12

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.