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