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