home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume24
/
rcs
/
part07
< prev
next >
Wrap
Text File
|
1991-03-05
|
54KB
|
1,805 lines
Subject: v24i007: RCS source control system, Part07/12
Newsgroups: comp.sources.unix
Approved: rsalz@uunet.UU.NET
X-Checksum-Snefru: 5267d696 2667712a 5d7f1b27 d40cfeda
Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
Posting-number: Volume 24, Issue 7
Archive-name: rcs/part07
#! /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/co.c src/rcsfcmp.c src/rcsrev.c
# Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:37:02 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 7 (of 12)."'
if test -f 'src/co.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/co.c'\"
else
echo shar: Extracting \"'src/co.c'\" \(21836 characters\)
sed "s/^X//" >'src/co.c' <<'END_OF_FILE'
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 * RCS checkout operation
X */
X/*****************************************************************************
X * check out revisions from RCS files
X *****************************************************************************
X */
X
X
X/* $Log: co.c,v $
X * Revision 5.6 1990/12/04 05:18:38 eggert
X * Don't checkaccesslist() unless necessary.
X * Use -I for prompts and -q for diagnostics.
X *
X * Revision 5.5 1990/11/01 05:03:26 eggert
X * Fix -j. Add -I.
X *
X * Revision 5.4 1990/10/04 06:30:11 eggert
X * Accumulate exit status across files.
X *
X * Revision 5.3 1990/09/11 02:41:09 eggert
X * co -kv yields a readonly working file.
X *
X * Revision 5.2 1990/09/04 08:02:13 eggert
X * Standardize yes-or-no procedure.
X *
X * Revision 5.0 1990/08/22 08:10:02 eggert
X * Permit multiple locks by same user. Add setuid support.
X * Remove compile-time limits; use malloc instead.
X * Permit dates past 1999/12/31. Switch to GMT.
X * Make lock and temp files faster and safer.
X * Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
X *
X * Revision 4.7 89/05/01 15:11:41 narten
X * changed copyright header to reflect current distribution rules
X *
X * Revision 4.6 88/08/09 19:12:15 eggert
X * Fix "co -d" core dump; rawdate wasn't always initialized.
X * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
X *
X * Revision 4.5 87/12/18 11:35:40 narten
X * lint cleanups (from Guy Harris)
X *
X * Revision 4.4 87/10/18 10:20:53 narten
X * Updating version numbers changes relative to 1.1, are actually
X * relative to 4.2
X *
X * Revision 1.3 87/09/24 13:58:30 narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
X * warnings)
X *
X * Revision 1.2 87/03/27 14:21:38 jenkins
X * Port to suns
X *
X * Revision 4.2 83/12/05 13:39:48 wft
X * made rewriteflag external.
X *
X * Revision 4.1 83/05/10 16:52:55 wft
X * Added option -u and -f.
X * Added handling of default branch.
X * Replaced getpwuid() with getcaller().
X * Removed calls to stat(); now done by pairfilenames().
X * Changed and renamed rmoldfile() to rmworkfile().
X * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
X *
X * Revision 3.7 83/02/15 15:27:07 wft
X * Added call to fastcopy() to copy remainder of RCS file.
X *
X * Revision 3.6 83/01/15 14:37:50 wft
X * Added ignoring of interrupts while RCS file is renamed; this avoids
X * deletion of RCS files during the unlink/link window.
X *
X * Revision 3.5 82/12/08 21:40:11 wft
X * changed processing of -d to use DATEFORM; removed actual from
X * call to preparejoin; re-fixed printing of done at the end.
X *
X * Revision 3.4 82/12/04 18:40:00 wft
X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
X * Fixed printing of "done".
X *
X * Revision 3.3 82/11/28 22:23:11 wft
X * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
X * %02d with %.2d, mode generation for working file with WORKMODE.
X * Fixed nil printing. Fixed -j combined with -l and -p, and exit
X * for non-existing revisions in preparejoin().
X *
X * Revision 3.2 82/10/18 20:47:21 wft
X * Mode of working file is now maintained even for co -l, but write permission
X * is removed.
X * The working file inherits its mode from the RCS file, plus write permission
X * for the owner. The write permission is not given if locking is strict and
X * co does not lock.
X * An existing working file without write permission is deleted automatically.
X * Otherwise, co asks (empty answer: abort co).
X * Call to getfullRCSname() added, check for write error added, call
X * for getlogin() fixed.
X *
X * Revision 3.1 82/10/13 16:01:30 wft
X * fixed type of variables receiving from getc() (char -> int).
X * removed unused variables.
X */
X
X
X
X
X#include "rcsbase.h"
X
Xstatic const char *getancestor P((const char*,const char*));
Xstatic int buildjoin P((const char*));
Xstatic int creatempty P((void));
Xstatic int fixworkmode P((const char*));
Xstatic int preparejoin P((void));
Xstatic int rmlock P((const struct hshentry*));
Xstatic int rmworkfile P((void));
Xstatic void cleanup P((void));
X
Xstatic const char quietarg[] = "-q";
X
Xstatic const char *join, *versionarg;
Xstatic const char *joinlist[joinlength];/* revisions to be joined */
Xstatic int exitstatus;
Xstatic int forceflag, tostdout;
Xstatic int lastjoin; /* index of last element in joinlist */
Xstatic int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
Xstatic struct hshentries *gendeltas; /* deltas to be generated */
Xstatic struct hshentry *targetdelta; /* final delta to be generated */
X
XmainProg(coId, "co", "$Id: co.c,v 5.6 1990/12/04 05:18:38 eggert Exp $")
X{
X static const char cmdusage[] =
X "\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ...";
X
X const char *author, *date, *rev, *state;
X const char *neworkfilename;
X int changelock; /* 1 if a lock has been changed, -1 if error */
X int expmode, r;
X struct buf numericrev; /* expanded revision number */
X char finaldate[datesize];
X
X initid();
X catchints();
X author = date = rev = state = nil;
X bufautobegin(&numericrev);
X expmode = -1;
X versionarg = nil;
X
X while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
X switch ((*argv)[1]) {
X
X case 'r':
X revno: if ((*argv)[2]!='\0') {
X if (rev) warn("redefinition of revision number");
X rev = (*argv)+2;
X }
X break;
X
X case 'f':
X forceflag=true;
X goto revno;
X
X case 'l':
X if (lockflag < 0) {
X warn("-l overrides -u.");
X }
X lockflag = 1;
X goto revno;
X
X case 'u':
X if (0 < lockflag) {
X warn("-l overrides -u.");
X }
X lockflag = -1;
X goto revno;
X
X case 'p':
X tostdout=true;
X goto revno;
X
X case 'I':
X interactiveflag = true;
X goto revno;
X
X case 'q':
X quietflag=true;
X goto revno;
X
X case 'd':
X if (date)
X redefined('d');
X str2date(*argv+2, finaldate);
X date=finaldate;
X break;
X
X case 'j':
X if ((*argv)[2]!='\0'){
X if (join) redefined('j');
X join = (*argv)+2;
X }
X break;
X
X case 's':
X if ((*argv)[2]!='\0'){
X if (state) redefined('s');
X state = (*argv)+2;
X }
X break;
X
X case 'w':
X if (author) redefined('w');
X if ((*argv)[2]!='\0')
X author = (*argv)+2;
X else
X author = getcaller();
X break;
X
X case 'V':
X if (versionarg) redefined('V');
X versionarg = *argv;
X setRCSversion(versionarg);
X break;
X
X case 'k': /* set keyword expand mode */
X if (0 <= expmode) redefined('k');
X if (0 <= (expmode = str2expmode(*argv+2)))
X break;
X /* fall into */
X default:
X faterror("unknown option: %s%s", *argv, cmdusage);
X
X };
X } /* end of option processing */
X
X if (argc<1) faterror("no input file%s", cmdusage);
X
X /* now handle all filenames */
X do {
X finptr=frewrite=NULL;
X fcopy = foutptr = NULL;
X ffree();
X
X if (!pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, tostdout))
X continue;
X
X /* now RCSfilename contains the name of the RCS file, and finptr
X * the file descriptor. If tostdout is false, workfilename contains
X * the name of the working file, otherwise undefined (not nil!).
X * Also, RCSstat has been set.
X */
X diagnose("%s --> %s\n", RCSfilename,tostdout?"stdout":workfilename);
X
X if (!tostdout) {
X if (!getworkstat()) continue; /* give up */
X if (!initeditfiles(workfilename)) {
X if (errno == EACCES)
X error("%s: parent directory isn't writable",
X workfilename
X );
X else
X eerror(resultfile);
X continue;
X }
X }
X if (0 <= expmode)
X Expand = expmode;
X if (0 < lockflag && Expand == VAL_EXPAND) {
X error("cannot combine -kv and -l");
X continue;
X }
X
X gettree(); /* reads in the delta tree */
X
X if (Head==nil) {
X /* no revisions; create empty file */
X diagnose("no revisions present; generating empty revision 0.0\n");
X if (!tostdout)
X if (!creatempty()) continue;
X /* Can't reserve a delta, so don't call addlock */
X } else {
X if (rev!=nil) {
X /* expand symbolic revision number */
X if (!expandsym(rev, &numericrev))
X continue;
X } else
X switch (lockflag<0 ? findlock(false,&targetdelta) : 0) {
X default:
X continue;
X case 0:
X bufscpy(&numericrev, Dbranch?Dbranch:"");
X break;
X case 1:
X bufscpy(&numericrev, targetdelta->num);
X break;
X }
X /* get numbers of deltas to be generated */
X if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
X continue;
X /* check reservations */
X changelock = 0;
X if (lockflag) {
X changelock =
X lockflag<0 ? rmlock(targetdelta) : addlock(targetdelta);
X if (changelock) {
X if (changelock<0 || !checkaccesslist())
X continue;
X } else {
X ffclose(frewrite); frewrite=NULL;
X seteid();
X ignoreints();
X r = unlink(newRCSfilename);
X keepdirtemp(newRCSfilename);
X restoreints();
X setrid();
X if (r != 0) {
X eerror(RCSfilename);
X continue;
X }
X }
X }
X
X if (join && !preparejoin()) continue;
X
X diagnose("revision %s%s\n",targetdelta->num,
X 0<lockflag ? " (locked)" :
X lockflag<0 ? " (unlocked)" : "");
X
X /* remove old working file if necessary */
X if (!tostdout)
X if (!rmworkfile()) continue;
X
X /* prepare for rewriting the RCS file */
X if (changelock) {
X putadmin(frewrite);
X puttree(Head,frewrite);
X aprintf(frewrite, "\n\n%s%c",Kdesc,nextc);
X foutptr = frewrite;
X }
X
X /* skip description */
X getdesc(false); /* don't echo*/
X
X locker_expansion = 0 < lockflag;
X if (!(neworkfilename=buildrevision(gendeltas,targetdelta,
X tostdout,Expand!=OLD_EXPAND)))
X continue;
X
X if (changelock && !nerror) {
X /* rewrite the rest of the RCSfile */
X fastcopy(finptr,frewrite);
X ffclose(finptr); finptr=NULL; /*Help the file system.*/
X ffclose(frewrite); frewrite=NULL;
X seteid();
X if ((r = chmod(newRCSfilename, RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH))) == 0) {
X ignoreints();
X r = re_name(newRCSfilename,RCSfilename);
X keepdirtemp(newRCSfilename);
X restoreints();
X }
X setrid();
X if (r != 0) {
X eerror(RCSfilename);
X error("saved in %s", newRCSfilename);
X dirtempunlink();
X break;
X }
X }
X
X if (join) {
X if (!buildjoin(neworkfilename)) continue;
X }
X if (!tostdout) {
X if (!fixworkmode(neworkfilename))
X continue;
X ignoreints();
X r = re_name(neworkfilename,workfilename);
X keepdirtemp(neworkfilename);
X restoreints();
X if (r != 0) {
X eerror(workfilename);
X error("see %s", neworkfilename);
X continue;
X }
X }
X }
X if (!tostdout) diagnose("done\n");
X } while (cleanup(),
X ++argv, --argc >=1);
X
X tempunlink();
X exitmain(exitstatus);
X
X} /* end of main (co) */
X
X static void
Xcleanup()
X{
X if (nerror) exitstatus = EXIT_FAILURE;
X if (finptr) ffclose(finptr);
X if (frewrite) ffclose(frewrite);
X dirtempunlink();
X}
X
X#if lint
X# define exiterr coExit
X#endif
X exiting void
Xexiterr()
X{
X dirtempunlink();
X tempunlink();
X _exit(EXIT_FAILURE);
X}
X
X
X/*****************************************************************
X * The following routines are auxiliary routines
X *****************************************************************/
X
X static int
Xrmworkfile()
X/* Function: prepares to remove workfilename, if it exists, and if
X * it is read-only.
X * Otherwise (file writable):
X * if !quietmode asks the user whether to really delete it (default: fail);
X * otherwise failure.
X * Returns 0 on failure to get permission, -1 if there's nothing to remove,
X * 1 if there is a file to remove.
X */
X{
X if (haveworkstat) /* File doesn't exist; set by pairfilenames*/
X return -1;
X
X if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
X /* File is writable */
X if (!yesorno(false, "writable %s exists; remove it? [ny](n): ",
X workfilename
X )) {
X error(!quietflag && ttystdin()
X ? "checkout aborted"
X : "writable %s exists; checkout aborted", workfilename);
X return 0;
X }
X }
X /* Actual unlink is done later by caller. */
X return 1;
X}
X
X static int
Xfixworkmode(f)
X const char *f;
X{
X if (
X chmod(f, WORKMODE(RCSstat.st_mode,
X !(Expand==VAL_EXPAND || lockflag<=0 && StrictLocks)
X )) < 0
X ) {
X eerror(workfilename);
X return false;
X }
X return true;
X}
X
X
X static int
Xcreatempty()
X/* Function: creates an empty working file.
X * First, removes an existing working file with rmworkfile().
X */
X{
X int fdesc; /* file descriptor */
X int s;
X
X if (!(s = rmworkfile()))
X return false;
X if (0 < s && unlink(workfilename) != 0) {
X eerror(workfilename);
X return false;
X }
X fdesc=creat(workfilename,0);
X if (fdesc < 0)
X efaterror(workfilename);
X VOID close(fdesc); /* empty file */
X return fixworkmode(workfilename);
X}
X
X
X static int
Xrmlock(delta)
X const struct hshentry *delta;
X/* Function: removes the lock held by caller on delta.
X * Returns -1 if someone else holds the lock,
X * 0 if there is no lock on delta,
X * and 1 if a lock was found and removed.
X */
X{ register struct lock * next, * trail;
X const char *num;
X struct lock dummy;
X int whomatch, nummatch;
X
X num=delta->num;
X dummy.nextlock=next=Locks;
X trail = &dummy;
X while (next!=nil) {
X whomatch = strcmp(getcaller(), next->login);
X nummatch=strcmp(num,next->delta->num);
X if ((whomatch==0) && (nummatch==0)) break;
X /*found a lock on delta by caller*/
X if ((whomatch!=0)&&(nummatch==0)) {
X error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
X return -1;
X }
X trail=next;
X next=next->nextlock;
X }
X if (next!=nil) {
X /*found one; delete it */
X trail->nextlock=next->nextlock;
X Locks=dummy.nextlock;
X next->delta->lockedby=nil; /* reset locked-by */
X return 1; /*success*/
X } else return 0; /*no lock on delta*/
X}
X
X
X
X
X/*****************************************************************
X * The rest of the routines are for handling joins
X *****************************************************************/
X
X
X static const char *
Xaddjoin(joinrev)
X char *joinrev;
X/* Add joinrev's number to joinlist, yielding address of char past joinrev,
X * or nil if no such revision exists.
X */
X{
X register char *j;
X register const struct hshentry *d;
X char terminator;
X struct buf numrev;
X struct hshentries *joindeltas;
X
X j = joinrev;
X for (;;) {
X switch (*j++) {
X default:
X continue;
X case 0:
X case ' ': case '\t': case '\n':
X case ':': case ',': case ';':
X break;
X }
X break;
X }
X terminator = *--j;
X *j = 0;
X bufautobegin(&numrev);
X d = 0;
X if (expandsym(joinrev, &numrev))
X d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas);
X bufautoend(&numrev);
X *j = terminator;
X if (d) {
X joinlist[++lastjoin] = d->num;
X return j;
X }
X return nil;
X}
X
X static int
Xpreparejoin()
X/* Function: Parses a join list pointed to by join and places pointers to the
X * revision numbers into joinlist.
X */
X{
X register const char *j;
X
X j=join;
X lastjoin= -1;
X for (;;) {
X while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
X if (*j=='\0') break;
X if (lastjoin>=joinlength-2) {
X error("too many joins");
X return(false);
X }
X if (!(j = addjoin(j))) return false;
X while ((*j==' ') || (*j=='\t')) j++;
X if (*j == ':') {
X j++;
X while((*j==' ') || (*j=='\t')) j++;
X if (*j!='\0') {
X if (!(j = addjoin(j))) return false;
X } else {
X error("join pair incomplete");
X return false;
X }
X } else {
X if (lastjoin==0) { /* first pair */
X /* common ancestor missing */
X joinlist[1]=joinlist[0];
X lastjoin=1;
X /*derive common ancestor*/
X if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
X return false;
X } else {
X error("join pair incomplete");
X return false;
X }
X }
X }
X if (lastjoin<1) {
X error("empty join");
X return false;
X } else return true;
X}
X
X
X
X static const char *
Xgetancestor(r1, r2)
X const char *r1, *r2;
X/* Yield the common ancestor of r1 and r2 if successful, nil otherwise.
X * Work reliably only if r1 and r2 are not branch numbers.
X */
X{
X static struct buf t1, t2;
X
X unsigned l1, l2, l3;
X const char *r;
X
X l1 = countnumflds(r1);
X l2 = countnumflds(r2);
X if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) {
X /* not on main trunk or identical */
X l3 = 0;
X while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
X l3 += 2;
X /* This will terminate since r1 and r2 are not the same; see above. */
X if (l3==0) {
X /* no common prefix; common ancestor on main trunk */
X VOID partialno(&t1, r1, l1>2 ? (unsigned)2 : l1);
X VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2);
X r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
X if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
X return r;
X } else if (cmpnumfld(r1, r2, l3+1)!=0)
X return partialno(&t1,r1,l3);
X }
X error("common ancestor of %s and %s undefined", r1, r2);
X return nil;
X}
X
X
X
X static int
Xbuildjoin(initialfile)
X const char *initialfile;
X/* Function: merge pairs of elements in joinlist into initialfile
X * If tostdout is set, copy result to stdout.
X * All unlinking of initialfile, rev2, and rev3 should be done by *tempunlink().
X */
X{
X struct buf commarg;
X struct buf subs;
X const char *rev2, *rev3;
X int i;
X int status;
X const char *cov[8], *mergev[12];
X const char **p;
X
X bufautobegin(&commarg);
X bufautobegin(&subs);
X rev2 = maketemp(0);
X rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
X
X cov[0] = nil;
X /* cov[1] setup below */
X cov[2] = CO;
X /* cov[3] setup below */
X p = &cov[4];
X if (versionarg) *p++ = versionarg;
X *p++ = quietarg;
X *p++ = RCSfilename;
X *p = nil;
X
X mergev[0] = nil;
X mergev[1] = nil;
X mergev[2] = MERGE;
X mergev[3] = mergev[5] = "-L";
X /* rest of mergev setup below */
X
X i=0;
X while (i<lastjoin) {
X /*prepare marker for merge*/
X if (i==0)
X bufscpy(&subs, targetdelta->num);
X else {
X bufscat(&subs, ",");
X bufscat(&subs, joinlist[i-2]);
X bufscat(&subs, ":");
X bufscat(&subs, joinlist[i-1]);
X }
X diagnose("revision %s\n",joinlist[i]);
X bufscpy(&commarg, "-p");
X bufscat(&commarg, joinlist[i]);
X cov[1] = rev2;
X cov[3] = commarg.string;
X if (runv(cov))
X goto badmerge;
X diagnose("revision %s\n",joinlist[i+1]);
X bufscpy(&commarg, "-p");
X bufscat(&commarg, joinlist[i+1]);
X cov[1] = rev3;
X cov[3] = commarg.string;
X if (runv(cov))
X goto badmerge;
X diagnose("merging...\n");
X mergev[4] = subs.string;
X mergev[6] = joinlist[i+1];
X p = &mergev[7];
X if (quietflag) *p++ = quietarg;
X if (lastjoin<=i+2 && tostdout) *p++ = "-p";
X *p++ = initialfile;
X *p++ = rev2;
X *p++ = rev3;
X *p = nil;
X status = runv(mergev);
X if (!WIFEXITED(status) || 1<WEXITSTATUS(status))
X goto badmerge;
X i=i+2;
X }
X bufautoend(&commarg);
X bufautoend(&subs);
X return true;
X
X badmerge:
X nerror++;
X bufautoend(&commarg);
X bufautoend(&subs);
X return false;
X}
END_OF_FILE
if test 21836 -ne `wc -c <'src/co.c'`; then
echo shar: \"'src/co.c'\" unpacked with wrong size!
fi
# end of 'src/co.c'
fi
if test -f 'src/rcsfcmp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/rcsfcmp.c'\"
else
echo shar: Extracting \"'src/rcsfcmp.c'\" \(7674 characters\)
sed "s/^X//" >'src/rcsfcmp.c' <<'END_OF_FILE'
X/*
X * RCS file comparison
X */
X/*****************************************************************************
X * rcsfcmp()
X * Testprogram: define FCMPTEST
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
X/* $Log: rcsfcmp.c,v $
X * Revision 5.5 1990/11/27 09:26:05 eggert
X * Fix comment leader bug.
X *
X * Revision 5.4 1990/11/01 05:03:42 eggert
X * Permit arbitrary data in logs and comment leaders.
X *
X * Revision 5.3 1990/09/11 02:41:15 eggert
X * Don't ignore differences inside keyword strings if -ko is set.
X *
X * Revision 5.1 1990/08/29 07:13:58 eggert
X * Clean old log messages too.
X *
X * Revision 5.0 1990/08/22 08:12:49 eggert
X * Don't append "checked in with -k by " log to logs,
X * so that checking in a program with -k doesn't change it.
X * Ansify and Posixate. Remove lint.
X *
X * Revision 4.5 89/05/01 15:12:42 narten
X * changed copyright header to reflect current distribution rules
X *
X * Revision 4.4 88/08/09 19:12:50 eggert
X * Shrink stdio code size.
X *
X * Revision 4.3 87/12/18 11:40:02 narten
X * lint cleanups (Guy Harris)
X *
X * Revision 4.2 87/10/18 10:33:06 narten
X * updting version number. Changes relative to 1.1 actually relative to
X * 4.1
X *
X * Revision 1.2 87/03/27 14:22:19 jenkins
X * Port to suns
X *
X * Revision 4.1 83/05/10 16:24:04 wft
X * Marker matching now uses trymatch(). Marker pattern is now
X * checked precisely.
X *
X * Revision 3.1 82/12/04 13:21:40 wft
X * Initial revision.
X *
X */
X
X/*
X#define FCMPTEST
X*/
X/* Testprogram; prints out whether two files are identical,
X * except for keywords
X */
X
X#include "rcsbase.h"
X
XlibId(fcmpId, "$Id: rcsfcmp.c,v 5.5 1990/11/27 09:26:05 eggert Exp $")
X
X
X int
Xrcsfcmp(xfname,uxfname,delta)
X const char *xfname, *uxfname;
X const struct hshentry *delta;
X/* Function: compares the files xfname and uxfname. Returns true
X * if xfname has the same contents as uxfname, while disregarding
X * keyword values. For the LOG-keyword, rcsfcmp skips the log message
X * given by the parameter delta in xfname. Thus, rcsfcmp returns true
X * if xfname contains the same as uxfname, with the keywords expanded.
X * Implementation: character-by-character comparison until $ is found.
X * If a $ is found, read in the marker keywords; if they are real keywords
X * and identical, read in keyword value. If value is terminated properly,
X * disregard it and optionally skip log message; otherwise, compare value.
X */
X{
X register int xc,uxc;
X char xkeyword[keylength+2], uxkeyword[keylength+2];
X int eqkeyvals;
X register FILE * xfp, * uxfp;
X register int delimiter;
X register char * tp;
X register const char *sp;
X int result;
X enum markers match1,match2;
X
X errno = 0;
X if (!(xfp=fopen(sp=xfname,"r")) || !(errno=0, uxfp=fopen(sp=uxfname,"r"))) {
X efaterror(sp);
X }
X result=false;
X delimiter = Expand==OLD_EXPAND ? EOF : KDELIM;
X xc=getc(xfp); uxc=getc(uxfp);
X while( xc == uxc) { /* comparison loop */
X if (xc==EOF) { /* finished; everything is the same*/
X result=true;
X break;
X }
X if (xc != delimiter) {
X /* get the next characters */
X xc=getc(xfp); uxc=getc(uxfp);
X } else {
X /* try to get both keywords */
X tp = xkeyword;
X while( (xc=getc(xfp))!=EOF && (tp< xkeyword+keylength) && (xc!='\n')
X && (xc!=KDELIM) && (xc!=VDELIM))
X *tp++ = xc;
X *tp++ = xc; /* add closing K/VDELIM */
X *tp='\0';
X tp = uxkeyword;
X while( (uxc=getc(uxfp))!=EOF && (tp< uxkeyword+keylength) && (uxc!='\n')
X && (uxc!=KDELIM) && (uxc!=VDELIM))
X *tp++ = uxc;
X *tp++ = xc; /* add closing K/VDELIM */
X *tp='\0';
X /* Now we have 2 keywords, or something that looks like it. */
X match1 = trymatch(xkeyword);
X match2 = trymatch(uxkeyword);
X if (match1 != match2) break; /* not identical */
X#ifdef FCMPTEST
X VOID printf("found potential keywords %s and %s\n",xkeyword,uxkeyword);
X#endif
X
X if (match1 == Nomatch) {
X /* not a keyword pattern, but could still be identical */
X if (strcmp(xkeyword,uxkeyword)==0)
X continue;
X else break;
X }
X#ifdef FCMPTEST
X VOID printf("found common keyword %s\n",xkeyword);
X#endif
X eqkeyvals = 1;
X for (;;) {
X if (xc==uxc) {
X if (xc==KDELIM)
X break;
X } else {
X eqkeyvals = 0;
X if (xc==KDELIM) {
X while (uxc!=KDELIM && uxc!='\n' && uxc!=EOF)
X uxc = getc(uxfp);
X break;
X }
X if (uxc==KDELIM) {
X while (xc!=KDELIM && xc!='\n' && xc!=EOF)
X xc = getc(xfp);
X break;
X }
X }
X if (xc=='\n' || uxc=='\n' || xc==EOF || uxc==EOF)
X break;
X xc = getc(xfp);
X uxc = getc(uxfp);
X }
X if (xc!=uxc) break; /* not the same */
X if (xc==KDELIM) {
X xc=getc(xfp); uxc=getc(uxfp); /* skip closing KDELIM */
X /* if the keyword is LOG, also skip the log message in xfp*/
X if (match1==Log) {
X /* first, compute the number of line feeds in log msg */
X unsigned lncnt;
X size_t ls, ccnt;
X lncnt = 3;
X sp = delta->log.string;
X ls = delta->log.size;
X if (sizeof(ciklog)-1<=ls && !strncmp(sp,ciklog,sizeof(ciklog)-1))
X continue; /* this log message wasn't inserted */
X while (ls--) if (*sp++=='\n') lncnt++;
X while(xc!=EOF) {
X if (xc=='\n')
X if(--lncnt==0) break;
X xc=getc(xfp);
X }
X /* skip last comment leader */
X /* Can't just skip another line here, because there may be */
X /* additional characters on the line (after the Log....$) */
X for (ccnt=Comment.size; ccnt--; ) {
X xc=getc(xfp);
X if(xc=='\n') break;
X /* reads to the end of the comment leader or '\n', */
X /* whatever comes first. This is because some editors */
X /* strip off trailing blanks from a leader like " * ". */
X }
X }
X } else {
X /* both end in the same character, but not a KDELIM */
X /* must compare string values.*/
X#ifdef FCMPTEST
X VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
X#endif
X if (!eqkeyvals) break;
X }
X }
X }
X ffclose(xfp); ffclose(uxfp);
X return result;
X}
X
X
X
X#ifdef FCMPTEST
X
Xconst char cmdid[] = "rcsfcmp";
X
Xmain(argc, argv)
Xint argc; char *argv[];
X/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
X * 4th: unexpanded file
X */
X{ struct hshentry delta;
X
X Comment.string = argv[1];
X Comment.size = strlen(argv[1]);
X delta.log.string = argv[2];
X delta.log.size = strlen(argv[2]);
X if (rcsfcmp(argv[3],argv[4],&delta))
X VOID printf("files are the same\n");
X else VOID printf("files are different\n");
X}
X#endif
END_OF_FILE
if test 7674 -ne `wc -c <'src/rcsfcmp.c'`; then
echo shar: \"'src/rcsfcmp.c'\" unpacked with wrong size!
fi
# end of 'src/rcsfcmp.c'
fi
if test -f 'src/rcsrev.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/rcsrev.c'\"
else
echo shar: Extracting \"'src/rcsrev.c'\" \(20500 characters\)
sed "s/^X//" >'src/rcsrev.c' <<'END_OF_FILE'
X/*
X * RCS revision number handling
X */
X
X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
X Copyright 1990 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: rcsrev.c,v $
X * Revision 5.0 1990/08/22 08:13:43 eggert
X * Remove compile-time limits; use malloc instead.
X * Ansify and Posixate. Tune.
X * Remove possibility of an internal error. Remove lint.
X *
X * Revision 4.5 89/05/01 15:13:22 narten
X * changed copyright header to reflect current distribution rules
X *
X * Revision 4.4 87/12/18 11:45:22 narten
X * more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
X * since there's now a return value there with a value. (Guy Harris)
X *
X * Revision 4.3 87/10/18 10:38:42 narten
X * Updating version numbers. Changes relative to version 1.1 actually
X * relative to 4.1
X *
X * Revision 1.3 87/09/24 14:00:37 narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
X * warnings)
X *
X * Revision 1.2 87/03/27 14:22:37 jenkins
X * Port to suns
X *
X * Revision 4.1 83/03/25 21:10:45 wft
X * Only changed $Header to $Id.
X *
X * Revision 3.4 82/12/04 13:24:08 wft
X * Replaced getdelta() with gettree().
X *
X * Revision 3.3 82/11/28 21:33:15 wft
X * fixed compartial() and compnum() for nil-parameters; fixed nils
X * in error messages. Testprogram output shortenend.
X *
X * Revision 3.2 82/10/18 21:19:47 wft
X * renamed compnum->cmpnum, compnumfld->cmpnumfld,
X * numericrevno->numricrevno.
X *
X * Revision 3.1 82/10/11 19:46:09 wft
X * changed expandsym() to check for source==nil; returns zero length string
X * in that case.
X */
X
X
X
X/*
X#define REVTEST
X*/
X/* version REVTEST is for testing the routines that generate a sequence
X * of delta numbers needed to regenerate a given delta.
X */
X
X#include "rcsbase.h"
X
XlibId(revId, "$Id: rcsrev.c,v 5.0 1990/08/22 08:13:43 eggert Exp $")
X
Xstatic struct hshentry *genbranch P((const struct hshentry*,const char*,unsigned,const char*,const char*,const char*,struct hshentries**));
X
X
X
X unsigned
Xcountnumflds(s)
X const char *s;
X/* Given a pointer s to a dotted number (date or revision number),
X * countnumflds returns the number of digitfields in s.
X */
X{
X register const char *sp;
X register unsigned count;
X if ((sp=s)==nil) return(0);
X if (*sp == '\0') return(0);
X count = 1;
X do {
X if (*sp++ == '.') count++;
X } while (*sp);
X if (*(--sp) == '.') count--; /*trailing periods don't count*/
X return(count);
X}
X
X void
Xgetbranchno(revno,branchno)
X const char *revno;
X struct buf *branchno;
X/* Given a non-nil revision number revno, getbranchno copies the number of the branch
X * on which revno is into branchno. If revno itself is a branch number,
X * it is copied unchanged.
X */
X{
X register unsigned numflds;
X register char *tp;
X
X bufscpy(branchno, revno);
X numflds=countnumflds(revno);
X if (!(numflds & 1)) {
X tp = branchno->string;
X while (--numflds)
X while (*tp++ != '.')
X ;
X *(tp-1)='\0';
X }
X}
X
X
X
Xint cmpnum(num1, num2)
X const char *num1, *num2;
X/* compares the two dotted numbers num1 and num2 lexicographically
X * by field. Individual fields are compared numerically.
X * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
X * omitted fields are assumed to be higher than the existing ones.
X*/
X{
X register const char *s1, *s2;
X register int n1, n2;
X
X s1=num1==nil?"":num1;
X s2=num2==nil?"":num2;
X
X do {
X n1 = 0;
X while (isdigit(*s1))
X n1 = n1*10 + (*s1++ - '0');
X /* skip '.' */
X if (*s1=='.') s1++;
X
X n2 = 0;
X while (isdigit(*s2))
X n2 = n2*10 + (*s2++ - '0');
X /* skip '.' */
X if (*s2=='.') s2++;
X
X } while ((n1==n2) && (*s1!='\0') && (*s2!='\0'));
X
X if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2))
X return (n1 - n2);
X /*now n1==n2 and one of s1 or s2 is shorter*/
X /*give precedence to shorter one*/
X if (*s1=='\0') return 1;
X else return -1;
X
X}
X
X
X
Xint cmpnumfld(num1, num2, fld)
X const char *num1, *num2;
X unsigned fld;
X/* compares the two dotted numbers at field fld and returns
X * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields.
X * fld must be positive.
X*/
X{
X register const char *s1, *s2;
X register unsigned n1, n2;
X
X s1 = num1;
X s2 = num2;
X /* skip fld-1 fields */
X for (n1 = fld; (--n1); ) {
X while (*s1++ != '.')
X ;
X while (*s2++ != '.')
X ;
X }
X /* Now s1 and s2 point to the beginning of the respective fields */
X /* compute numerical value and compare */
X n1 = 0;
X while (isdigit(*s1))
X n1 = n1*10 + (*s1++ - '0');
X n2 = 0;
X while (isdigit(*s2))
X n2 = n2*10 + (*s2++ - '0');
X return n1<n2 ? -1 : n1==n2 ? 0 : 1;
X}
X
X
X int
Xcompartial(num1, num2, length)
X const char *num1, *num2;
X unsigned length;
X
X/* compare the first "length" fields of two dot numbers;
X the omitted field is considered to be larger than any number */
X/* restriction: at least one number has length or more fields */
X
X{
X register const char *s1, *s2;
X register int n1, n2;
X
X
X s1 = num1; s2 = num2;
X if ( s1==nil || *s1 == '\0' ) return 1;
X if ( s2==nil || *s2 == '\0' ) return -1;
X
X for (;;) {
X n1 = 0;
X while (isdigit(*s1))
X n1 = n1*10 + (*s1++ - '0');
X if ( *s1 == '.' ) s1++; /* skip . */
X
X n2 = 0;
X while (isdigit(*s2))
X n2 = n2*10 + (*s2++ - '0');
X if (*s2 == '.') s2++;
X
X if ( n1 != n2 ) return n1-n2;
X if ( --length == 0 ) return 0;
X if ( *s1 == '\0' ) return 1;
X if ( *s2 == '\0' ) return -1;
X }
X}
X
X
Xchar * partialno(rev1,rev2,length)
X struct buf *rev1;
X const char *rev2;
X register unsigned length;
X/* Function: Copies length fields of revision number rev2 into rev1.
X * Return rev1's string.
X */
X{
X register char *r1;
X
X bufscpy(rev1, rev2);
X r1 = rev1->string;
X while (length) {
X while (*r1!='.' && *r1)
X ++r1;
X ++r1;
X length--;
X }
X /* eliminate last '.'*/
X *(r1-1)='\0';
X return rev1->string;
X}
X
X
X
X
X static void
Xstore1(store, next)
X struct hshentries ***store;
X struct hshentry *next;
X/*
X * Allocate a new list node that addresses NEXT.
X * Append it to the list that **STORE is the end pointer of.
X */
X{
X register struct hshentries *p;
X
X p = ftalloc(struct hshentries);
X p->first = next;
X **store = p;
X *store = &p->rest;
X}
X
Xstruct hshentry * genrevs(revno,date,author,state,store)
X const char *revno, *date, *author, *state;
X struct hshentries **store;
X/* Function: finds the deltas needed for reconstructing the
X * revision given by revno, date, author, and state, and stores pointers
X * to these deltas into a list whose starting address is given by store.
X * The last delta (target delta) is returned.
X * If the proper delta could not be found, nil is returned.
X */
X{
X unsigned length;
X register struct hshentry * next;
X int result;
X const char *branchnum;
X struct buf t;
X
X bufautobegin(&t);
X
X if (!(next = Head)) {
X error("RCS file empty");
X goto norev;
X }
X
X length = countnumflds(revno);
X
X if (length >= 1) {
X /* at least one field; find branch exactly */
X while ((result=cmpnumfld(revno,next->num,1)) < 0) {
X store1(&store, next);
X next = next->next;
X if (!next) {
X error("branch number %s too low", partialno(&t,revno,1));
X goto norev;
X }
X }
X
X if (result>0) {
X error("branch number %s absent", partialno(&t,revno,1));
X goto norev;
X }
X }
X if (length<=1){
X /* pick latest one on given branch */
X branchnum = next->num; /* works even for empty revno*/
X while ((next!=nil) &&
X (cmpnumfld(branchnum,next->num,1)==0) &&
X !(
X (date==nil?1:(cmpnum(date,next->date)>=0)) &&
X (author==nil?1:(strcmp(author,next->author)==0)) &&
X (state ==nil?1:(strcmp(state, next->state) ==0))
X )
X )
X {
X store1(&store, next);
X next=next->next;
X }
X if ((next==nil) ||
X (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
X error("can't find revision on branch %s with a date before %s, author %s, and state %s",
X length ? revno : partialno(&t,branchnum,1),
X date ? date : "<now>",
X author==nil?"<any>":author, state==nil?"<any>":state);
X goto norev;
X } else {
X store1(&store, next);
X }
X *store = nil;
X return next;
X }
X
X /* length >=2 */
X /* find revision; may go low if length==2*/
X while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
X (cmpnumfld(revno,next->num,1)==0) ) {
X store1(&store, next);
X next = next->next;
X if (!next)
X break;
X }
X
X if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
X error("revision number %s too low", partialno(&t,revno,2));
X goto norev;
X }
X if ((length>2) && (result!=0)) {
X error("revision %s absent", partialno(&t,revno,2));
X goto norev;
X }
X
X /* print last one */
X store1(&store, next);
X
X if (length>2)
X return genbranch(next,revno,length,date,author,state,store);
X else { /* length == 2*/
X if ((date!=nil) && (cmpnum(date,next->date)<0)){
X error("Revision %s has date %s.",next->num, next->date);
X return nil;
X }
X if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
X error("Revision %s has author %s.",next->num,next->author);
X return nil;
X }
X if ((state!=nil)&&(strcmp(state,next->state)!=0)) {
X error("Revision %s has state %s.",next->num,
X next->state==nil?"<empty>":next->state);
X return nil;
X }
X *store=nil;
X return next;
X }
X norev:
X bufautoend(&t);
X return nil;
X}
X
X
X
X
X static struct hshentry *
Xgenbranch(bpoint, revno, length, date, author, state, store)
X const struct hshentry *bpoint;
X const char *revno;
X unsigned length;
X const char *date, *author, *state;
X struct hshentries **store;
X/* Function: given a branchpoint, a revision number, date, author, and state,
X * genbranch finds the deltas necessary to reconstruct the given revision
X * from the branch point on.
X * Pointers to the found deltas are stored in a list beginning with store.
X * revno must be on a side branch.
X * return nil on error
X */
X{
X unsigned field;
X register struct hshentry * next, * trail;
X register const struct branchhead *bhead;
X int result;
X struct buf t;
X
X field = 3;
X bhead = bpoint->branches;
X
X do {
X if (!bhead) {
X bufautobegin(&t);
X error("no side branches present for %s", partialno(&t,revno,field-1));
X bufautoend(&t);
X return nil;
X }
X
X /*find branch head*/
X /*branches are arranged in increasing order*/
X while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
X bhead = bhead->nextbranch;
X if (!bhead) {
X bufautobegin(&t);
X error("branch number %s too high",partialno(&t,revno,field));
X bufautoend(&t);
X return nil;
X }
X }
X
X if (result<0) {
X bufautobegin(&t);
X error("branch number %s absent", partialno(&t,revno,field));
X bufautoend(&t);
X return nil;
X }
X
X next = bhead->hsh;
X if (length==field) {
X /* pick latest one on that branch */
X trail=nil;
X do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) &&
X (author==nil?1:(strcmp(author,next->author)==0)) &&
X (state ==nil?1:(strcmp(state, next->state) ==0))
X ) trail = next;
X next=next->next;
X } while (next!=nil);
X
X if (trail==nil) {
X error("can't find revision on branch %s with a date before %s, author %s, and state %s",
X revno, date==nil?"<now>":date,
X author==nil?"<any>":author, state==nil?"<any>":state);
X return nil;
X } else { /* print up to last one suitable */
X next = bhead->hsh;
X while (next!=trail) {
X store1(&store, next);
X next=next->next;
X }
X store1(&store, next);
X }
X *store = nil;
X return next;
X }
X
X /* length > field */
X /* find revision */
X /* check low */
X if (cmpnumfld(revno,next->num,field+1)<0) {
X bufautobegin(&t);
X error("revision number %s too low", partialno(&t,revno,field+1));
X bufautoend(&t);
X return(nil);
X }
X do {
X store1(&store, next);
X trail = next;
X next = next->next;
X } while ((next!=nil) &&
X (cmpnumfld(revno,next->num,field+1) >=0));
X
X if ((length>field+1) && /*need exact hit */
X (cmpnumfld(revno,trail->num,field+1) !=0)){
X bufautobegin(&t);
X error("revision %s absent", partialno(&t,revno,field+1));
X bufautoend(&t);
X return(nil);
X }
X if (length == field+1) {
X if ((date!=nil) && (cmpnum(date,trail->date)<0)){
X error("Revision %s has date %s.",trail->num, trail->date);
X return nil;
X }
X if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
X error("Revision %s has author %s.",trail->num,trail->author);
X return nil;
X }
X if ((state!=nil)&&(strcmp(state,trail->state)!=0)) {
X error("Revision %s has state %s.",trail->num,
X trail->state==nil?"<empty>":trail->state);
X return nil;
X }
X }
X bhead = trail->branches;
X
X } while ((field+=2) <= length);
X * store = nil;
X return trail;
X}
X
X
X static const char *
Xlookupsym(id)
X const char *id;
X/* Function: looks up id in the list of symbolic names starting
X * with pointer SYMBOLS, and returns a pointer to the corresponding
X * revision number. Returns nil if not present.
X */
X{
X register const struct assoc *next;
X next = Symbols;
X while (next!=nil) {
X if (strcmp(id, next->symbol)==0)
X return next->num;
X else next=next->nextassoc;
X }
X return nil;
X}
X
Xint expandsym(source, target)
X const char *source;
X struct buf *target;
X/* Function: Source points to a revision number. Expandsym copies
X * the number to target, but replaces all symbolic fields in the
X * source number with their numeric values.
X * A trailing '.' is omitted; leading zeroes are compressed.
X * returns false on error;
X */
X{
X register const char *sp;
X register char *tp;
X const char *tlim;
X register enum tokens d;
X
X bufalloc(target, 1);
X tp = target->string;
X sp = source;
X if (sp == nil) { /*accept nil pointer as a legal value*/
X *tp='\0';
X return true;
X }
X tlim = tp + target->size;
X
X while (*sp != '\0') {
X switch (ctab[(unsigned char)*sp]) {
X case DIGIT:
X if (*sp=='0') {
X /* skip leading zeroes */
X sp++;
X while(*sp == '0') sp++;
X if (!*sp || *sp=='.') --sp; /* single zero */
X }
X while (isdigit(*sp)) {
X if (tlim <= tp)
X tp = bufenlarge(target, &tlim);
X *tp++ = *sp++;
X }
X if (tlim <= tp)
X tp = bufenlarge(target, &tlim);
X if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) {
X *tp='\0'; return true;
X }
X if (*sp != '.')
X goto improper;
X *tp++ = *sp++;
X break;
X
X case LETTER:
X case Letter:
X {
X register char *bp = tp;
X register size_t s = tp - target->string;
X do {
X if (tlim <= bp)
X bp = bufenlarge(target, &tlim);
X *bp++ = *sp++;
X } while ((d=ctab[(unsigned char)*sp])==LETTER ||
X d==Letter || d==DIGIT ||
X (d==IDCHAR));
X if (tlim <= bp)
X bp = bufenlarge(target, &tlim);
X *bp= '\0';
X tp = target->string + s;
X }
X {
X register const char *bp = lookupsym(tp);
X if (bp==nil) {
X error("Symbolic number %s is undefined.", tp);
X return false;
X } else { /* copy number */
X do {
X if (tlim <= tp)
X tp = bufenlarge(target, &tlim);
X } while ((*tp++ = *bp++));
X }
X }
X if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0')))
X return true;
X if (*sp++ != '.')
X goto improper;
X tp[-1] = '.';
X break;
X
X default:
X improper:
X error("improper revision number: %s", source);
X return false;
X }
X }
X if (tlim<=tp)
X tp = bufenlarge(target,&tlim);
X *tp = '\0';
X return true;
X}
X
X
X
X#ifdef REVTEST
X
Xconst char cmdid[] = "revtest";
X
X int
Xmain(argc,argv)
Xint argc; char * argv[];
X{
X static struct buf numricrevno;
X char symrevno[100]; /* used for input of revision numbers */
X char author[20];
X char state[20];
X char date[20];
X struct hshentries *gendeltas;
X struct hshentry * target;
X int i;
X
X if (argc<2) {
X aputs("No input file\n",stderr);
X exitmain(EXIT_FAILURE);
X }
X if ((finptr=fopen(argv[1], "r")) == NULL) {
X faterror("can't open input file %s", argv[1]);
X }
X Lexinit();
X getadmin();
X
X gettree();
X
X getdesc(false);
X
X do {
X /* all output goes to stderr, to have diagnostics and */
X /* errors in sequence. */
X aputs("\nEnter revision number or <return> or '.': ",stderr);
X if(gets(symrevno)==NULL) break;
X if (*symrevno == '.') break;
X aprintf(stderr,"%s;\n",symrevno);
X expandsym(symrevno,&numricrevno);
X aprintf(stderr,"expanded number: %s; ",numricrevno.string);
X aprintf(stderr,"Date: ");
X gets(date); aprintf(stderr,"%s; ",date);
X aprintf(stderr,"Author: ");
X gets(author); aprintf(stderr,"%s; ",author);
X aprintf(stderr,"State: ");
X gets(state); aprintf(stderr, "%s;\n", state);
X target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil,
X *state?state:(char*)nil, &gendeltas);
X if (target!=nil) {
X while (gendeltas) {
X aprintf(stderr,"%s\n",gendeltas->first->num);
X gendeltas = gendeltas->next;
X }
X }
X } while (true);
X aprintf(stderr,"done\n");
X exitmain(EXIT_SUCCESS);
X}
X
Xexiting void exiterr() { _exit(EXIT_FAILURE); }
X
X#endif
END_OF_FILE
if test 20500 -ne `wc -c <'src/rcsrev.c'`; then
echo shar: \"'src/rcsrev.c'\" unpacked with wrong size!
fi
# end of 'src/rcsrev.c'
fi
echo shar: End of archive 7 \(of 12\).
cp /dev/null ark7isdone
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...