1 /* Print log messages and other information about RCS files.  */
2 
3 /* Copyright 1982, 1988, 1989 Walter Tichy
4    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Distributed under license by the Free Software Foundation, Inc.
6 
7 This file is part of RCS.
8 
9 RCS is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RCS; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 Report problems and direct all questions to:
25 
26     rcs-bugs@cs.purdue.edu
27 
28 */
29 
30 /*
31  * $Log: rlog.c,v $
32  * Revision 5.18  1995/06/16 06:19:24  eggert
33  * Update FSF address.
34  *
35  * Revision 5.17  1995/06/01 16:23:43  eggert
36  * (struct rcslockers): Renamed from `struct lockers'.
37  * (getnumericrev): Return error indication instead of ignoring errors.
38  * (main): Check it.  Don't use dateform.
39  * (recentdate, extdate): cmpnum -> cmpdate
40  *
41  * Revision 5.16  1994/04/13 16:30:34  eggert
42  * Fix bug; `rlog -lxxx' inverted the sense of -l.
43  *
44  * Revision 5.15  1994/03/17 14:05:48  eggert
45  * -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it.
46  * Emulate -V4's white space generation more precisely.
47  * Work around SVR4 stdio performance bug.  Remove lint.
48  *
49  * Revision 5.14  1993/11/09 17:40:15  eggert
50  * -V now prints version on stdout and exits.
51  *
52  * Revision 5.13  1993/11/03 17:42:27  eggert
53  * Add -N, -z.  Ignore -T.
54  *
55  * Revision 5.12  1992/07/28  16:12:44  eggert
56  * Don't miss B.0 when handling branch B.  Diagnose missing `,' in -r.
57  * Add -V.  Avoid `unsigned'.  Statement macro names now end in _.
58  *
59  * Revision 5.11  1992/01/24  18:44:19  eggert
60  * Don't duplicate unexpected_EOF's function.  lint -> RCS_lint
61  *
62  * Revision 5.10  1992/01/06  02:42:34  eggert
63  * Update usage string.
64  * while (E) ; -> while (E) continue;
65  *
66  * Revision 5.9  1991/09/17  19:07:40  eggert
67  * Getscript() didn't uncache partial lines.
68  *
69  * Revision 5.8  1991/08/19  03:13:55  eggert
70  * Revision separator is `:', not `-'.
71  * Check for missing and duplicate logs.  Tune.
72  * Permit log messages that do not end in newline (including empty logs).
73  *
74  * Revision 5.7  1991/04/21  11:58:31  eggert
75  * Add -x, RCSINIT, MS-DOS support.
76  *
77  * Revision 5.6  1991/02/26  17:07:17  eggert
78  * Survive RCS files with missing logs.
79  * strsave -> str_save (DG/UX name clash)
80  *
81  * Revision 5.5  1990/11/01  05:03:55  eggert
82  * Permit arbitrary data in logs and comment leaders.
83  *
84  * Revision 5.4  1990/10/04  06:30:22  eggert
85  * Accumulate exit status across files.
86  *
87  * Revision 5.3  1990/09/11  02:41:16  eggert
88  * Plug memory leak.
89  *
90  * Revision 5.2  1990/09/04  08:02:33  eggert
91  * Count RCS lines better.
92  *
93  * Revision 5.0  1990/08/22  08:13:48  eggert
94  * Remove compile-time limits; use malloc instead.  Add setuid support.
95  * Switch to GMT.
96  * Report dates in long form, to warn about dates past 1999/12/31.
97  * Change "added/del" message to make room for the longer dates.
98  * Don't generate trailing white space.  Add -V.  Ansify and Posixate.
99  *
100  * Revision 4.7  89/05/01  15:13:48  narten
101  * changed copyright header to reflect current distribution rules
102  *
103  * Revision 4.6  88/08/09  19:13:28  eggert
104  * Check for memory exhaustion; don't access freed storage.
105  * Shrink stdio code size; remove lint.
106  *
107  * Revision 4.5  87/12/18  11:46:38  narten
108  * more lint cleanups (Guy Harris)
109  *
110  * Revision 4.4  87/10/18  10:41:12  narten
111  * Updating version numbers
112  * Changes relative to 1.1 actually relative to 4.2
113  *
114  * Revision 1.3  87/09/24  14:01:10  narten
115  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
116  * warnings)
117  *
118  * Revision 1.2  87/03/27  14:22:45  jenkins
119  * Port to suns
120  *
121  * Revision 4.2  83/12/05  09:18:09  wft
122  * changed rewriteflag to external.
123  *
124  * Revision 4.1  83/05/11  16:16:55  wft
125  * Added -b, updated getnumericrev() accordingly.
126  * Replaced getpwuid() with getcaller().
127  *
128  * Revision 3.7  83/05/11  14:24:13  wft
129  * Added options -L and -R;
130  * Fixed selection bug with -l on multiple files.
131  * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
132  *
133  * Revision 3.6  82/12/24  15:57:53  wft
134  * shortened output format.
135  *
136  * Revision 3.5  82/12/08  21:45:26  wft
137  * removed call to checkaccesslist(); used DATEFORM to format all dates;
138  * removed unused variables.
139  *
140  * Revision 3.4  82/12/04  13:26:25  wft
141  * Replaced getdelta() with gettree(); removed updating of field lockedby.
142  *
143  * Revision 3.3  82/12/03  14:08:20  wft
144  * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
145  * Fixed printing of nil, removed printing of Suffix,
146  * added shortcut if no revisions are printed, disambiguated struct members.
147  *
148  * Revision 3.2  82/10/18  21:09:06  wft
149  * call to curdir replaced with getfullRCSname(),
150  * fixed call to getlogin(), cosmetic changes on output,
151  * changed conflicting long identifiers.
152  *
153  * Revision 3.1  82/10/13  16:07:56  wft
154  * fixed type of variables receiving from getc() (char -> int).
155  */
156 
157 
158 
159 #include "rcsbase.h"
160 
161 struct rcslockers {                   /* lockers in locker option; stored   */
162      char const		* login;      /* lockerlist			    */
163      struct rcslockers  * lockerlink;
164      }  ;
165 
166 struct  stateattri {                  /* states in state option; stored in  */
167      char const		* status;     /* statelist			    */
168      struct  stateattri * nextstate;
169      }  ;
170 
171 struct  authors {                     /* login names in author option;      */
172      char const		* login;      /* stored in authorlist		    */
173      struct     authors * nextauthor;
174      }  ;
175 
176 struct Revpairs{                      /* revision or branch range in -r     */
177      int		  numfld;     /* option; stored in revlist	    */
178      char const		* strtrev;
179      char const		* endrev;
180      struct  Revpairs   * rnext;
181      } ;
182 
183 struct Datepairs{                     /* date range in -d option; stored in */
184      struct Datepairs *dnext;
185      char               strtdate[datesize];   /* duelst and datelist      */
186      char               enddate[datesize];
187      char ne_date; /* datelist only; distinguishes < from <= */
188      };
189 
190 static char extractdelta P((struct hshentry const*));
191 static int checkrevpair P((char const*,char const*));
192 static int extdate P((struct hshentry*));
193 static int getnumericrev P((void));
194 static struct hshentry const *readdeltalog P((void));
195 static void cleanup P((void));
196 static void exttree P((struct hshentry*));
197 static void getauthor P((char*));
198 static void getdatepair P((char*));
199 static void getlocker P((char*));
200 static void getrevpairs P((char*));
201 static void getscript P((struct hshentry*));
202 static void getstate P((char*));
203 static void putabranch P((struct hshentry const*));
204 static void putadelta P((struct hshentry const*,struct hshentry const*,int));
205 static void putforest P((struct branchhead const*));
206 static void putree P((struct hshentry const*));
207 static void putrunk P((void));
208 static void recentdate P((struct hshentry const*,struct Datepairs*));
209 static void trunclocks P((void));
210 
211 static char const *insDelFormat;
212 static int branchflag;	/*set on -b */
213 static int exitstatus;
214 static int lockflag;
215 static struct Datepairs *datelist, *duelst;
216 static struct Revpairs *revlist, *Revlst;
217 static struct authors *authorlist;
218 static struct rcslockers *lockerlist;
219 static struct stateattri *statelist;
220 
221 
222 mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.18 1995/06/16 06:19:24 eggert Exp $")
223 {
224 	static char const cmdusage[] =
225 		"\nrlog usage: rlog -{bhLNRt} -v[string] -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ...";
226 
227 	register FILE *out;
228 	char *a, **newargv;
229 	struct Datepairs *currdate;
230 	char const *accessListString, *accessFormat;
231 	char const *headFormat, *symbolFormat;
232 	struct access const *curaccess;
233 	struct assoc const *curassoc;
234 	struct hshentry const *delta;
235 	struct rcslock const *currlock;
236 	int descflag, selectflag;
237 	int onlylockflag;  /* print only files with locks */
238 	int onlyRCSflag;  /* print only RCS pathname */
239 	int pre5;
240 	int shownames;
241 	int revno;
242 	int versionlist;
243 	char *vstring;
244 
245         descflag = selectflag = shownames = true;
246 	versionlist = onlylockflag = onlyRCSflag = false;
247 	vstring=0;
248 	out = stdout;
249 	suffixes = X_DEFAULT;
250 
251 	argc = getRCSINIT(argc, argv, &newargv);
252 	argv = newargv;
253 	while (a = *++argv,  0<--argc && *a++=='-') {
254 		switch (*a++) {
255 
256 		case 'L':
257 			onlylockflag = true;
258 			break;
259 
260 		case 'N':
261 			shownames = false;
262 			break;
263 
264 		case 'R':
265 			onlyRCSflag =true;
266 			break;
267 
268                 case 'l':
269                         lockflag = true;
270 			getlocker(a);
271                         break;
272 
273                 case 'b':
274                         branchflag = true;
275                         break;
276 
277                 case 'r':
278 			getrevpairs(a);
279                         break;
280 
281                 case 'd':
282 			getdatepair(a);
283                         break;
284 
285                 case 's':
286 			getstate(a);
287                         break;
288 
289                 case 'w':
290 			getauthor(a);
291                         break;
292 
293                 case 'h':
294 			descflag = false;
295                         break;
296 
297                 case 't':
298                         selectflag = false;
299                         break;
300 
301 		case 'q':
302 			/* This has no effect; it's here for consistency.  */
303 			quietflag = true;
304 			break;
305 
306 		case 'x':
307 			suffixes = a;
308 			break;
309 
310 		case 'z':
311 			zone_set(a);
312 			break;
313 
314 		case 'T':
315 			/* Ignore -T, so that RCSINIT can contain -T.  */
316 			if (*a)
317 				goto unknown;
318 			break;
319 
320 		case 'V':
321 			setRCSversion(*argv);
322 			break;
323 
324 		case 'v':
325 			versionlist = true;
326 			vstring = a;
327 			break;
328 
329                 default:
330 		unknown:
331 			error("unknown option: %s%s", *argv, cmdusage);
332 
333                 };
334         } /* end of option processing */
335 
336 	if (! (descflag|selectflag)) {
337 		warn("-t overrides -h.");
338 		descflag = true;
339 	}
340 
341 	pre5 = RCSversion < VERSION(5);
342 	if (pre5) {
343 	    accessListString = "\naccess list:   ";
344 	    accessFormat = "  %s";
345 	    headFormat = "RCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
346 	    insDelFormat = "  lines added/del: %ld/%ld";
347 	    symbolFormat = "  %s: %s;";
348 	} else {
349 	    accessListString = "\naccess list:";
350 	    accessFormat = "\n\t%s";
351 	    headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
352 	    insDelFormat = "  lines: +%ld -%ld";
353 	    symbolFormat = "\n\t%s: %s";
354 	}
355 
356 	/* Now handle all pathnames.  */
357 	if (nerror)
358 	  cleanup();
359 	else if (argc < 1)
360 	  faterror("no input file%s", cmdusage);
361 	else
362 	  for (;  0 < argc;  cleanup(), ++argv, --argc) {
363 	    ffree();
364 
365 	    if (pairnames(argc, argv, rcsreadopen, true, false)  <=  0)
366 		continue;
367 
368 	    /*
369 	     * RCSname contains the name of the RCS file,
370 	     * and finptr the file descriptor;
371 	     * workname contains the name of the working file.
372              */
373 
374 	    /* Keep only those locks given by -l.  */
375 	    if (lockflag)
376 		trunclocks();
377 
378             /* do nothing if -L is given and there are no locks*/
379 	    if (onlylockflag && !Locks)
380 		continue;
381 
382 	    if ( versionlist ) {
383 		gettree();
384 		aprintf(out, "%s%s %s\n", vstring, workname, tiprev());
385 		continue;
386 	    }
387 
388 	    if ( onlyRCSflag ) {
389 		aprintf(out, "%s\n", RCSname);
390 		continue;
391 	    }
392 
393 	    gettree();
394 
395 	    if (!getnumericrev())
396 		continue;
397 
398 	    /*
399 	    * Output the first character with putc, not printf.
400 	    * Otherwise, an SVR4 stdio bug buffers output inefficiently.
401 	    */
402 	    aputc_('\n', out)
403 
404 	    /*   print RCS pathname, working pathname and optional
405                  administrative information                         */
406             /* could use getfullRCSname() here, but that is very slow */
407 	    aprintf(out, headFormat, RCSname, workname,
408 		    Head ? " " : "",  Head ? Head->num : "",
409 		    Dbranch ? " " : "",  Dbranch ? Dbranch : "",
410 		    StrictLocks ? " strict" : ""
411 	    );
412             currlock = Locks;
413             while( currlock ) {
414 		aprintf(out, symbolFormat, currlock->login,
415                                 currlock->delta->num);
416                 currlock = currlock->nextlock;
417             }
418             if (StrictLocks && pre5)
419                 aputs("  ;  strict" + (Locks?3:0), out);
420 
421 	    aputs(accessListString, out);      /*  print access list  */
422             curaccess = AccessList;
423             while(curaccess) {
424 		aprintf(out, accessFormat, curaccess->login);
425                 curaccess = curaccess->nextaccess;
426             }
427 
428 	    if (shownames) {
429 		aputs("\nsymbolic names:", out);   /*  print symbolic names   */
430 		for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
431 		    aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
432 	    }
433 	    if (pre5) {
434 		aputs("\ncomment leader:  \"", out);
435 		awrite(Comment.string, Comment.size, out);
436 		afputc('\"', out);
437 	    }
438 	    if (!pre5  ||  Expand != KEYVAL_EXPAND)
439 		aprintf(out, "\nkeyword substitution: %s",
440 			expand_names[Expand]
441 		);
442 
443 	    aprintf(out, "\ntotal revisions: %d", TotalDeltas);
444 
445 	    revno = 0;
446 
447 	    if (Head  &&  selectflag & descflag) {
448 
449 		exttree(Head);
450 
451 		/*  get most recently date of the dates pointed by duelst  */
452 		currdate = duelst;
453 		while( currdate) {
454 		    VOID strcpy(currdate->strtdate, "0.0.0.0.0.0");
455 		    recentdate(Head, currdate);
456 		    currdate = currdate->dnext;
457 		}
458 
459 		revno = extdate(Head);
460 
461 		aprintf(out, ";\tselected revisions: %d", revno);
462 	    }
463 
464 	    afputc('\n',out);
465 	    if (descflag) {
466 		aputs("description:\n", out);
467 		getdesc(true);
468 	    }
469 	    if (revno) {
470 		while (! (delta = readdeltalog())->selector  ||  --revno)
471 		    continue;
472 		if (delta->next && countnumflds(delta->num)==2)
473 		    /* Read through delta->next to get its insertlns.  */
474 		    while (readdeltalog() != delta->next)
475 			continue;
476 		putrunk();
477 		putree(Head);
478 	    }
479 	    aputs("----------------------------\n", out);
480 	    aputs("=============================================================================\n",out);
481 	  }
482 	Ofclose(out);
483 	exitmain(exitstatus);
484 }
485 
486 	static void
cleanup()487 cleanup()
488 {
489 	if (nerror) exitstatus = EXIT_FAILURE;
490 	Izclose(&finptr);
491 }
492 
493 #if RCS_lint
494 #	define exiterr rlogExit
495 #endif
496 	void
exiterr()497 exiterr()
498 {
499 	_exit(EXIT_FAILURE);
500 }
501 
502 
503 
504 	static void
putrunk()505 putrunk()
506 /*  function:  print revisions chosen, which are in trunk      */
507 
508 {
509 	register struct hshentry const *ptr;
510 
511 	for (ptr = Head;  ptr;  ptr = ptr->next)
512 		putadelta(ptr, ptr->next, true);
513 }
514 
515 
516 
517 	static void
putree(root)518 putree(root)
519 	struct hshentry const *root;
520 /*   function: print delta tree (not including trunk) in reverse
521                order on each branch                                        */
522 
523 {
524 	if (!root) return;
525 
526         putree(root->next);
527 
528         putforest(root->branches);
529 }
530 
531 
532 
533 
534 	static void
putforest(branchroot)535 putforest(branchroot)
536 	struct branchhead const *branchroot;
537 /*   function:  print branches that has the same direct ancestor    */
538 {
539 	if (!branchroot) return;
540 
541         putforest(branchroot->nextbranch);
542 
543         putabranch(branchroot->hsh);
544         putree(branchroot->hsh);
545 }
546 
547 
548 
549 
550 	static void
putabranch(root)551 putabranch(root)
552 	struct hshentry const *root;
553 /*   function  :  print one branch     */
554 
555 {
556 	if (!root) return;
557 
558         putabranch(root->next);
559 
560         putadelta(root, root, false);
561 }
562 
563 
564 
565 
566 
567 	static void
putadelta(node,editscript,trunk)568 putadelta(node,editscript,trunk)
569 	register struct hshentry const *node, *editscript;
570 	int trunk;
571 /*  function: Print delta node if node->selector is set.        */
572 /*      editscript indicates where the editscript is stored     */
573 /*      trunk indicated whether this node is in trunk           */
574 {
575 	static char emptych[] = EMPTYLOG;
576 
577 	register FILE *out;
578 	char const *s;
579 	size_t n;
580 	struct branchhead const *newbranch;
581 	struct buf branchnum;
582 	char datebuf[datesize + zonelenmax];
583 	int pre5 = RCSversion < VERSION(5);
584 
585 	if (!node->selector)
586             return;
587 
588 	out = stdout;
589 	aprintf(out,
590 		"----------------------------\nrevision %s%s",
591 		node->num,  pre5 ? "        " : ""
592 	);
593         if ( node->lockedby )
594 	    aprintf(out, pre5+"\tlocked by: %s;", node->lockedby);
595 
596 	aprintf(out, "\ndate: %s;  author: %s;  state: %s;",
597 		date2str(node->date, datebuf),
598 		node->author, node->state
599 	);
600 
601         if ( editscript )
602            if(trunk)
603 	      aprintf(out, insDelFormat,
604                              editscript->deletelns, editscript->insertlns);
605            else
606 	      aprintf(out, insDelFormat,
607                              editscript->insertlns, editscript->deletelns);
608 
609         newbranch = node->branches;
610         if ( newbranch ) {
611 	   bufautobegin(&branchnum);
612 	   aputs("\nbranches:", out);
613            while( newbranch ) {
614 		getbranchno(newbranch->hsh->num, &branchnum);
615 		aprintf(out, "  %s;", branchnum.string);
616                 newbranch = newbranch->nextbranch;
617            }
618 	   bufautoend(&branchnum);
619         }
620 
621 	afputc('\n', out);
622 	s = node->log.string;
623 	if (!(n = node->log.size)) {
624 		s = emptych;
625 		n = sizeof(emptych)-1;
626 	}
627 	awrite(s, n, out);
628 	if (s[n-1] != '\n')
629 		afputc('\n', out);
630 }
631 
632 
633 	static struct hshentry const *
readdeltalog()634 readdeltalog()
635 /*  Function : get the log message and skip the text of a deltatext node.
636  *	       Return the delta found.
637  *             Assumes the current lexeme is not yet in nexttok; does not
638  *             advance nexttok.
639  */
640 {
641         register struct  hshentry  * Delta;
642 	struct buf logbuf;
643 	struct cbuf cb;
644 
645 	if (eoflex())
646 		fatserror("missing delta log");
647         nextlex();
648 	if (!(Delta = getnum()))
649 		fatserror("delta number corrupted");
650 	getkeystring(Klog);
651 	if (Delta->log.string)
652 		fatserror("duplicate delta log");
653 	bufautobegin(&logbuf);
654 	cb = savestring(&logbuf);
655 	Delta->log = bufremember(&logbuf, cb.size);
656 
657 	ignorephrases(Ktext);
658 	getkeystring(Ktext);
659         Delta->insertlns = Delta->deletelns = 0;
660         if ( Delta != Head)
661                 getscript(Delta);
662         else
663                 readstring();
664 	return Delta;
665 }
666 
667 
668 	static void
getscript(Delta)669 getscript(Delta)
670 struct    hshentry   * Delta;
671 /*   function:  read edit script of Delta and count how many lines added  */
672 /*              and deleted in the script                                 */
673 
674 {
675         int ed;   /*  editor command  */
676 	declarecache;
677 	register RILE *fin;
678         register  int   c;
679 	register long i;
680 	struct diffcmd dc;
681 
682 	fin = finptr;
683 	setupcache(fin);
684 	initdiffcmd(&dc);
685 	while (0  <=  (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
686 	    if (!ed)
687                  Delta->deletelns += dc.nlines;
688 	    else {
689                  /*  skip scripted lines  */
690 		 i = dc.nlines;
691 		 Delta->insertlns += i;
692 		 cache(fin);
693 		 do {
694 		     for (;;) {
695 			cacheget_(c)
696 			switch (c) {
697 			    default:
698 				continue;
699 			    case SDELIM:
700 				cacheget_(c)
701 				if (c == SDELIM)
702 				    continue;
703 				if (--i)
704 					unexpected_EOF();
705 				nextc = c;
706 				uncache(fin);
707 				return;
708 			    case '\n':
709 				break;
710 			}
711 			break;
712 		     }
713 		     ++rcsline;
714 		 } while (--i);
715 		 uncache(fin);
716             }
717 }
718 
719 
720 
721 
722 
723 
724 
725 	static void
exttree(root)726 exttree(root)
727 struct hshentry  *root;
728 /*  function: select revisions , starting with root             */
729 
730 {
731 	struct branchhead const *newbranch;
732 
733 	if (!root) return;
734 
735 	root->selector = extractdelta(root);
736 	root->log.string = 0;
737         exttree(root->next);
738 
739         newbranch = root->branches;
740         while( newbranch ) {
741             exttree(newbranch->hsh);
742             newbranch = newbranch->nextbranch;
743         }
744 }
745 
746 
747 
748 
749 	static void
getlocker(argv)750 getlocker(argv)
751 char    * argv;
752 /*   function : get the login names of lockers from command line   */
753 /*              and store in lockerlist.                           */
754 
755 {
756         register char c;
757 	struct rcslockers *newlocker;
758         argv--;
759 	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
760 	    continue;
761         if (  c == '\0') {
762 	    lockerlist = 0;
763             return;
764         }
765 
766         while( c != '\0' ) {
767 	    newlocker = talloc(struct rcslockers);
768             newlocker->lockerlink = lockerlist;
769             newlocker->login = argv;
770             lockerlist = newlocker;
771 	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
772 		continue;
773             *argv = '\0';
774             if ( c == '\0' ) return;
775 	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
776 		continue;
777         }
778 }
779 
780 
781 
782 	static void
getauthor(argv)783 getauthor(argv)
784 char   *argv;
785 /*   function:  get the author's name from command line   */
786 /*              and store in authorlist                   */
787 
788 {
789         register    c;
790         struct     authors  * newauthor;
791 
792         argv--;
793 	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
794 	    continue;
795         if ( c == '\0' ) {
796 	    authorlist = talloc(struct authors);
797 	    authorlist->login = getusername(false);
798 	    authorlist->nextauthor = 0;
799             return;
800         }
801 
802         while( c != '\0' ) {
803 	    newauthor = talloc(struct authors);
804             newauthor->nextauthor = authorlist;
805             newauthor->login = argv;
806             authorlist = newauthor;
807 	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
808 		continue;
809             * argv = '\0';
810             if ( c == '\0') return;
811 	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
812 		continue;
813         }
814 }
815 
816 
817 
818 
819 	static void
getstate(argv)820 getstate(argv)
821 char   * argv;
822 /*   function :  get the states of revisions from command line  */
823 /*               and store in statelist                         */
824 
825 {
826         register  char  c;
827         struct    stateattri    *newstate;
828 
829         argv--;
830 	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
831 	    continue;
832         if ( c == '\0'){
833 	    error("missing state attributes after -s options");
834             return;
835         }
836 
837         while( c != '\0' ) {
838 	    newstate = talloc(struct stateattri);
839             newstate->nextstate = statelist;
840             newstate->status = argv;
841             statelist = newstate;
842 	    while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
843 		continue;
844             *argv = '\0';
845             if ( c == '\0' ) return;
846 	    while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
847 		continue;
848         }
849 }
850 
851 
852 
853 	static void
trunclocks()854 trunclocks()
855 /*  Function:  Truncate the list of locks to those that are held by the  */
856 /*             id's on lockerlist. Do not truncate if lockerlist empty.  */
857 
858 {
859 	struct rcslockers const *plocker;
860 	struct rcslock *p, **pp;
861 
862 	if (!lockerlist) return;
863 
864         /* shorten Locks to those contained in lockerlist */
865 	for (pp = &Locks;  (p = *pp);  )
866 	    for (plocker = lockerlist;  ;  )
867 		if (strcmp(plocker->login, p->login) == 0) {
868 		    pp = &p->nextlock;
869 		    break;
870 		} else if (!(plocker = plocker->lockerlink)) {
871 		    *pp = p->nextlock;
872 		    break;
873 		}
874 }
875 
876 
877 
878 	static void
recentdate(root,pd)879 recentdate(root, pd)
880 	struct hshentry const *root;
881 	struct Datepairs *pd;
882 /*  function:  Finds the delta that is closest to the cutoff date given by   */
883 /*             pd among the revisions selected by exttree.                   */
884 /*             Successively narrows down the interval given by pd,           */
885 /*             and sets the strtdate of pd to the date of the selected delta */
886 {
887 	struct branchhead const *newbranch;
888 
889 	if (!root) return;
890 	if (root->selector) {
891 	     if ( cmpdate(root->date, pd->strtdate) >= 0 &&
892 		  cmpdate(root->date, pd->enddate) <= 0)
893 		VOID strcpy(pd->strtdate, root->date);
894         }
895 
896         recentdate(root->next, pd);
897         newbranch = root->branches;
898         while( newbranch) {
899            recentdate(newbranch->hsh, pd);
900            newbranch = newbranch->nextbranch;
901 	}
902 }
903 
904 
905 
906 
907 
908 
909 	static int
extdate(root)910 extdate(root)
911 struct  hshentry        * root;
912 /*  function:  select revisions which are in the date range specified     */
913 /*             in duelst  and datelist, start at root                     */
914 /* Yield number of revisions selected, including those already selected.  */
915 {
916 	struct branchhead const *newbranch;
917 	struct Datepairs const *pdate;
918 	int revno, ne;
919 
920 	if (!root)
921 	    return 0;
922 
923         if ( datelist || duelst) {
924             pdate = datelist;
925             while( pdate ) {
926 		ne = pdate->ne_date;
927 		if (
928 			(!pdate->strtdate[0]
929 			|| ne <= cmpdate(root->date, pdate->strtdate))
930 		    &&
931 			(!pdate->enddate[0]
932 			|| ne <= cmpdate(pdate->enddate, root->date))
933 		)
934                         break;
935                 pdate = pdate->dnext;
936             }
937 	    if (!pdate) {
938                 pdate = duelst;
939 		for (;;) {
940 		   if (!pdate) {
941 			root->selector = false;
942 			break;
943 		   }
944 		   if (cmpdate(root->date, pdate->strtdate) == 0)
945                       break;
946                    pdate = pdate->dnext;
947                 }
948             }
949         }
950 	revno = root->selector + extdate(root->next);
951 
952         newbranch = root->branches;
953         while( newbranch ) {
954 	   revno += extdate(newbranch->hsh);
955            newbranch = newbranch->nextbranch;
956         }
957 	return revno;
958 }
959 
960 
961 
962 	static char
extractdelta(pdelta)963 extractdelta(pdelta)
964 	struct hshentry const *pdelta;
965 /*  function:  compare information of pdelta to the authorlist, lockerlist,*/
966 /*             statelist, revlist and yield true if pdelta is selected.    */
967 
968 {
969 	struct rcslock const *plock;
970 	struct stateattri const *pstate;
971 	struct authors const *pauthor;
972 	struct Revpairs const *prevision;
973 	int length;
974 
975 	if ((pauthor = authorlist)) /* only certain authors wanted */
976 	    while (strcmp(pauthor->login, pdelta->author) != 0)
977 		if (!(pauthor = pauthor->nextauthor))
978 		    return false;
979 	if ((pstate = statelist)) /* only certain states wanted */
980 	    while (strcmp(pstate->status, pdelta->state) != 0)
981 		if (!(pstate = pstate->nextstate))
982 		    return false;
983 	if (lockflag) /* only locked revisions wanted */
984 	    for (plock = Locks;  ;  plock = plock->nextlock)
985 		if (!plock)
986 		    return false;
987 		else if (plock->delta == pdelta)
988 		    break;
989 	if ((prevision = Revlst)) /* only certain revs or branches wanted */
990 	    for (;;) {
991                 length = prevision->numfld;
992 		if (
993 		    countnumflds(pdelta->num) == length+(length&1) &&
994 		    0 <= compartial(pdelta->num, prevision->strtrev, length) &&
995 		    0 <= compartial(prevision->endrev, pdelta->num, length)
996 		)
997 		     break;
998 		if (!(prevision = prevision->rnext))
999 		    return false;
1000             }
1001 	return true;
1002 }
1003 
1004 
1005 
1006 	static void
getdatepair(argv)1007 getdatepair(argv)
1008    char   * argv;
1009 /*  function:  get time range from command line and store in datelist if    */
1010 /*             a time range specified or in duelst if a time spot specified */
1011 
1012 {
1013         register   char         c;
1014         struct     Datepairs    * nextdate;
1015 	char const		* rawdate;
1016 	int                     switchflag;
1017 
1018         argv--;
1019 	while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
1020 	    continue;
1021         if ( c == '\0' ) {
1022 	    error("missing date/time after -d");
1023             return;
1024         }
1025 
1026         while( c != '\0' )  {
1027 	    switchflag = false;
1028 	    nextdate = talloc(struct Datepairs);
1029             if ( c == '<' ) {   /*   case: -d <date   */
1030                 c = *++argv;
1031 		if (!(nextdate->ne_date = c!='='))
1032 		    c = *++argv;
1033                 (nextdate->strtdate)[0] = '\0';
1034 	    } else if (c == '>') { /* case: -d'>date' */
1035 		c = *++argv;
1036 		if (!(nextdate->ne_date = c!='='))
1037 		    c = *++argv;
1038 		(nextdate->enddate)[0] = '\0';
1039 		switchflag = true;
1040 	    } else {
1041                 rawdate = argv;
1042 		while( c != '<' && c != '>' && c != ';' && c != '\0')
1043 		     c = *++argv;
1044                 *argv = '\0';
1045 		if ( c == '>' ) switchflag=true;
1046 		str2date(rawdate,
1047 			 switchflag ? nextdate->enddate : nextdate->strtdate);
1048 		if ( c == ';' || c == '\0') {  /*  case: -d date  */
1049 		    VOID strcpy(nextdate->enddate,nextdate->strtdate);
1050                     nextdate->dnext = duelst;
1051                     duelst = nextdate;
1052 		    goto end;
1053 		} else {
1054 		    /*   case:   -d date<  or -d  date>; see switchflag */
1055 		    int eq = argv[1]=='=';
1056 		    nextdate->ne_date = !eq;
1057 		    argv += eq;
1058 		    while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
1059 			continue;
1060 		    if ( c == ';' || c == '\0') {
1061 			/* second date missing */
1062 			if (switchflag)
1063 			    *nextdate->strtdate= '\0';
1064 			else
1065 			    *nextdate->enddate= '\0';
1066 			nextdate->dnext = datelist;
1067 			datelist = nextdate;
1068 			goto end;
1069 		    }
1070                 }
1071             }
1072             rawdate = argv;
1073 	    while( c != '>' && c != '<' && c != ';' && c != '\0')
1074  		c = *++argv;
1075             *argv = '\0';
1076 	    str2date(rawdate,
1077 		     switchflag ? nextdate->strtdate : nextdate->enddate);
1078             nextdate->dnext = datelist;
1079 	    datelist = nextdate;
1080      end:
1081 	    if (RCSversion < VERSION(5))
1082 		nextdate->ne_date = 0;
1083 	    if ( c == '\0')  return;
1084 	    while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
1085 		continue;
1086         }
1087 }
1088 
1089 
1090 
1091 	static int
getnumericrev()1092 getnumericrev()
1093 /*  function:  get the numeric name of revisions which stored in revlist  */
1094 /*             and then stored the numeric names in Revlst                */
1095 /*             if branchflag, also add default branch                     */
1096 
1097 {
1098         struct  Revpairs        * ptr, *pt;
1099 	int n;
1100 	struct buf s, e;
1101 	char const *lrev;
1102 	struct buf const *rstart, *rend;
1103 
1104 	Revlst = 0;
1105         ptr = revlist;
1106 	bufautobegin(&s);
1107 	bufautobegin(&e);
1108         while( ptr ) {
1109 	    n = 0;
1110 	    rstart = &s;
1111 	    rend = &e;
1112 
1113 	    switch (ptr->numfld) {
1114 
1115 	      case 1: /* -rREV */
1116 		if (!expandsym(ptr->strtrev, &s))
1117 		    goto freebufs;
1118 		rend = &s;
1119 		n = countnumflds(s.string);
1120 		if (!n  &&  (lrev = tiprev())) {
1121 		    bufscpy(&s, lrev);
1122 		    n = countnumflds(lrev);
1123 		}
1124 		break;
1125 
1126 	      case 2: /* -rREV: */
1127 		if (!expandsym(ptr->strtrev, &s))
1128 		    goto freebufs;
1129 		bufscpy(&e, s.string);
1130 		n = countnumflds(s.string);
1131 		(n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
1132 		break;
1133 
1134 	      case 3: /* -r:REV */
1135 		if (!expandsym(ptr->endrev, &e))
1136 		    goto freebufs;
1137 		if ((n = countnumflds(e.string)) < 2)
1138 		    bufscpy(&s, ".0");
1139 		else {
1140 		    bufscpy(&s, e.string);
1141 		    VOID strcpy(strrchr(s.string,'.'), ".0");
1142 		}
1143 		break;
1144 
1145 	      default: /* -rREV1:REV2 */
1146 		if (!(
1147 			expandsym(ptr->strtrev, &s)
1148 		    &&	expandsym(ptr->endrev, &e)
1149 		    &&	checkrevpair(s.string, e.string)
1150 		))
1151 		    goto freebufs;
1152 		n = countnumflds(s.string);
1153 		/* Swap if out of order.  */
1154 		if (compartial(s.string,e.string,n) > 0) {
1155 		    rstart = &e;
1156 		    rend = &s;
1157 		}
1158 		break;
1159 	    }
1160 
1161 	    if (n) {
1162 		pt = ftalloc(struct Revpairs);
1163 		pt->numfld = n;
1164 		pt->strtrev = fstr_save(rstart->string);
1165 		pt->endrev = fstr_save(rend->string);
1166                 pt->rnext = Revlst;
1167                 Revlst = pt;
1168 	    }
1169 	    ptr = ptr->rnext;
1170         }
1171         /* Now take care of branchflag */
1172 	if (branchflag && (Dbranch||Head)) {
1173 	    pt = ftalloc(struct Revpairs);
1174 	    pt->strtrev = pt->endrev =
1175 		Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
1176 	    pt->rnext=Revlst; Revlst=pt;
1177 	    pt->numfld = countnumflds(pt->strtrev);
1178         }
1179 
1180       freebufs:
1181 	bufautoend(&s);
1182 	bufautoend(&e);
1183 	return !ptr;
1184 }
1185 
1186 
1187 
1188 	static int
checkrevpair(num1,num2)1189 checkrevpair(num1,num2)
1190 	char const *num1, *num2;
1191 /*  function:  check whether num1, num2 are legal pair,i.e.
1192     only the last field are different and have same number of
1193     fields( if length <= 2, may be different if first field)   */
1194 
1195 {
1196 	int length = countnumflds(num1);
1197 
1198 	if (
1199 			countnumflds(num2) != length
1200 		||	(2 < length  &&  compartial(num1, num2, length-1) != 0)
1201 	) {
1202 	    rcserror("invalid branch or revision pair %s : %s", num1, num2);
1203             return false;
1204         }
1205 
1206         return true;
1207 }
1208 
1209 
1210 
1211 	static void
getrevpairs(argv)1212 getrevpairs(argv)
1213 register     char    * argv;
1214 /*  function:  get revision or branch range from command line, and   */
1215 /*             store in revlist                                      */
1216 
1217 {
1218         register    char    c;
1219         struct      Revpairs  * nextrevpair;
1220 	int separator;
1221 
1222 	c = *argv;
1223 
1224 	/* Support old ambiguous '-' syntax; this will go away.  */
1225 	if (strchr(argv,':'))
1226 	    separator = ':';
1227 	else {
1228 	    if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
1229 		warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
1230 	    separator = '-';
1231 	}
1232 
1233 	for (;;) {
1234 	    while (c==' ' || c=='\t' || c=='\n')
1235 		c = *++argv;
1236 	    nextrevpair = talloc(struct Revpairs);
1237             nextrevpair->rnext = revlist;
1238             revlist = nextrevpair;
1239 	    nextrevpair->numfld = 1;
1240 	    nextrevpair->strtrev = argv;
1241 	    for (;;  c = *++argv) {
1242 		switch (c) {
1243 		    default:
1244 			continue;
1245 		    case '\0': case ' ': case '\t': case '\n':
1246 		    case ',': case ';':
1247 			break;
1248 		    case ':': case '-':
1249 			if (c == separator)
1250 			    break;
1251 			continue;
1252 		}
1253 		break;
1254 	    }
1255 	    *argv = '\0';
1256 	    while (c==' ' || c=='\t' || c=='\n')
1257 		c = *++argv;
1258 	    if (c == separator) {
1259 		while ((c = *++argv) == ' ' || c == '\t' || c =='\n')
1260 		    continue;
1261 		nextrevpair->endrev = argv;
1262 		for (;;  c = *++argv) {
1263 		    switch (c) {
1264 			default:
1265 			    continue;
1266 			case '\0': case ' ': case '\t': case '\n':
1267 			case ',': case ';':
1268 			    break;
1269 			case ':': case '-':
1270 			    if (c == separator)
1271 				break;
1272 			    continue;
1273 		    }
1274 		    break;
1275 		}
1276 		*argv = '\0';
1277 		while (c==' ' || c=='\t' || c =='\n')
1278 		    c = *++argv;
1279 		nextrevpair->numfld =
1280 		    !nextrevpair->endrev[0] ? 2 /* -rREV: */ :
1281 		    !nextrevpair->strtrev[0] ? 3 /* -r:REV */ :
1282 		    4 /* -rREV1:REV2 */;
1283             }
1284 	    if (!c)
1285 		break;
1286 	    else if (c==',' || c==';')
1287 		c = *++argv;
1288 	    else
1289 		error("missing `,' near `%c%s'", c, argv+1);
1290 	}
1291 }
1292