111894Speter/* Print log messages and other information about RCS files.  */
29Sjkh
311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
411894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
59Sjkh   Distributed under license by the Free Software Foundation, Inc.
69Sjkh
79SjkhThis file is part of RCS.
89Sjkh
99SjkhRCS is free software; you can redistribute it and/or modify
109Sjkhit under the terms of the GNU General Public License as published by
119Sjkhthe Free Software Foundation; either version 2, or (at your option)
129Sjkhany later version.
139Sjkh
149SjkhRCS is distributed in the hope that it will be useful,
159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of
169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179SjkhGNU General Public License for more details.
189Sjkh
199SjkhYou should have received a copy of the GNU General Public License
2011894Speteralong with RCS; see the file COPYING.
2111894SpeterIf not, write to the Free Software Foundation,
2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
239Sjkh
249SjkhReport problems and direct all questions to:
259Sjkh
269Sjkh    rcs-bugs@cs.purdue.edu
279Sjkh
289Sjkh*/
299Sjkh
3011894Speter/*
3111894Speter * Revision 5.18  1995/06/16 06:19:24  eggert
3211894Speter * Update FSF address.
338858Srgrimes *
3411894Speter * Revision 5.17  1995/06/01 16:23:43  eggert
3511894Speter * (struct rcslockers): Renamed from `struct lockers'.
3611894Speter * (getnumericrev): Return error indication instead of ignoring errors.
3711894Speter * (main): Check it.  Don't use dateform.
3811894Speter * (recentdate, extdate): cmpnum -> cmpdate
391487Sphk *
4011894Speter * Revision 5.16  1994/04/13 16:30:34  eggert
4111894Speter * Fix bug; `rlog -lxxx' inverted the sense of -l.
421486Sphk *
4311894Speter * Revision 5.15  1994/03/17 14:05:48  eggert
4411894Speter * -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it.
4511894Speter * Emulate -V4's white space generation more precisely.
4611894Speter * Work around SVR4 stdio performance bug.  Remove lint.
471485Sphk *
4811894Speter * Revision 5.14  1993/11/09 17:40:15  eggert
4911894Speter * -V now prints version on stdout and exits.
50246Snate *
5111894Speter * Revision 5.13  1993/11/03 17:42:27  eggert
5211894Speter * Add -N, -z.  Ignore -T.
5311894Speter *
5411894Speter * Revision 5.12  1992/07/28  16:12:44  eggert
5511894Speter * Don't miss B.0 when handling branch B.  Diagnose missing `,' in -r.
5611894Speter * Add -V.  Avoid `unsigned'.  Statement macro names now end in _.
5711894Speter *
5811894Speter * Revision 5.11  1992/01/24  18:44:19  eggert
5911894Speter * Don't duplicate unexpected_EOF's function.  lint -> RCS_lint
6011894Speter *
6111894Speter * Revision 5.10  1992/01/06  02:42:34  eggert
6211894Speter * Update usage string.
6311894Speter * while (E) ; -> while (E) continue;
6411894Speter *
659Sjkh * Revision 5.9  1991/09/17  19:07:40  eggert
669Sjkh * Getscript() didn't uncache partial lines.
679Sjkh *
689Sjkh * Revision 5.8  1991/08/19  03:13:55  eggert
699Sjkh * Revision separator is `:', not `-'.
709Sjkh * Check for missing and duplicate logs.  Tune.
719Sjkh * Permit log messages that do not end in newline (including empty logs).
729Sjkh *
739Sjkh * Revision 5.7  1991/04/21  11:58:31  eggert
749Sjkh * Add -x, RCSINIT, MS-DOS support.
759Sjkh *
769Sjkh * Revision 5.6  1991/02/26  17:07:17  eggert
779Sjkh * Survive RCS files with missing logs.
789Sjkh * strsave -> str_save (DG/UX name clash)
799Sjkh *
809Sjkh * Revision 5.5  1990/11/01  05:03:55  eggert
819Sjkh * Permit arbitrary data in logs and comment leaders.
829Sjkh *
839Sjkh * Revision 5.4  1990/10/04  06:30:22  eggert
849Sjkh * Accumulate exit status across files.
859Sjkh *
869Sjkh * Revision 5.3  1990/09/11  02:41:16  eggert
879Sjkh * Plug memory leak.
889Sjkh *
899Sjkh * Revision 5.2  1990/09/04  08:02:33  eggert
909Sjkh * Count RCS lines better.
919Sjkh *
929Sjkh * Revision 5.0  1990/08/22  08:13:48  eggert
939Sjkh * Remove compile-time limits; use malloc instead.  Add setuid support.
949Sjkh * Switch to GMT.
959Sjkh * Report dates in long form, to warn about dates past 1999/12/31.
969Sjkh * Change "added/del" message to make room for the longer dates.
979Sjkh * Don't generate trailing white space.  Add -V.  Ansify and Posixate.
989Sjkh *
999Sjkh * Revision 4.7  89/05/01  15:13:48  narten
1009Sjkh * changed copyright header to reflect current distribution rules
1018858Srgrimes *
1029Sjkh * Revision 4.6  88/08/09  19:13:28  eggert
1039Sjkh * Check for memory exhaustion; don't access freed storage.
1049Sjkh * Shrink stdio code size; remove lint.
1058858Srgrimes *
1069Sjkh * Revision 4.5  87/12/18  11:46:38  narten
1079Sjkh * more lint cleanups (Guy Harris)
1088858Srgrimes *
1099Sjkh * Revision 4.4  87/10/18  10:41:12  narten
1109Sjkh * Updating version numbers
1119Sjkh * Changes relative to 1.1 actually relative to 4.2
1128858Srgrimes *
1139Sjkh * Revision 1.3  87/09/24  14:01:10  narten
1148858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1159Sjkh * warnings)
1168858Srgrimes *
1179Sjkh * Revision 1.2  87/03/27  14:22:45  jenkins
1189Sjkh * Port to suns
1198858Srgrimes *
1209Sjkh * Revision 4.2  83/12/05  09:18:09  wft
1219Sjkh * changed rewriteflag to external.
1228858Srgrimes *
1239Sjkh * Revision 4.1  83/05/11  16:16:55  wft
1249Sjkh * Added -b, updated getnumericrev() accordingly.
1259Sjkh * Replaced getpwuid() with getcaller().
1268858Srgrimes *
1279Sjkh * Revision 3.7  83/05/11  14:24:13  wft
1289Sjkh * Added options -L and -R;
1299Sjkh * Fixed selection bug with -l on multiple files.
1309Sjkh * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
1318858Srgrimes *
1329Sjkh * Revision 3.6  82/12/24  15:57:53  wft
1339Sjkh * shortened output format.
1349Sjkh *
1359Sjkh * Revision 3.5  82/12/08  21:45:26  wft
1369Sjkh * removed call to checkaccesslist(); used DATEFORM to format all dates;
1379Sjkh * removed unused variables.
1389Sjkh *
1399Sjkh * Revision 3.4  82/12/04  13:26:25  wft
1409Sjkh * Replaced getdelta() with gettree(); removed updating of field lockedby.
1419Sjkh *
1429Sjkh * Revision 3.3  82/12/03  14:08:20  wft
1439Sjkh * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
1449Sjkh * Fixed printing of nil, removed printing of Suffix,
1459Sjkh * added shortcut if no revisions are printed, disambiguated struct members.
1469Sjkh *
1479Sjkh * Revision 3.2  82/10/18  21:09:06  wft
1489Sjkh * call to curdir replaced with getfullRCSname(),
1499Sjkh * fixed call to getlogin(), cosmetic changes on output,
1509Sjkh * changed conflicting long identifiers.
1519Sjkh *
1529Sjkh * Revision 3.1  82/10/13  16:07:56  wft
1539Sjkh * fixed type of variables receiving from getc() (char -> int).
1549Sjkh */
1559Sjkh
1569Sjkh
1579Sjkh
1589Sjkh#include "rcsbase.h"
1599Sjkh
16011894Speterstruct rcslockers {                   /* lockers in locker option; stored   */
1619Sjkh     char const		* login;      /* lockerlist			    */
16211894Speter     struct rcslockers  * lockerlink;
1639Sjkh     }  ;
1649Sjkh
1659Sjkhstruct  stateattri {                  /* states in state option; stored in  */
1669Sjkh     char const		* status;     /* statelist			    */
1679Sjkh     struct  stateattri * nextstate;
1689Sjkh     }  ;
1699Sjkh
1709Sjkhstruct  authors {                     /* login names in author option;      */
1719Sjkh     char const		* login;      /* stored in authorlist		    */
1729Sjkh     struct     authors * nextauthor;
1739Sjkh     }  ;
1749Sjkh
1759Sjkhstruct Revpairs{                      /* revision or branch range in -r     */
17611894Speter     int		  numfld;     /* option; stored in revlist	    */
1779Sjkh     char const		* strtrev;
1789Sjkh     char const		* endrev;
1799Sjkh     struct  Revpairs   * rnext;
1809Sjkh     } ;
1819Sjkh
1829Sjkhstruct Datepairs{                     /* date range in -d option; stored in */
18311894Speter     struct Datepairs *dnext;
1849Sjkh     char               strtdate[datesize];   /* duelst and datelist      */
1859Sjkh     char               enddate[datesize];
18611894Speter     char ne_date; /* datelist only; distinguishes < from <= */
1879Sjkh     };
1889Sjkh
1899Sjkhstatic char extractdelta P((struct hshentry const*));
1909Sjkhstatic int checkrevpair P((char const*,char const*));
19111894Speterstatic int extdate P((struct hshentry*));
19211894Speterstatic int getnumericrev P((void));
1939Sjkhstatic struct hshentry const *readdeltalog P((void));
1949Sjkhstatic void cleanup P((void));
1959Sjkhstatic void exttree P((struct hshentry*));
1969Sjkhstatic void getauthor P((char*));
1979Sjkhstatic void getdatepair P((char*));
1989Sjkhstatic void getlocker P((char*));
1999Sjkhstatic void getrevpairs P((char*));
2009Sjkhstatic void getscript P((struct hshentry*));
2019Sjkhstatic void getstate P((char*));
2029Sjkhstatic void putabranch P((struct hshentry const*));
2039Sjkhstatic void putadelta P((struct hshentry const*,struct hshentry const*,int));
2049Sjkhstatic void putforest P((struct branchhead const*));
2059Sjkhstatic void putree P((struct hshentry const*));
2069Sjkhstatic void putrunk P((void));
2079Sjkhstatic void recentdate P((struct hshentry const*,struct Datepairs*));
2089Sjkhstatic void trunclocks P((void));
2099Sjkh
2109Sjkhstatic char const *insDelFormat;
2119Sjkhstatic int branchflag;	/*set on -b */
2129Sjkhstatic int exitstatus;
2139Sjkhstatic int lockflag;
2149Sjkhstatic struct Datepairs *datelist, *duelst;
2159Sjkhstatic struct Revpairs *revlist, *Revlst;
2169Sjkhstatic struct authors *authorlist;
21711894Speterstatic struct rcslockers *lockerlist;
2189Sjkhstatic struct stateattri *statelist;
2199Sjkh
2209Sjkh
22150472SpetermainProg(rlogId, "rlog", "$FreeBSD$")
2229Sjkh{
2239Sjkh	static char const cmdusage[] =
22411926Speter		"\nrlog usage: rlog -{bhLNRt} -v[string] -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ...";
2259Sjkh
2269Sjkh	register FILE *out;
2279Sjkh	char *a, **newargv;
2289Sjkh	struct Datepairs *currdate;
22911894Speter	char const *accessListString, *accessFormat;
2309Sjkh	char const *headFormat, *symbolFormat;
2319Sjkh	struct access const *curaccess;
2329Sjkh	struct assoc const *curassoc;
2339Sjkh	struct hshentry const *delta;
23411894Speter	struct rcslock const *currlock;
2359Sjkh	int descflag, selectflag;
2369Sjkh	int onlylockflag;  /* print only files with locks */
23711894Speter	int onlyRCSflag;  /* print only RCS pathname */
23811894Speter	int pre5;
23911894Speter	int shownames;
24011894Speter	int revno;
24111926Speter	int versionlist;
24211926Speter	char *vstring;
2439Sjkh
24411894Speter        descflag = selectflag = shownames = true;
24511926Speter	versionlist = onlylockflag = onlyRCSflag = false;
24611926Speter	vstring=0;
2479Sjkh	out = stdout;
2489Sjkh	suffixes = X_DEFAULT;
2499Sjkh
2509Sjkh	argc = getRCSINIT(argc, argv, &newargv);
2519Sjkh	argv = newargv;
2529Sjkh	while (a = *++argv,  0<--argc && *a++=='-') {
2539Sjkh		switch (*a++) {
2549Sjkh
2559Sjkh		case 'L':
2569Sjkh			onlylockflag = true;
2579Sjkh			break;
2589Sjkh
25911894Speter		case 'N':
26011894Speter			shownames = false;
26111894Speter			break;
26211894Speter
2639Sjkh		case 'R':
2649Sjkh			onlyRCSflag =true;
2659Sjkh			break;
2669Sjkh
2679Sjkh                case 'l':
2689Sjkh                        lockflag = true;
2699Sjkh			getlocker(a);
2709Sjkh                        break;
2719Sjkh
2729Sjkh                case 'b':
2739Sjkh                        branchflag = true;
2749Sjkh                        break;
2759Sjkh
2769Sjkh                case 'r':
2779Sjkh			getrevpairs(a);
2789Sjkh                        break;
2799Sjkh
2809Sjkh                case 'd':
2819Sjkh			getdatepair(a);
2829Sjkh                        break;
2839Sjkh
2849Sjkh                case 's':
2859Sjkh			getstate(a);
2869Sjkh                        break;
2879Sjkh
2889Sjkh                case 'w':
2899Sjkh			getauthor(a);
2909Sjkh                        break;
2919Sjkh
2929Sjkh                case 'h':
2939Sjkh			descflag = false;
2949Sjkh                        break;
2959Sjkh
2969Sjkh                case 't':
2979Sjkh                        selectflag = false;
2989Sjkh                        break;
2999Sjkh
3009Sjkh		case 'q':
3019Sjkh			/* This has no effect; it's here for consistency.  */
3029Sjkh			quietflag = true;
3039Sjkh			break;
3049Sjkh
3059Sjkh		case 'x':
3069Sjkh			suffixes = a;
3079Sjkh			break;
3089Sjkh
30911894Speter		case 'z':
31011894Speter			zone_set(a);
31111894Speter			break;
31211894Speter
31311894Speter		case 'T':
31411894Speter			/* Ignore -T, so that RCSINIT can contain -T.  */
31511894Speter			if (*a)
31611894Speter				goto unknown;
31711894Speter			break;
31811894Speter
3199Sjkh		case 'V':
3209Sjkh			setRCSversion(*argv);
3219Sjkh			break;
3229Sjkh
32311926Speter		case 'v':
32411926Speter			versionlist = true;
32511926Speter			vstring = a;
32611926Speter			break;
32711926Speter
3289Sjkh                default:
32911894Speter		unknown:
33011894Speter			error("unknown option: %s%s", *argv, cmdusage);
3319Sjkh
3329Sjkh                };
3339Sjkh        } /* end of option processing */
3349Sjkh
3359Sjkh	if (! (descflag|selectflag)) {
3369Sjkh		warn("-t overrides -h.");
3379Sjkh		descflag = true;
3389Sjkh	}
3399Sjkh
34011894Speter	pre5 = RCSversion < VERSION(5);
34111894Speter	if (pre5) {
3429Sjkh	    accessListString = "\naccess list:   ";
3439Sjkh	    accessFormat = "  %s";
34411894Speter	    headFormat = "RCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
34511894Speter	    insDelFormat = "  lines added/del: %ld/%ld";
3469Sjkh	    symbolFormat = "  %s: %s;";
3479Sjkh	} else {
3489Sjkh	    accessListString = "\naccess list:";
3499Sjkh	    accessFormat = "\n\t%s";
35011894Speter	    headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
35111894Speter	    insDelFormat = "  lines: +%ld -%ld";
3529Sjkh	    symbolFormat = "\n\t%s: %s";
3539Sjkh	}
3549Sjkh
35511894Speter	/* Now handle all pathnames.  */
35611894Speter	if (nerror)
35711894Speter	  cleanup();
35811894Speter	else if (argc < 1)
35911894Speter	  faterror("no input file%s", cmdusage);
36011894Speter	else
36111894Speter	  for (;  0 < argc;  cleanup(), ++argv, --argc) {
3629Sjkh	    ffree();
3639Sjkh
36411894Speter	    if (pairnames(argc, argv, rcsreadopen, true, false)  <=  0)
3659Sjkh		continue;
3669Sjkh
36711894Speter	    /*
36811894Speter	     * RCSname contains the name of the RCS file,
36911894Speter	     * and finptr the file descriptor;
37011894Speter	     * workname contains the name of the working file.
3719Sjkh             */
3729Sjkh
3739Sjkh	    /* Keep only those locks given by -l.  */
3749Sjkh	    if (lockflag)
3759Sjkh		trunclocks();
3769Sjkh
3779Sjkh            /* do nothing if -L is given and there are no locks*/
3789Sjkh	    if (onlylockflag && !Locks)
3799Sjkh		continue;
3809Sjkh
38111926Speter	    if ( versionlist ) {
38211926Speter		gettree();
38311926Speter		aprintf(out, "%s%s %s\n", vstring, workname, tiprev());
38411926Speter		continue;
38511926Speter	    }
38611926Speter
38711894Speter	    if ( onlyRCSflag ) {
38811894Speter		aprintf(out, "%s\n", RCSname);
3891485Sphk		continue;
3901485Sphk	    }
3911485Sphk
39211894Speter	    gettree();
39311894Speter
39411894Speter	    if (!getnumericrev())
3959Sjkh		continue;
39611894Speter
39711894Speter	    /*
39811894Speter	    * Output the first character with putc, not printf.
39911894Speter	    * Otherwise, an SVR4 stdio bug buffers output inefficiently.
40011894Speter	    */
40111894Speter	    aputc_('\n', out)
40211894Speter
40311894Speter	    /*   print RCS pathname, working pathname and optional
4049Sjkh                 administrative information                         */
4059Sjkh            /* could use getfullRCSname() here, but that is very slow */
40611894Speter	    aprintf(out, headFormat, RCSname, workname,
4079Sjkh		    Head ? " " : "",  Head ? Head->num : "",
4089Sjkh		    Dbranch ? " " : "",  Dbranch ? Dbranch : "",
4099Sjkh		    StrictLocks ? " strict" : ""
4109Sjkh	    );
4119Sjkh            currlock = Locks;
4129Sjkh            while( currlock ) {
4139Sjkh		aprintf(out, symbolFormat, currlock->login,
4149Sjkh                                currlock->delta->num);
4159Sjkh                currlock = currlock->nextlock;
4169Sjkh            }
41711894Speter            if (StrictLocks && pre5)
41811894Speter                aputs("  ;  strict" + (Locks?3:0), out);
4199Sjkh
4209Sjkh	    aputs(accessListString, out);      /*  print access list  */
4219Sjkh            curaccess = AccessList;
4229Sjkh            while(curaccess) {
4239Sjkh		aprintf(out, accessFormat, curaccess->login);
4249Sjkh                curaccess = curaccess->nextaccess;
4259Sjkh            }
4269Sjkh
42711894Speter	    if (shownames) {
42811894Speter		aputs("\nsymbolic names:", out);   /*  print symbolic names   */
42911894Speter		for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
43011894Speter		    aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
43111894Speter	    }
43211894Speter	    if (pre5) {
43311894Speter		aputs("\ncomment leader:  \"", out);
43411894Speter		awrite(Comment.string, Comment.size, out);
43511894Speter		afputc('\"', out);
43611894Speter	    }
43711894Speter	    if (!pre5  ||  Expand != KEYVAL_EXPAND)
43811894Speter		aprintf(out, "\nkeyword substitution: %s",
4399Sjkh			expand_names[Expand]
4409Sjkh		);
4419Sjkh
44211894Speter	    aprintf(out, "\ntotal revisions: %d", TotalDeltas);
4439Sjkh
4449Sjkh	    revno = 0;
4459Sjkh
4469Sjkh	    if (Head  &&  selectflag & descflag) {
4479Sjkh
4489Sjkh		exttree(Head);
4499Sjkh
4509Sjkh		/*  get most recently date of the dates pointed by duelst  */
4519Sjkh		currdate = duelst;
4529Sjkh		while( currdate) {
45311894Speter		    VOID strcpy(currdate->strtdate, "0.0.0.0.0.0");
4549Sjkh		    recentdate(Head, currdate);
4559Sjkh		    currdate = currdate->dnext;
4569Sjkh		}
4579Sjkh
4589Sjkh		revno = extdate(Head);
4599Sjkh
46011894Speter		aprintf(out, ";\tselected revisions: %d", revno);
4619Sjkh	    }
4629Sjkh
4639Sjkh	    afputc('\n',out);
4649Sjkh	    if (descflag) {
4659Sjkh		aputs("description:\n", out);
4669Sjkh		getdesc(true);
4679Sjkh	    }
4689Sjkh	    if (revno) {
4699Sjkh		while (! (delta = readdeltalog())->selector  ||  --revno)
47011894Speter		    continue;
4719Sjkh		if (delta->next && countnumflds(delta->num)==2)
4729Sjkh		    /* Read through delta->next to get its insertlns.  */
4739Sjkh		    while (readdeltalog() != delta->next)
47411894Speter			continue;
4759Sjkh		putrunk();
4769Sjkh		putree(Head);
4779Sjkh	    }
47811925Speter	    aputs("----------------------------\n", out);
4799Sjkh	    aputs("=============================================================================\n",out);
48011894Speter	  }
4819Sjkh	Ofclose(out);
4829Sjkh	exitmain(exitstatus);
4839Sjkh}
4849Sjkh
4859Sjkh	static void
4869Sjkhcleanup()
4879Sjkh{
4889Sjkh	if (nerror) exitstatus = EXIT_FAILURE;
4899Sjkh	Izclose(&finptr);
4909Sjkh}
4919Sjkh
49211894Speter#if RCS_lint
4939Sjkh#	define exiterr rlogExit
4949Sjkh#endif
49511894Speter	void
4969Sjkhexiterr()
4979Sjkh{
4989Sjkh	_exit(EXIT_FAILURE);
4999Sjkh}
5009Sjkh
5019Sjkh
5029Sjkh
5039Sjkh	static void
5049Sjkhputrunk()
5059Sjkh/*  function:  print revisions chosen, which are in trunk      */
5069Sjkh
5079Sjkh{
5089Sjkh	register struct hshentry const *ptr;
5099Sjkh
5109Sjkh	for (ptr = Head;  ptr;  ptr = ptr->next)
5119Sjkh		putadelta(ptr, ptr->next, true);
5129Sjkh}
5139Sjkh
5149Sjkh
5159Sjkh
5169Sjkh	static void
5179Sjkhputree(root)
5189Sjkh	struct hshentry const *root;
5199Sjkh/*   function: print delta tree (not including trunk) in reverse
5209Sjkh               order on each branch                                        */
5219Sjkh
5229Sjkh{
52311894Speter	if (!root) return;
5249Sjkh
5259Sjkh        putree(root->next);
5269Sjkh
5279Sjkh        putforest(root->branches);
5289Sjkh}
5299Sjkh
5309Sjkh
5319Sjkh
5329Sjkh
5339Sjkh	static void
5349Sjkhputforest(branchroot)
5359Sjkh	struct branchhead const *branchroot;
5369Sjkh/*   function:  print branches that has the same direct ancestor    */
5379Sjkh{
53811894Speter	if (!branchroot) return;
5399Sjkh
5409Sjkh        putforest(branchroot->nextbranch);
5419Sjkh
5429Sjkh        putabranch(branchroot->hsh);
5439Sjkh        putree(branchroot->hsh);
5449Sjkh}
5459Sjkh
5469Sjkh
5479Sjkh
5489Sjkh
5499Sjkh	static void
5509Sjkhputabranch(root)
5519Sjkh	struct hshentry const *root;
5529Sjkh/*   function  :  print one branch     */
5539Sjkh
5549Sjkh{
55511894Speter	if (!root) return;
5569Sjkh
5579Sjkh        putabranch(root->next);
5589Sjkh
5599Sjkh        putadelta(root, root, false);
5609Sjkh}
5619Sjkh
5629Sjkh
5639Sjkh
5649Sjkh
5659Sjkh
5669Sjkh	static void
5679Sjkhputadelta(node,editscript,trunk)
5689Sjkh	register struct hshentry const *node, *editscript;
5699Sjkh	int trunk;
5709Sjkh/*  function: Print delta node if node->selector is set.        */
5719Sjkh/*      editscript indicates where the editscript is stored     */
5729Sjkh/*      trunk indicated whether this node is in trunk           */
5739Sjkh{
5749Sjkh	static char emptych[] = EMPTYLOG;
5759Sjkh
5769Sjkh	register FILE *out;
5779Sjkh	char const *s;
5789Sjkh	size_t n;
5799Sjkh	struct branchhead const *newbranch;
5809Sjkh	struct buf branchnum;
58111894Speter	char datebuf[datesize + zonelenmax];
58211894Speter	int pre5 = RCSversion < VERSION(5);
5839Sjkh
5849Sjkh	if (!node->selector)
5859Sjkh            return;
5869Sjkh
5879Sjkh	out = stdout;
5889Sjkh	aprintf(out,
58911894Speter		"----------------------------\nrevision %s%s",
59011894Speter		node->num,  pre5 ? "        " : ""
5919Sjkh	);
5929Sjkh        if ( node->lockedby )
59311894Speter	    aprintf(out, pre5+"\tlocked by: %s;", node->lockedby);
5949Sjkh
5959Sjkh	aprintf(out, "\ndate: %s;  author: %s;  state: %s;",
5969Sjkh		date2str(node->date, datebuf),
5979Sjkh		node->author, node->state
5989Sjkh	);
5999Sjkh
6009Sjkh        if ( editscript )
6019Sjkh           if(trunk)
6029Sjkh	      aprintf(out, insDelFormat,
6039Sjkh                             editscript->deletelns, editscript->insertlns);
6049Sjkh           else
6059Sjkh	      aprintf(out, insDelFormat,
6069Sjkh                             editscript->insertlns, editscript->deletelns);
6079Sjkh
6089Sjkh        newbranch = node->branches;
6099Sjkh        if ( newbranch ) {
6109Sjkh	   bufautobegin(&branchnum);
6119Sjkh	   aputs("\nbranches:", out);
6129Sjkh           while( newbranch ) {
6139Sjkh		getbranchno(newbranch->hsh->num, &branchnum);
6149Sjkh		aprintf(out, "  %s;", branchnum.string);
6159Sjkh                newbranch = newbranch->nextbranch;
6169Sjkh           }
6179Sjkh	   bufautoend(&branchnum);
6189Sjkh        }
6199Sjkh
6209Sjkh	afputc('\n', out);
6219Sjkh	s = node->log.string;
6229Sjkh	if (!(n = node->log.size)) {
6239Sjkh		s = emptych;
6249Sjkh		n = sizeof(emptych)-1;
6259Sjkh	}
6269Sjkh	awrite(s, n, out);
6279Sjkh	if (s[n-1] != '\n')
6289Sjkh		afputc('\n', out);
6299Sjkh}
6309Sjkh
6319Sjkh
6329Sjkh	static struct hshentry const *
6339Sjkhreaddeltalog()
6349Sjkh/*  Function : get the log message and skip the text of a deltatext node.
6359Sjkh *	       Return the delta found.
6369Sjkh *             Assumes the current lexeme is not yet in nexttok; does not
6379Sjkh *             advance nexttok.
6389Sjkh */
6399Sjkh{
6409Sjkh        register struct  hshentry  * Delta;
6419Sjkh	struct buf logbuf;
6429Sjkh	struct cbuf cb;
6439Sjkh
6449Sjkh	if (eoflex())
6459Sjkh		fatserror("missing delta log");
6469Sjkh        nextlex();
6479Sjkh	if (!(Delta = getnum()))
6489Sjkh		fatserror("delta number corrupted");
6499Sjkh	getkeystring(Klog);
6509Sjkh	if (Delta->log.string)
6519Sjkh		fatserror("duplicate delta log");
6529Sjkh	bufautobegin(&logbuf);
6539Sjkh	cb = savestring(&logbuf);
6549Sjkh	Delta->log = bufremember(&logbuf, cb.size);
6559Sjkh
65611894Speter	ignorephrases(Ktext);
6579Sjkh	getkeystring(Ktext);
6589Sjkh        Delta->insertlns = Delta->deletelns = 0;
6599Sjkh        if ( Delta != Head)
6609Sjkh                getscript(Delta);
6619Sjkh        else
6629Sjkh                readstring();
6639Sjkh	return Delta;
6649Sjkh}
6659Sjkh
6669Sjkh
6679Sjkh	static void
6689Sjkhgetscript(Delta)
6699Sjkhstruct    hshentry   * Delta;
6709Sjkh/*   function:  read edit script of Delta and count how many lines added  */
6719Sjkh/*              and deleted in the script                                 */
6729Sjkh
6739Sjkh{
6749Sjkh        int ed;   /*  editor command  */
6759Sjkh	declarecache;
6769Sjkh	register RILE *fin;
6779Sjkh        register  int   c;
67811894Speter	register long i;
6799Sjkh	struct diffcmd dc;
6809Sjkh
6819Sjkh	fin = finptr;
6829Sjkh	setupcache(fin);
6839Sjkh	initdiffcmd(&dc);
6849Sjkh	while (0  <=  (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
6859Sjkh	    if (!ed)
6869Sjkh                 Delta->deletelns += dc.nlines;
6879Sjkh	    else {
6889Sjkh                 /*  skip scripted lines  */
6899Sjkh		 i = dc.nlines;
6909Sjkh		 Delta->insertlns += i;
6919Sjkh		 cache(fin);
6929Sjkh		 do {
6939Sjkh		     for (;;) {
69411894Speter			cacheget_(c)
6959Sjkh			switch (c) {
6969Sjkh			    default:
6979Sjkh				continue;
6989Sjkh			    case SDELIM:
69911894Speter				cacheget_(c)
7009Sjkh				if (c == SDELIM)
7019Sjkh				    continue;
7029Sjkh				if (--i)
70311894Speter					unexpected_EOF();
7049Sjkh				nextc = c;
7059Sjkh				uncache(fin);
7069Sjkh				return;
7079Sjkh			    case '\n':
7089Sjkh				break;
7099Sjkh			}
7109Sjkh			break;
7119Sjkh		     }
7129Sjkh		     ++rcsline;
7139Sjkh		 } while (--i);
7149Sjkh		 uncache(fin);
7159Sjkh            }
7169Sjkh}
7179Sjkh
7189Sjkh
7199Sjkh
7209Sjkh
7219Sjkh
7229Sjkh
7239Sjkh
7249Sjkh	static void
7259Sjkhexttree(root)
7269Sjkhstruct hshentry  *root;
7279Sjkh/*  function: select revisions , starting with root             */
7289Sjkh
7299Sjkh{
7309Sjkh	struct branchhead const *newbranch;
7319Sjkh
73211894Speter	if (!root) return;
7339Sjkh
7349Sjkh	root->selector = extractdelta(root);
73511894Speter	root->log.string = 0;
7369Sjkh        exttree(root->next);
7379Sjkh
7389Sjkh        newbranch = root->branches;
7399Sjkh        while( newbranch ) {
7409Sjkh            exttree(newbranch->hsh);
7419Sjkh            newbranch = newbranch->nextbranch;
7429Sjkh        }
7439Sjkh}
7449Sjkh
7459Sjkh
7469Sjkh
7479Sjkh
7489Sjkh	static void
7499Sjkhgetlocker(argv)
7509Sjkhchar    * argv;
7519Sjkh/*   function : get the login names of lockers from command line   */
7529Sjkh/*              and store in lockerlist.                           */
7539Sjkh
7549Sjkh{
7559Sjkh        register char c;
75611894Speter	struct rcslockers *newlocker;
7579Sjkh        argv--;
75811894Speter	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
75911894Speter	    continue;
7609Sjkh        if (  c == '\0') {
76111894Speter	    lockerlist = 0;
7629Sjkh            return;
7639Sjkh        }
7649Sjkh
7659Sjkh        while( c != '\0' ) {
76611894Speter	    newlocker = talloc(struct rcslockers);
7679Sjkh            newlocker->lockerlink = lockerlist;
7689Sjkh            newlocker->login = argv;
7699Sjkh            lockerlist = newlocker;
77011894Speter	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
77111894Speter		continue;
7729Sjkh            *argv = '\0';
7739Sjkh            if ( c == '\0' ) return;
77411894Speter	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
77511894Speter		continue;
7769Sjkh        }
7779Sjkh}
7789Sjkh
7799Sjkh
7809Sjkh
7819Sjkh	static void
7829Sjkhgetauthor(argv)
7839Sjkhchar   *argv;
7849Sjkh/*   function:  get the author's name from command line   */
7859Sjkh/*              and store in authorlist                   */
7869Sjkh
7879Sjkh{
7889Sjkh        register    c;
7899Sjkh        struct     authors  * newauthor;
7909Sjkh
7919Sjkh        argv--;
79211894Speter	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
79311894Speter	    continue;
7949Sjkh        if ( c == '\0' ) {
7959Sjkh	    authorlist = talloc(struct authors);
7969Sjkh	    authorlist->login = getusername(false);
79711894Speter	    authorlist->nextauthor = 0;
7989Sjkh            return;
7999Sjkh        }
8009Sjkh
8019Sjkh        while( c != '\0' ) {
8029Sjkh	    newauthor = talloc(struct authors);
8039Sjkh            newauthor->nextauthor = authorlist;
8049Sjkh            newauthor->login = argv;
8059Sjkh            authorlist = newauthor;
80611894Speter	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
80711894Speter		continue;
8089Sjkh            * argv = '\0';
8099Sjkh            if ( c == '\0') return;
81011894Speter	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
81111894Speter		continue;
8129Sjkh        }
8139Sjkh}
8149Sjkh
8159Sjkh
8169Sjkh
8179Sjkh
8189Sjkh	static void
8199Sjkhgetstate(argv)
8209Sjkhchar   * argv;
8219Sjkh/*   function :  get the states of revisions from command line  */
8229Sjkh/*               and store in statelist                         */
8239Sjkh
8249Sjkh{
8259Sjkh        register  char  c;
8269Sjkh        struct    stateattri    *newstate;
8279Sjkh
8289Sjkh        argv--;
82911894Speter	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
83011894Speter	    continue;
8319Sjkh        if ( c == '\0'){
83211894Speter	    error("missing state attributes after -s options");
8339Sjkh            return;
8349Sjkh        }
8359Sjkh
8369Sjkh        while( c != '\0' ) {
8379Sjkh	    newstate = talloc(struct stateattri);
8389Sjkh            newstate->nextstate = statelist;
8399Sjkh            newstate->status = argv;
8409Sjkh            statelist = newstate;
84111894Speter	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
84211894Speter		continue;
8439Sjkh            *argv = '\0';
8449Sjkh            if ( c == '\0' ) return;
84511894Speter	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
84611894Speter		continue;
8479Sjkh        }
8489Sjkh}
8499Sjkh
8509Sjkh
8519Sjkh
8529Sjkh	static void
8539Sjkhtrunclocks()
8549Sjkh/*  Function:  Truncate the list of locks to those that are held by the  */
8559Sjkh/*             id's on lockerlist. Do not truncate if lockerlist empty.  */
8569Sjkh
8579Sjkh{
85811894Speter	struct rcslockers const *plocker;
85911894Speter	struct rcslock *p, **pp;
8609Sjkh
86111894Speter	if (!lockerlist) return;
8629Sjkh
8639Sjkh        /* shorten Locks to those contained in lockerlist */
86411894Speter	for (pp = &Locks;  (p = *pp);  )
86511894Speter	    for (plocker = lockerlist;  ;  )
86611894Speter		if (strcmp(plocker->login, p->login) == 0) {
86711894Speter		    pp = &p->nextlock;
86811894Speter		    break;
86911894Speter		} else if (!(plocker = plocker->lockerlink)) {
87011894Speter		    *pp = p->nextlock;
87111894Speter		    break;
87211894Speter		}
8739Sjkh}
8749Sjkh
8759Sjkh
8769Sjkh
8779Sjkh	static void
8789Sjkhrecentdate(root, pd)
8799Sjkh	struct hshentry const *root;
8809Sjkh	struct Datepairs *pd;
8819Sjkh/*  function:  Finds the delta that is closest to the cutoff date given by   */
8829Sjkh/*             pd among the revisions selected by exttree.                   */
8839Sjkh/*             Successively narrows down the interval given by pd,           */
8849Sjkh/*             and sets the strtdate of pd to the date of the selected delta */
8859Sjkh{
8869Sjkh	struct branchhead const *newbranch;
8879Sjkh
88811894Speter	if (!root) return;
8899Sjkh	if (root->selector) {
89011894Speter	     if ( cmpdate(root->date, pd->strtdate) >= 0 &&
89111894Speter		  cmpdate(root->date, pd->enddate) <= 0)
8929Sjkh		VOID strcpy(pd->strtdate, root->date);
8939Sjkh        }
8949Sjkh
8959Sjkh        recentdate(root->next, pd);
8969Sjkh        newbranch = root->branches;
8979Sjkh        while( newbranch) {
8989Sjkh           recentdate(newbranch->hsh, pd);
8999Sjkh           newbranch = newbranch->nextbranch;
9009Sjkh	}
9019Sjkh}
9029Sjkh
9039Sjkh
9049Sjkh
9059Sjkh
9069Sjkh
9079Sjkh
90811894Speter	static int
9099Sjkhextdate(root)
9109Sjkhstruct  hshentry        * root;
9119Sjkh/*  function:  select revisions which are in the date range specified     */
9129Sjkh/*             in duelst  and datelist, start at root                     */
9139Sjkh/* Yield number of revisions selected, including those already selected.  */
9149Sjkh{
9159Sjkh	struct branchhead const *newbranch;
9169Sjkh	struct Datepairs const *pdate;
91711894Speter	int revno, ne;
9189Sjkh
9199Sjkh	if (!root)
9209Sjkh	    return 0;
9219Sjkh
9229Sjkh        if ( datelist || duelst) {
9239Sjkh            pdate = datelist;
9249Sjkh            while( pdate ) {
92511894Speter		ne = pdate->ne_date;
92611894Speter		if (
92711894Speter			(!pdate->strtdate[0]
92811894Speter			|| ne <= cmpdate(root->date, pdate->strtdate))
92911894Speter		    &&
93011894Speter			(!pdate->enddate[0]
93111894Speter			|| ne <= cmpdate(pdate->enddate, root->date))
93211894Speter		)
9339Sjkh                        break;
9349Sjkh                pdate = pdate->dnext;
9359Sjkh            }
93611894Speter	    if (!pdate) {
9379Sjkh                pdate = duelst;
9389Sjkh		for (;;) {
9399Sjkh		   if (!pdate) {
9409Sjkh			root->selector = false;
9419Sjkh			break;
9429Sjkh		   }
94311894Speter		   if (cmpdate(root->date, pdate->strtdate) == 0)
9449Sjkh                      break;
9459Sjkh                   pdate = pdate->dnext;
9469Sjkh                }
9479Sjkh            }
9489Sjkh        }
9499Sjkh	revno = root->selector + extdate(root->next);
9509Sjkh
9519Sjkh        newbranch = root->branches;
9529Sjkh        while( newbranch ) {
9539Sjkh	   revno += extdate(newbranch->hsh);
9549Sjkh           newbranch = newbranch->nextbranch;
9559Sjkh        }
9569Sjkh	return revno;
9579Sjkh}
9589Sjkh
9599Sjkh
9609Sjkh
9619Sjkh	static char
9629Sjkhextractdelta(pdelta)
9639Sjkh	struct hshentry const *pdelta;
9649Sjkh/*  function:  compare information of pdelta to the authorlist, lockerlist,*/
9659Sjkh/*             statelist, revlist and yield true if pdelta is selected.    */
9669Sjkh
9679Sjkh{
96811894Speter	struct rcslock const *plock;
9699Sjkh	struct stateattri const *pstate;
9709Sjkh	struct authors const *pauthor;
9719Sjkh	struct Revpairs const *prevision;
97211894Speter	int length;
9739Sjkh
9749Sjkh	if ((pauthor = authorlist)) /* only certain authors wanted */
9759Sjkh	    while (strcmp(pauthor->login, pdelta->author) != 0)
9769Sjkh		if (!(pauthor = pauthor->nextauthor))
9779Sjkh		    return false;
9789Sjkh	if ((pstate = statelist)) /* only certain states wanted */
9799Sjkh	    while (strcmp(pstate->status, pdelta->state) != 0)
9809Sjkh		if (!(pstate = pstate->nextstate))
9819Sjkh		    return false;
9829Sjkh	if (lockflag) /* only locked revisions wanted */
9839Sjkh	    for (plock = Locks;  ;  plock = plock->nextlock)
9849Sjkh		if (!plock)
9859Sjkh		    return false;
9869Sjkh		else if (plock->delta == pdelta)
9879Sjkh		    break;
9889Sjkh	if ((prevision = Revlst)) /* only certain revs or branches wanted */
9899Sjkh	    for (;;) {
9909Sjkh                length = prevision->numfld;
9919Sjkh		if (
9929Sjkh		    countnumflds(pdelta->num) == length+(length&1) &&
9939Sjkh		    0 <= compartial(pdelta->num, prevision->strtrev, length) &&
9949Sjkh		    0 <= compartial(prevision->endrev, pdelta->num, length)
9959Sjkh		)
9969Sjkh		     break;
9979Sjkh		if (!(prevision = prevision->rnext))
9989Sjkh		    return false;
9999Sjkh            }
10009Sjkh	return true;
10019Sjkh}
10029Sjkh
10039Sjkh
10049Sjkh
10059Sjkh	static void
10069Sjkhgetdatepair(argv)
10079Sjkh   char   * argv;
10089Sjkh/*  function:  get time range from command line and store in datelist if    */
10099Sjkh/*             a time range specified or in duelst if a time spot specified */
10109Sjkh
10119Sjkh{
10129Sjkh        register   char         c;
10139Sjkh        struct     Datepairs    * nextdate;
10149Sjkh	char const		* rawdate;
10159Sjkh	int                     switchflag;
10169Sjkh
10179Sjkh        argv--;
101811894Speter	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
101911894Speter	    continue;
10209Sjkh        if ( c == '\0' ) {
102111894Speter	    error("missing date/time after -d");
10229Sjkh            return;
10239Sjkh        }
10249Sjkh
10259Sjkh        while( c != '\0' )  {
10269Sjkh	    switchflag = false;
10279Sjkh	    nextdate = talloc(struct Datepairs);
10289Sjkh            if ( c == '<' ) {   /*   case: -d <date   */
10299Sjkh                c = *++argv;
103011894Speter		if (!(nextdate->ne_date = c!='='))
103111894Speter		    c = *++argv;
10329Sjkh                (nextdate->strtdate)[0] = '\0';
10339Sjkh	    } else if (c == '>') { /* case: -d'>date' */
10349Sjkh		c = *++argv;
103511894Speter		if (!(nextdate->ne_date = c!='='))
103611894Speter		    c = *++argv;
10379Sjkh		(nextdate->enddate)[0] = '\0';
10389Sjkh		switchflag = true;
10399Sjkh	    } else {
10409Sjkh                rawdate = argv;
10419Sjkh		while( c != '<' && c != '>' && c != ';' && c != '\0')
10429Sjkh		     c = *++argv;
10439Sjkh                *argv = '\0';
10449Sjkh		if ( c == '>' ) switchflag=true;
10459Sjkh		str2date(rawdate,
10469Sjkh			 switchflag ? nextdate->enddate : nextdate->strtdate);
10479Sjkh		if ( c == ';' || c == '\0') {  /*  case: -d date  */
10489Sjkh		    VOID strcpy(nextdate->enddate,nextdate->strtdate);
10499Sjkh                    nextdate->dnext = duelst;
10509Sjkh                    duelst = nextdate;
10519Sjkh		    goto end;
10529Sjkh		} else {
10539Sjkh		    /*   case:   -d date<  or -d  date>; see switchflag */
105411894Speter		    int eq = argv[1]=='=';
105511894Speter		    nextdate->ne_date = !eq;
105611894Speter		    argv += eq;
105711894Speter		    while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
105811894Speter			continue;
10599Sjkh		    if ( c == ';' || c == '\0') {
10609Sjkh			/* second date missing */
10619Sjkh			if (switchflag)
10629Sjkh			    *nextdate->strtdate= '\0';
10639Sjkh			else
10649Sjkh			    *nextdate->enddate= '\0';
10659Sjkh			nextdate->dnext = datelist;
10669Sjkh			datelist = nextdate;
10679Sjkh			goto end;
10689Sjkh		    }
10699Sjkh                }
10709Sjkh            }
10719Sjkh            rawdate = argv;
10729Sjkh	    while( c != '>' && c != '<' && c != ';' && c != '\0')
10739Sjkh 		c = *++argv;
10749Sjkh            *argv = '\0';
10759Sjkh	    str2date(rawdate,
10769Sjkh		     switchflag ? nextdate->strtdate : nextdate->enddate);
10779Sjkh            nextdate->dnext = datelist;
10789Sjkh	    datelist = nextdate;
10799Sjkh     end:
108011894Speter	    if (RCSversion < VERSION(5))
108111894Speter		nextdate->ne_date = 0;
10829Sjkh	    if ( c == '\0')  return;
108311894Speter	    while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
108411894Speter		continue;
10859Sjkh        }
10869Sjkh}
10879Sjkh
10889Sjkh
10899Sjkh
109011894Speter	static int
10919Sjkhgetnumericrev()
10929Sjkh/*  function:  get the numeric name of revisions which stored in revlist  */
10939Sjkh/*             and then stored the numeric names in Revlst                */
10949Sjkh/*             if branchflag, also add default branch                     */
10959Sjkh
10969Sjkh{
10979Sjkh        struct  Revpairs        * ptr, *pt;
109811894Speter	int n;
10999Sjkh	struct buf s, e;
11009Sjkh	char const *lrev;
11019Sjkh	struct buf const *rstart, *rend;
11029Sjkh
110311894Speter	Revlst = 0;
11049Sjkh        ptr = revlist;
11059Sjkh	bufautobegin(&s);
11069Sjkh	bufautobegin(&e);
11079Sjkh        while( ptr ) {
11089Sjkh	    n = 0;
11099Sjkh	    rstart = &s;
11109Sjkh	    rend = &e;
11119Sjkh
11129Sjkh	    switch (ptr->numfld) {
11139Sjkh
111411894Speter	      case 1: /* -rREV */
111511894Speter		if (!expandsym(ptr->strtrev, &s))
111611894Speter		    goto freebufs;
111711894Speter		rend = &s;
111811894Speter		n = countnumflds(s.string);
111911894Speter		if (!n  &&  (lrev = tiprev())) {
112011894Speter		    bufscpy(&s, lrev);
112111894Speter		    n = countnumflds(lrev);
112211894Speter		}
11239Sjkh		break;
11249Sjkh
112511894Speter	      case 2: /* -rREV: */
112611894Speter		if (!expandsym(ptr->strtrev, &s))
112711894Speter		    goto freebufs;
112811894Speter		bufscpy(&e, s.string);
112911894Speter		n = countnumflds(s.string);
113011894Speter		(n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
11319Sjkh		break;
11329Sjkh
113311894Speter	      case 3: /* -r:REV */
113411894Speter		if (!expandsym(ptr->endrev, &e))
113511894Speter		    goto freebufs;
113611894Speter		if ((n = countnumflds(e.string)) < 2)
113711894Speter		    bufscpy(&s, ".0");
113811894Speter		else {
113911894Speter		    bufscpy(&s, e.string);
114011894Speter		    VOID strcpy(strrchr(s.string,'.'), ".0");
114111894Speter		}
11429Sjkh		break;
11439Sjkh
114411894Speter	      default: /* -rREV1:REV2 */
114511894Speter		if (!(
11469Sjkh			expandsym(ptr->strtrev, &s)
11479Sjkh		    &&	expandsym(ptr->endrev, &e)
11489Sjkh		    &&	checkrevpair(s.string, e.string)
114911894Speter		))
115011894Speter		    goto freebufs;
115111894Speter		n = countnumflds(s.string);
115211894Speter		/* Swap if out of order.  */
115311894Speter		if (compartial(s.string,e.string,n) > 0) {
115411894Speter		    rstart = &e;
115511894Speter		    rend = &s;
11569Sjkh		}
11579Sjkh		break;
11589Sjkh	    }
11599Sjkh
11609Sjkh	    if (n) {
11619Sjkh		pt = ftalloc(struct Revpairs);
11629Sjkh		pt->numfld = n;
11639Sjkh		pt->strtrev = fstr_save(rstart->string);
11649Sjkh		pt->endrev = fstr_save(rend->string);
11659Sjkh                pt->rnext = Revlst;
11669Sjkh                Revlst = pt;
11679Sjkh	    }
11689Sjkh	    ptr = ptr->rnext;
11699Sjkh        }
11709Sjkh        /* Now take care of branchflag */
11719Sjkh	if (branchflag && (Dbranch||Head)) {
11729Sjkh	    pt = ftalloc(struct Revpairs);
11739Sjkh	    pt->strtrev = pt->endrev =
11749Sjkh		Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
11759Sjkh	    pt->rnext=Revlst; Revlst=pt;
11769Sjkh	    pt->numfld = countnumflds(pt->strtrev);
11779Sjkh        }
117811894Speter
117911894Speter      freebufs:
11809Sjkh	bufautoend(&s);
11819Sjkh	bufautoend(&e);
118211894Speter	return !ptr;
11839Sjkh}
11849Sjkh
11859Sjkh
11869Sjkh
11879Sjkh	static int
11889Sjkhcheckrevpair(num1,num2)
11899Sjkh	char const *num1, *num2;
11909Sjkh/*  function:  check whether num1, num2 are legal pair,i.e.
11919Sjkh    only the last field are different and have same number of
11929Sjkh    fields( if length <= 2, may be different if first field)   */
11939Sjkh
11949Sjkh{
119511894Speter	int length = countnumflds(num1);
11969Sjkh
11979Sjkh	if (
11989Sjkh			countnumflds(num2) != length
119911894Speter		||	(2 < length  &&  compartial(num1, num2, length-1) != 0)
12009Sjkh	) {
120111894Speter	    rcserror("invalid branch or revision pair %s : %s", num1, num2);
12029Sjkh            return false;
12039Sjkh        }
12049Sjkh
12059Sjkh        return true;
12069Sjkh}
12079Sjkh
12089Sjkh
12099Sjkh
12109Sjkh	static void
12119Sjkhgetrevpairs(argv)
12129Sjkhregister     char    * argv;
12139Sjkh/*  function:  get revision or branch range from command line, and   */
12149Sjkh/*             store in revlist                                      */
12159Sjkh
12169Sjkh{
12179Sjkh        register    char    c;
12189Sjkh        struct      Revpairs  * nextrevpair;
12199Sjkh	int separator;
12209Sjkh
12219Sjkh	c = *argv;
12229Sjkh
12239Sjkh	/* Support old ambiguous '-' syntax; this will go away.  */
12249Sjkh	if (strchr(argv,':'))
12259Sjkh	    separator = ':';
12269Sjkh	else {
12279Sjkh	    if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
12289Sjkh		warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
12299Sjkh	    separator = '-';
12309Sjkh	}
12319Sjkh
12329Sjkh	for (;;) {
12339Sjkh	    while (c==' ' || c=='\t' || c=='\n')
12349Sjkh		c = *++argv;
12359Sjkh	    nextrevpair = talloc(struct Revpairs);
12369Sjkh            nextrevpair->rnext = revlist;
12379Sjkh            revlist = nextrevpair;
12389Sjkh	    nextrevpair->numfld = 1;
12399Sjkh	    nextrevpair->strtrev = argv;
12409Sjkh	    for (;;  c = *++argv) {
12419Sjkh		switch (c) {
12429Sjkh		    default:
12439Sjkh			continue;
12449Sjkh		    case '\0': case ' ': case '\t': case '\n':
12459Sjkh		    case ',': case ';':
12469Sjkh			break;
12479Sjkh		    case ':': case '-':
12489Sjkh			if (c == separator)
12499Sjkh			    break;
12509Sjkh			continue;
12519Sjkh		}
12529Sjkh		break;
12539Sjkh	    }
12549Sjkh	    *argv = '\0';
12559Sjkh	    while (c==' ' || c=='\t' || c=='\n')
12569Sjkh		c = *++argv;
12579Sjkh	    if (c == separator) {
125811894Speter		while ((c = *++argv) == ' ' || c == '\t' || c =='\n')
125911894Speter		    continue;
12609Sjkh		nextrevpair->endrev = argv;
12619Sjkh		for (;;  c = *++argv) {
12629Sjkh		    switch (c) {
12639Sjkh			default:
12649Sjkh			    continue;
12659Sjkh			case '\0': case ' ': case '\t': case '\n':
12669Sjkh			case ',': case ';':
12679Sjkh			    break;
12689Sjkh			case ':': case '-':
12699Sjkh			    if (c == separator)
127011894Speter				break;
127111894Speter			    continue;
12729Sjkh		    }
12739Sjkh		    break;
12749Sjkh		}
12759Sjkh		*argv = '\0';
12769Sjkh		while (c==' ' || c=='\t' || c =='\n')
12779Sjkh		    c = *++argv;
12789Sjkh		nextrevpair->numfld =
127911894Speter		    !nextrevpair->endrev[0] ? 2 /* -rREV: */ :
128011894Speter		    !nextrevpair->strtrev[0] ? 3 /* -r:REV */ :
128111894Speter		    4 /* -rREV1:REV2 */;
12829Sjkh            }
12839Sjkh	    if (!c)
12849Sjkh		break;
128511894Speter	    else if (c==',' || c==';')
128611894Speter		c = *++argv;
128711894Speter	    else
12889Sjkh		error("missing `,' near `%c%s'", c, argv+1);
12899Sjkh	}
12909Sjkh}
1291