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