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