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