home *** CD-ROM | disk | FTP | other *** search
- 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...
-