1 /* $NetBSD: rlog.c,v 1.2 2016/01/14 04:22:39 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
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 P((struct hshentry const*));
193 static int checkrevpair P((char const*,char const*));
194 static int extdate P((struct hshentry*));
195 static int getnumericrev P((void));
196 static struct hshentry const *readdeltalog P((void));
197 static void cleanup P((void));
198 static void exttree P((struct hshentry*));
199 static void getauthor P((char*));
200 static void getdatepair P((char*));
201 static void getlocker P((char*));
202 static void getrevpairs P((char*));
203 static void getscript P((struct hshentry*));
204 static void getstate P((char*));
205 static void putabranch P((struct hshentry const*));
206 static void putadelta P((struct hshentry const*,struct hshentry const*,int));
207 static void putforest P((struct branchhead const*));
208 static void putree P((struct hshentry const*));
209 static void putrunk P((void));
210 static void recentdate P((struct hshentry const*,struct Datepairs*));
211 static void trunclocks P((void));
212
213 static char const *insDelFormat;
214 static int branchflag; /*set on -b */
215 static int exitstatus;
216 static int lockflag;
217 static int commitid;
218 static struct Datepairs *datelist, *duelst;
219 static struct Revpairs *revlist, *Revlst;
220 static struct authors *authorlist;
221 static struct rcslockers *lockerlist;
222 static struct stateattri *statelist;
223
224
225 mainProg(rlogId, "rlog", "Id: rlog.c,v 5.18 1995/06/16 06:19:24 eggert Exp ")
226 {
227 static char const cmdusage[] =
228 "\nrlog usage: rlog -{cbhLNRt} -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ...";
229
230 register FILE *out;
231 char *a, **newargv;
232 struct Datepairs *currdate;
233 char const *accessListString, *accessFormat;
234 char const *headFormat, *symbolFormat;
235 struct access const *curaccess;
236 struct assoc const *curassoc;
237 struct hshentry const *delta;
238 struct rcslock const *currlock;
239 int descflag, selectflag;
240 int onlylockflag; /* print only files with locks */
241 int onlyRCSflag; /* print only RCS pathname */
242 int pre5;
243 int shownames;
244 int revno;
245
246 descflag = selectflag = shownames = true;
247 onlylockflag = onlyRCSflag = false;
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 case 'c':
256 commitid = true;
257 break;
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 VOID strcpy(currdate->strtdate, "0.0.0.0.0.0");
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 exitmain(exitstatus);
475 }
476
477 static void
cleanup()478 cleanup()
479 {
480 if (nerror) exitstatus = EXIT_FAILURE;
481 Izclose(&finptr);
482 }
483
484 #if RCS_lint
485 # define exiterr rlogExit
486 #endif
487 void
exiterr()488 exiterr()
489 {
490 _exit(EXIT_FAILURE);
491 }
492
493
494
495 static void
putrunk()496 putrunk()
497 /* function: print revisions chosen, which are in trunk */
498
499 {
500 register struct hshentry const *ptr;
501
502 for (ptr = Head; ptr; ptr = ptr->next)
503 putadelta(ptr, ptr->next, true);
504 }
505
506
507
508 static void
putree(root)509 putree(root)
510 struct hshentry const *root;
511 /* function: print delta tree (not including trunk) in reverse
512 order on each branch */
513
514 {
515 if (!root) return;
516
517 putree(root->next);
518
519 putforest(root->branches);
520 }
521
522
523
524
525 static void
putforest(branchroot)526 putforest(branchroot)
527 struct branchhead const *branchroot;
528 /* function: print branches that has the same direct ancestor */
529 {
530 if (!branchroot) return;
531
532 putforest(branchroot->nextbranch);
533
534 putabranch(branchroot->hsh);
535 putree(branchroot->hsh);
536 }
537
538
539
540
541 static void
putabranch(root)542 putabranch(root)
543 struct hshentry const *root;
544 /* function : print one branch */
545
546 {
547 if (!root) return;
548
549 putabranch(root->next);
550
551 putadelta(root, root, false);
552 }
553
554
555
556
557
558 static void
putadelta(node,editscript,trunk)559 putadelta(node,editscript,trunk)
560 register struct hshentry const *node, *editscript;
561 int trunk;
562 /* function: Print delta node if node->selector is set. */
563 /* editscript indicates where the editscript is stored */
564 /* trunk indicated whether this node is in trunk */
565 {
566 static char emptych[] = EMPTYLOG;
567
568 register FILE *out;
569 char const *s;
570 size_t n;
571 struct branchhead const *newbranch;
572 struct buf branchnum;
573 char datebuf[datesize + zonelenmax];
574 int pre5 = RCSversion < VERSION(5);
575
576 if (!node->selector)
577 return;
578
579 out = stdout;
580 aprintf(out,
581 "----------------------------\nrevision %s%s",
582 node->num, pre5 ? " " : ""
583 );
584 if ( node->lockedby )
585 aprintf(out, pre5+"\tlocked by: %s;", node->lockedby);
586
587 aprintf(out, "\ndate: %s; author: %s; state: %s;",
588 date2str(node->date, datebuf),
589 node->author, node->state
590 );
591 if (commitid && node->commitid)
592 aprintf(out, " commitid: %s;", node->commitid);
593
594 if ( editscript ) {
595 if(trunk)
596 aprintf(out, insDelFormat,
597 editscript->deletelns, editscript->insertlns);
598 else
599 aprintf(out, insDelFormat,
600 editscript->insertlns, editscript->deletelns);
601 }
602
603 newbranch = node->branches;
604 if ( newbranch ) {
605 bufautobegin(&branchnum);
606 aputs("\nbranches:", out);
607 while( newbranch ) {
608 getbranchno(newbranch->hsh->num, &branchnum);
609 aprintf(out, " %s;", branchnum.string);
610 newbranch = newbranch->nextbranch;
611 }
612 bufautoend(&branchnum);
613 }
614
615 afputc('\n', out);
616 s = node->log.string;
617 if (!(n = node->log.size)) {
618 s = emptych;
619 n = sizeof(emptych)-1;
620 }
621 awrite(s, n, out);
622 if (s[n-1] != '\n')
623 afputc('\n', out);
624 }
625
626
627 static struct hshentry const *
readdeltalog()628 readdeltalog()
629 /* Function : get the log message and skip the text of a deltatext node.
630 * Return the delta found.
631 * Assumes the current lexeme is not yet in nexttok; does not
632 * advance nexttok.
633 */
634 {
635 register struct hshentry * Delta;
636 struct buf logbuf;
637 struct cbuf cb;
638
639 if (eoflex())
640 fatserror("missing delta log");
641 nextlex();
642 if (!(Delta = getnum()))
643 fatserror("delta number corrupted");
644 getkeystring(Klog);
645 if (Delta->log.string)
646 fatserror("duplicate delta log");
647 bufautobegin(&logbuf);
648 cb = savestring(&logbuf);
649 Delta->log = bufremember(&logbuf, cb.size);
650
651 ignorephrases(Ktext);
652 getkeystring(Ktext);
653 Delta->insertlns = Delta->deletelns = 0;
654 if ( Delta != Head)
655 getscript(Delta);
656 else
657 readstring();
658 return Delta;
659 }
660
661
662 static void
getscript(Delta)663 getscript(Delta)
664 struct hshentry * Delta;
665 /* function: read edit script of Delta and count how many lines added */
666 /* and deleted in the script */
667
668 {
669 int ed; /* editor command */
670 declarecache;
671 register RILE *fin;
672 register int c;
673 register long i;
674 struct diffcmd dc;
675
676 fin = finptr;
677 setupcache(fin);
678 initdiffcmd(&dc);
679 while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
680 if (!ed)
681 Delta->deletelns += dc.nlines;
682 else {
683 /* skip scripted lines */
684 i = dc.nlines;
685 Delta->insertlns += i;
686 cache(fin);
687 do {
688 for (;;) {
689 cacheget_(c)
690 switch (c) {
691 default:
692 continue;
693 case SDELIM:
694 cacheget_(c)
695 if (c == SDELIM)
696 continue;
697 if (--i)
698 unexpected_EOF();
699 nextc = c;
700 uncache(fin);
701 return;
702 case '\n':
703 break;
704 }
705 break;
706 }
707 ++rcsline;
708 } while (--i);
709 uncache(fin);
710 }
711 }
712
713
714
715
716
717
718
719 static void
exttree(root)720 exttree(root)
721 struct hshentry *root;
722 /* function: select revisions , starting with root */
723
724 {
725 struct branchhead const *newbranch;
726
727 if (!root) return;
728
729 root->selector = extractdelta(root);
730 root->log.string = 0;
731 exttree(root->next);
732
733 newbranch = root->branches;
734 while( newbranch ) {
735 exttree(newbranch->hsh);
736 newbranch = newbranch->nextbranch;
737 }
738 }
739
740
741
742
743 static void
getlocker(argv)744 getlocker(argv)
745 char * argv;
746 /* function : get the login names of lockers from command line */
747 /* and store in lockerlist. */
748
749 {
750 register char c;
751 struct rcslockers *newlocker;
752 argv--;
753 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
754 continue;
755 if ( c == '\0') {
756 lockerlist = 0;
757 return;
758 }
759
760 while( c != '\0' ) {
761 newlocker = talloc(struct rcslockers);
762 newlocker->lockerlink = lockerlist;
763 newlocker->login = argv;
764 lockerlist = newlocker;
765 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
766 continue;
767 *argv = '\0';
768 if ( c == '\0' ) return;
769 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
770 continue;
771 }
772 }
773
774
775
776 static void
getauthor(argv)777 getauthor(argv)
778 char *argv;
779 /* function: get the author's name from command line */
780 /* and store in authorlist */
781
782 {
783 int c;
784 struct authors * newauthor;
785
786 argv--;
787 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
788 continue;
789 if ( c == '\0' ) {
790 authorlist = talloc(struct authors);
791 authorlist->login = getusername(false);
792 authorlist->nextauthor = 0;
793 return;
794 }
795
796 while( c != '\0' ) {
797 newauthor = talloc(struct authors);
798 newauthor->nextauthor = authorlist;
799 newauthor->login = argv;
800 authorlist = newauthor;
801 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
802 continue;
803 * argv = '\0';
804 if ( c == '\0') return;
805 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
806 continue;
807 }
808 }
809
810
811
812
813 static void
getstate(argv)814 getstate(argv)
815 char * argv;
816 /* function : get the states of revisions from command line */
817 /* and store in statelist */
818
819 {
820 register char c;
821 struct stateattri *newstate;
822
823 argv--;
824 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
825 continue;
826 if ( c == '\0'){
827 error("missing state attributes after -s options");
828 return;
829 }
830
831 while( c != '\0' ) {
832 newstate = talloc(struct stateattri);
833 newstate->nextstate = statelist;
834 newstate->status = argv;
835 statelist = newstate;
836 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
837 continue;
838 *argv = '\0';
839 if ( c == '\0' ) return;
840 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
841 continue;
842 }
843 }
844
845
846
847 static void
trunclocks()848 trunclocks()
849 /* Function: Truncate the list of locks to those that are held by the */
850 /* id's on lockerlist. Do not truncate if lockerlist empty. */
851
852 {
853 struct rcslockers const *plocker;
854 struct rcslock *p, **pp;
855
856 if (!lockerlist) return;
857
858 /* shorten Locks to those contained in lockerlist */
859 for (pp = &Locks; (p = *pp); )
860 for (plocker = lockerlist; ; )
861 if (strcmp(plocker->login, p->login) == 0) {
862 pp = &p->nextlock;
863 break;
864 } else if (!(plocker = plocker->lockerlink)) {
865 *pp = p->nextlock;
866 break;
867 }
868 }
869
870
871
872 static void
recentdate(root,pd)873 recentdate(root, pd)
874 struct hshentry const *root;
875 struct Datepairs *pd;
876 /* function: Finds the delta that is closest to the cutoff date given by */
877 /* pd among the revisions selected by exttree. */
878 /* Successively narrows down the interval given by pd, */
879 /* and sets the strtdate of pd to the date of the selected delta */
880 {
881 struct branchhead const *newbranch;
882
883 if (!root) return;
884 if (root->selector) {
885 if ( cmpdate(root->date, pd->strtdate) >= 0 &&
886 cmpdate(root->date, pd->enddate) <= 0)
887 VOID strcpy(pd->strtdate, root->date);
888 }
889
890 recentdate(root->next, pd);
891 newbranch = root->branches;
892 while( newbranch) {
893 recentdate(newbranch->hsh, pd);
894 newbranch = newbranch->nextbranch;
895 }
896 }
897
898
899
900
901
902
903 static int
extdate(root)904 extdate(root)
905 struct hshentry * root;
906 /* function: select revisions which are in the date range specified */
907 /* in duelst and datelist, start at root */
908 /* Yield number of revisions selected, including those already selected. */
909 {
910 struct branchhead const *newbranch;
911 struct Datepairs const *pdate;
912 int revno, ne;
913
914 if (!root)
915 return 0;
916
917 if ( datelist || duelst) {
918 pdate = datelist;
919 while( pdate ) {
920 ne = pdate->ne_date;
921 if (
922 (!pdate->strtdate[0]
923 || ne <= cmpdate(root->date, pdate->strtdate))
924 &&
925 (!pdate->enddate[0]
926 || ne <= cmpdate(pdate->enddate, root->date))
927 )
928 break;
929 pdate = pdate->dnext;
930 }
931 if (!pdate) {
932 pdate = duelst;
933 for (;;) {
934 if (!pdate) {
935 root->selector = false;
936 break;
937 }
938 if (cmpdate(root->date, pdate->strtdate) == 0)
939 break;
940 pdate = pdate->dnext;
941 }
942 }
943 }
944 revno = root->selector + extdate(root->next);
945
946 newbranch = root->branches;
947 while( newbranch ) {
948 revno += extdate(newbranch->hsh);
949 newbranch = newbranch->nextbranch;
950 }
951 return revno;
952 }
953
954
955
956 static char
extractdelta(pdelta)957 extractdelta(pdelta)
958 struct hshentry const *pdelta;
959 /* function: compare information of pdelta to the authorlist, lockerlist,*/
960 /* statelist, revlist and yield true if pdelta is selected. */
961
962 {
963 struct rcslock const *plock;
964 struct stateattri const *pstate;
965 struct authors const *pauthor;
966 struct Revpairs const *prevision;
967 int length;
968
969 if ((pauthor = authorlist)) /* only certain authors wanted */
970 while (strcmp(pauthor->login, pdelta->author) != 0)
971 if (!(pauthor = pauthor->nextauthor))
972 return false;
973 if ((pstate = statelist)) /* only certain states wanted */
974 while (strcmp(pstate->status, pdelta->state) != 0)
975 if (!(pstate = pstate->nextstate))
976 return false;
977 if (lockflag) { /* only locked revisions wanted */
978 for (plock = Locks; ; plock = plock->nextlock)
979 if (!plock)
980 return false;
981 else if (plock->delta == pdelta)
982 break;
983 }
984 if ((prevision = Revlst)) /* only certain revs or branches wanted */
985 for (;;) {
986 length = prevision->numfld;
987 if (
988 countnumflds(pdelta->num) == length+(length&1) &&
989 0 <= compartial(pdelta->num, prevision->strtrev, length) &&
990 0 <= compartial(prevision->endrev, pdelta->num, length)
991 )
992 break;
993 if (!(prevision = prevision->rnext))
994 return false;
995 }
996 return true;
997 }
998
999
1000
1001 static void
getdatepair(argv)1002 getdatepair(argv)
1003 char * argv;
1004 /* function: get time range from command line and store in datelist if */
1005 /* a time range specified or in duelst if a time spot specified */
1006
1007 {
1008 register char c;
1009 struct Datepairs * nextdate;
1010 char const * rawdate;
1011 int switchflag;
1012
1013 argv--;
1014 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
1015 continue;
1016 if ( c == '\0' ) {
1017 error("missing date/time after -d");
1018 return;
1019 }
1020
1021 while( c != '\0' ) {
1022 switchflag = false;
1023 nextdate = talloc(struct Datepairs);
1024 if ( c == '<' ) { /* case: -d <date */
1025 c = *++argv;
1026 if (!(nextdate->ne_date = c!='='))
1027 c = *++argv;
1028 (nextdate->strtdate)[0] = '\0';
1029 } else if (c == '>') { /* case: -d'>date' */
1030 c = *++argv;
1031 if (!(nextdate->ne_date = c!='='))
1032 c = *++argv;
1033 (nextdate->enddate)[0] = '\0';
1034 switchflag = true;
1035 } else {
1036 rawdate = argv;
1037 while( c != '<' && c != '>' && c != ';' && c != '\0')
1038 c = *++argv;
1039 *argv = '\0';
1040 if ( c == '>' ) switchflag=true;
1041 str2date(rawdate,
1042 switchflag ? nextdate->enddate : nextdate->strtdate);
1043 if ( c == ';' || c == '\0') { /* case: -d date */
1044 VOID strcpy(nextdate->enddate,nextdate->strtdate);
1045 nextdate->dnext = duelst;
1046 duelst = nextdate;
1047 goto end;
1048 } else {
1049 /* case: -d date< or -d date>; see switchflag */
1050 int eq = argv[1]=='=';
1051 nextdate->ne_date = !eq;
1052 argv += eq;
1053 while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
1054 continue;
1055 if ( c == ';' || c == '\0') {
1056 /* second date missing */
1057 if (switchflag)
1058 *nextdate->strtdate= '\0';
1059 else
1060 *nextdate->enddate= '\0';
1061 nextdate->dnext = datelist;
1062 datelist = nextdate;
1063 goto end;
1064 }
1065 }
1066 }
1067 rawdate = argv;
1068 while( c != '>' && c != '<' && c != ';' && c != '\0')
1069 c = *++argv;
1070 *argv = '\0';
1071 str2date(rawdate,
1072 switchflag ? nextdate->strtdate : nextdate->enddate);
1073 nextdate->dnext = datelist;
1074 datelist = nextdate;
1075 end:
1076 if (RCSversion < VERSION(5))
1077 nextdate->ne_date = 0;
1078 if ( c == '\0') return;
1079 while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
1080 continue;
1081 }
1082 }
1083
1084
1085
1086 static int
getnumericrev()1087 getnumericrev()
1088 /* function: get the numeric name of revisions which stored in revlist */
1089 /* and then stored the numeric names in Revlst */
1090 /* if branchflag, also add default branch */
1091
1092 {
1093 struct Revpairs * ptr, *pt;
1094 int n;
1095 struct buf s, e;
1096 char const *lrev;
1097 struct buf const *rstart, *rend;
1098
1099 Revlst = 0;
1100 ptr = revlist;
1101 bufautobegin(&s);
1102 bufautobegin(&e);
1103 while( ptr ) {
1104 n = 0;
1105 rstart = &s;
1106 rend = &e;
1107
1108 switch (ptr->numfld) {
1109
1110 case 1: /* -rREV */
1111 if (!expandsym(ptr->strtrev, &s))
1112 goto freebufs;
1113 rend = &s;
1114 n = countnumflds(s.string);
1115 if (!n && (lrev = tiprev())) {
1116 bufscpy(&s, lrev);
1117 n = countnumflds(lrev);
1118 }
1119 break;
1120
1121 case 2: /* -rREV: */
1122 if (!expandsym(ptr->strtrev, &s))
1123 goto freebufs;
1124 bufscpy(&e, s.string);
1125 n = countnumflds(s.string);
1126 (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0;
1127 break;
1128
1129 case 3: /* -r:REV */
1130 if (!expandsym(ptr->endrev, &e))
1131 goto freebufs;
1132 if ((n = countnumflds(e.string)) < 2)
1133 bufscpy(&s, ".0");
1134 else {
1135 bufscpy(&s, e.string);
1136 VOID strcpy(strrchr(s.string,'.'), ".0");
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