No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / usr.bin / rcs / rlog / rlog.c
blob76b92064f9961a7e0f424e861a6d9bdc1fa91472
1 /* $NetBSD: rlog.c,v 1.1.1.2 1996/10/13 21:57:27 veego Exp $ */
3 /* Print log messages and other information about RCS files. */
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.
9 This file is part of RCS.
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.
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.
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.
26 Report problems and direct all questions to:
28 rcs-bugs@cs.purdue.edu
33 * $Log: rlog.c,v $
34 * Revision 5.18 1995/06/16 06:19:24 eggert
35 * Update FSF address.
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
43 * Revision 5.16 1994/04/13 16:30:34 eggert
44 * Fix bug; `rlog -lxxx' inverted the sense of -l.
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.
51 * Revision 5.14 1993/11/09 17:40:15 eggert
52 * -V now prints version on stdout and exits.
54 * Revision 5.13 1993/11/03 17:42:27 eggert
55 * Add -N, -z. Ignore -T.
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 _.
61 * Revision 5.11 1992/01/24 18:44:19 eggert
62 * Don't duplicate unexpected_EOF's function. lint -> RCS_lint
64 * Revision 5.10 1992/01/06 02:42:34 eggert
65 * Update usage string.
66 * while (E) ; -> while (E) continue;
68 * Revision 5.9 1991/09/17 19:07:40 eggert
69 * Getscript() didn't uncache partial lines.
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).
76 * Revision 5.7 1991/04/21 11:58:31 eggert
77 * Add -x, RCSINIT, MS-DOS support.
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)
83 * Revision 5.5 1990/11/01 05:03:55 eggert
84 * Permit arbitrary data in logs and comment leaders.
86 * Revision 5.4 1990/10/04 06:30:22 eggert
87 * Accumulate exit status across files.
89 * Revision 5.3 1990/09/11 02:41:16 eggert
90 * Plug memory leak.
92 * Revision 5.2 1990/09/04 08:02:33 eggert
93 * Count RCS lines better.
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.
102 * Revision 4.7 89/05/01 15:13:48 narten
103 * changed copyright header to reflect current distribution rules
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.
109 * Revision 4.5 87/12/18 11:46:38 narten
110 * more lint cleanups (Guy Harris)
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
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)
120 * Revision 1.2 87/03/27 14:22:45 jenkins
121 * Port to suns
123 * Revision 4.2 83/12/05 09:18:09 wft
124 * changed rewriteflag to external.
126 * Revision 4.1 83/05/11 16:16:55 wft
127 * Added -b, updated getnumericrev() accordingly.
128 * Replaced getpwuid() with getcaller().
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()).
135 * Revision 3.6 82/12/24 15:57:53 wft
136 * shortened output format.
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.
142 * Revision 3.4 82/12/04 13:26:25 wft
143 * Replaced getdelta() with gettree(); removed updating of field lockedby.
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.
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.
155 * Revision 3.1 82/10/13 16:07:56 wft
156 * fixed type of variables receiving from getc() (char -> int).
161 #include "rcsbase.h"
163 struct rcslockers { /* lockers in locker option; stored */
164 char const * login; /* lockerlist */
165 struct rcslockers * lockerlink;
168 struct stateattri { /* states in state option; stored in */
169 char const * status; /* statelist */
170 struct stateattri * nextstate;
173 struct authors { /* login names in author option; */
174 char const * login; /* stored in authorlist */
175 struct authors * nextauthor;
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;
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 <= */
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));
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;
224 mainProg(rlogId, "rlog", "Id: rlog.c,v 5.18 1995/06/16 06:19:24 eggert Exp")
226 static char const cmdusage[] =
227 "\nrlog usage: rlog -{bhLNRt} -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ...";
229 register FILE *out;
230 char *a, **newargv;
231 struct Datepairs *currdate;
232 char const *accessListString, *accessFormat;
233 char const *headFormat, *symbolFormat;
234 struct access const *curaccess;
235 struct assoc const *curassoc;
236 struct hshentry const *delta;
237 struct rcslock const *currlock;
238 int descflag, selectflag;
239 int onlylockflag; /* print only files with locks */
240 int onlyRCSflag; /* print only RCS pathname */
241 int pre5;
242 int shownames;
243 int revno;
245 descflag = selectflag = shownames = true;
246 onlylockflag = onlyRCSflag = false;
247 out = stdout;
248 suffixes = X_DEFAULT;
250 argc = getRCSINIT(argc, argv, &newargv);
251 argv = newargv;
252 while (a = *++argv, 0<--argc && *a++=='-') {
253 switch (*a++) {
255 case 'L':
256 onlylockflag = true;
257 break;
259 case 'N':
260 shownames = false;
261 break;
263 case 'R':
264 onlyRCSflag =true;
265 break;
267 case 'l':
268 lockflag = true;
269 getlocker(a);
270 break;
272 case 'b':
273 branchflag = true;
274 break;
276 case 'r':
277 getrevpairs(a);
278 break;
280 case 'd':
281 getdatepair(a);
282 break;
284 case 's':
285 getstate(a);
286 break;
288 case 'w':
289 getauthor(a);
290 break;
292 case 'h':
293 descflag = false;
294 break;
296 case 't':
297 selectflag = false;
298 break;
300 case 'q':
301 /* This has no effect; it's here for consistency. */
302 quietflag = true;
303 break;
305 case 'x':
306 suffixes = a;
307 break;
309 case 'z':
310 zone_set(a);
311 break;
313 case 'T':
314 /* Ignore -T, so that RCSINIT can contain -T. */
315 if (*a)
316 goto unknown;
317 break;
319 case 'V':
320 setRCSversion(*argv);
321 break;
323 default:
324 unknown:
325 error("unknown option: %s%s", *argv, cmdusage);
328 } /* end of option processing */
330 if (! (descflag|selectflag)) {
331 warn("-t overrides -h.");
332 descflag = true;
335 pre5 = RCSversion < VERSION(5);
336 if (pre5) {
337 accessListString = "\naccess list: ";
338 accessFormat = " %s";
339 headFormat = "RCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: ";
340 insDelFormat = " lines added/del: %ld/%ld";
341 symbolFormat = " %s: %s;";
342 } else {
343 accessListString = "\naccess list:";
344 accessFormat = "\n\t%s";
345 headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
346 insDelFormat = " lines: +%ld -%ld";
347 symbolFormat = "\n\t%s: %s";
350 /* Now handle all pathnames. */
351 if (nerror)
352 cleanup();
353 else if (argc < 1)
354 faterror("no input file%s", cmdusage);
355 else
356 for (; 0 < argc; cleanup(), ++argv, --argc) {
357 ffree();
359 if (pairnames(argc, argv, rcsreadopen, true, false) <= 0)
360 continue;
363 * RCSname contains the name of the RCS file,
364 * and finptr the file descriptor;
365 * workname contains the name of the working file.
368 /* Keep only those locks given by -l. */
369 if (lockflag)
370 trunclocks();
372 /* do nothing if -L is given and there are no locks*/
373 if (onlylockflag && !Locks)
374 continue;
376 if ( onlyRCSflag ) {
377 aprintf(out, "%s\n", RCSname);
378 continue;
381 gettree();
383 if (!getnumericrev())
384 continue;
387 * Output the first character with putc, not printf.
388 * Otherwise, an SVR4 stdio bug buffers output inefficiently.
390 aputc_('\n', out)
392 /* print RCS pathname, working pathname and optional
393 administrative information */
394 /* could use getfullRCSname() here, but that is very slow */
395 aprintf(out, headFormat, RCSname, workname,
396 Head ? " " : "", Head ? Head->num : "",
397 Dbranch ? " " : "", Dbranch ? Dbranch : "",
398 StrictLocks ? " strict" : ""
400 currlock = Locks;
401 while( currlock ) {
402 aprintf(out, symbolFormat, currlock->login,
403 currlock->delta->num);
404 currlock = currlock->nextlock;
406 if (StrictLocks && pre5)
407 aputs(" ; strict" + (Locks?3:0), out);
409 aputs(accessListString, out); /* print access list */
410 curaccess = AccessList;
411 while(curaccess) {
412 aprintf(out, accessFormat, curaccess->login);
413 curaccess = curaccess->nextaccess;
416 if (shownames) {
417 aputs("\nsymbolic names:", out); /* print symbolic names */
418 for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
419 aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
421 if (pre5) {
422 aputs("\ncomment leader: \"", out);
423 awrite(Comment.string, Comment.size, out);
424 afputc('\"', out);
426 if (!pre5 || Expand != KEYVAL_EXPAND)
427 aprintf(out, "\nkeyword substitution: %s",
428 expand_names[Expand]
431 aprintf(out, "\ntotal revisions: %d", TotalDeltas);
433 revno = 0;
435 if (Head && selectflag & descflag) {
437 exttree(Head);
439 /* get most recently date of the dates pointed by duelst */
440 currdate = duelst;
441 while( currdate) {
442 VOID strcpy(currdate->strtdate, "0.0.0.0.0.0");
443 recentdate(Head, currdate);
444 currdate = currdate->dnext;
447 revno = extdate(Head);
449 aprintf(out, ";\tselected revisions: %d", revno);
452 afputc('\n',out);
453 if (descflag) {
454 aputs("description:\n", out);
455 getdesc(true);
457 if (revno) {
458 while (! (delta = readdeltalog())->selector || --revno)
459 continue;
460 if (delta->next && countnumflds(delta->num)==2)
461 /* Read through delta->next to get its insertlns. */
462 while (readdeltalog() != delta->next)
463 continue;
464 putrunk();
465 putree(Head);
467 aputs("=============================================================================\n",out);
469 Ofclose(out);
470 exitmain(exitstatus);
473 static void
474 cleanup()
476 if (nerror) exitstatus = EXIT_FAILURE;
477 Izclose(&finptr);
480 #if RCS_lint
481 # define exiterr rlogExit
482 #endif
483 void
484 exiterr()
486 _exit(EXIT_FAILURE);
491 static void
492 putrunk()
493 /* function: print revisions chosen, which are in trunk */
496 register struct hshentry const *ptr;
498 for (ptr = Head; ptr; ptr = ptr->next)
499 putadelta(ptr, ptr->next, true);
504 static void
505 putree(root)
506 struct hshentry const *root;
507 /* function: print delta tree (not including trunk) in reverse
508 order on each branch */
511 if (!root) return;
513 putree(root->next);
515 putforest(root->branches);
521 static void
522 putforest(branchroot)
523 struct branchhead const *branchroot;
524 /* function: print branches that has the same direct ancestor */
526 if (!branchroot) return;
528 putforest(branchroot->nextbranch);
530 putabranch(branchroot->hsh);
531 putree(branchroot->hsh);
537 static void
538 putabranch(root)
539 struct hshentry const *root;
540 /* function : print one branch */
543 if (!root) return;
545 putabranch(root->next);
547 putadelta(root, root, false);
554 static void
555 putadelta(node,editscript,trunk)
556 register struct hshentry const *node, *editscript;
557 int trunk;
558 /* function: Print delta node if node->selector is set. */
559 /* editscript indicates where the editscript is stored */
560 /* trunk indicated whether this node is in trunk */
562 static char emptych[] = EMPTYLOG;
564 register FILE *out;
565 char const *s;
566 size_t n;
567 struct branchhead const *newbranch;
568 struct buf branchnum;
569 char datebuf[datesize + zonelenmax];
570 int pre5 = RCSversion < VERSION(5);
572 if (!node->selector)
573 return;
575 out = stdout;
576 aprintf(out,
577 "----------------------------\nrevision %s%s",
578 node->num, pre5 ? " " : ""
580 if ( node->lockedby )
581 aprintf(out, pre5+"\tlocked by: %s;", node->lockedby);
583 aprintf(out, "\ndate: %s; author: %s; state: %s;",
584 date2str(node->date, datebuf),
585 node->author, node->state
588 if ( editscript )
589 if(trunk)
590 aprintf(out, insDelFormat,
591 editscript->deletelns, editscript->insertlns);
592 else
593 aprintf(out, insDelFormat,
594 editscript->insertlns, editscript->deletelns);
596 newbranch = node->branches;
597 if ( newbranch ) {
598 bufautobegin(&branchnum);
599 aputs("\nbranches:", out);
600 while( newbranch ) {
601 getbranchno(newbranch->hsh->num, &branchnum);
602 aprintf(out, " %s;", branchnum.string);
603 newbranch = newbranch->nextbranch;
605 bufautoend(&branchnum);
608 afputc('\n', out);
609 s = node->log.string;
610 if (!(n = node->log.size)) {
611 s = emptych;
612 n = sizeof(emptych)-1;
614 awrite(s, n, out);
615 if (s[n-1] != '\n')
616 afputc('\n', out);
620 static struct hshentry const *
621 readdeltalog()
622 /* Function : get the log message and skip the text of a deltatext node.
623 * Return the delta found.
624 * Assumes the current lexeme is not yet in nexttok; does not
625 * advance nexttok.
628 register struct hshentry * Delta;
629 struct buf logbuf;
630 struct cbuf cb;
632 if (eoflex())
633 fatserror("missing delta log");
634 nextlex();
635 if (!(Delta = getnum()))
636 fatserror("delta number corrupted");
637 getkeystring(Klog);
638 if (Delta->log.string)
639 fatserror("duplicate delta log");
640 bufautobegin(&logbuf);
641 cb = savestring(&logbuf);
642 Delta->log = bufremember(&logbuf, cb.size);
644 ignorephrases(Ktext);
645 getkeystring(Ktext);
646 Delta->insertlns = Delta->deletelns = 0;
647 if ( Delta != Head)
648 getscript(Delta);
649 else
650 readstring();
651 return Delta;
655 static void
656 getscript(Delta)
657 struct hshentry * Delta;
658 /* function: read edit script of Delta and count how many lines added */
659 /* and deleted in the script */
662 int ed; /* editor command */
663 declarecache;
664 register RILE *fin;
665 register int c;
666 register long i;
667 struct diffcmd dc;
669 fin = finptr;
670 setupcache(fin);
671 initdiffcmd(&dc);
672 while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
673 if (!ed)
674 Delta->deletelns += dc.nlines;
675 else {
676 /* skip scripted lines */
677 i = dc.nlines;
678 Delta->insertlns += i;
679 cache(fin);
680 do {
681 for (;;) {
682 cacheget_(c)
683 switch (c) {
684 default:
685 continue;
686 case SDELIM:
687 cacheget_(c)
688 if (c == SDELIM)
689 continue;
690 if (--i)
691 unexpected_EOF();
692 nextc = c;
693 uncache(fin);
694 return;
695 case '\n':
696 break;
698 break;
700 ++rcsline;
701 } while (--i);
702 uncache(fin);
712 static void
713 exttree(root)
714 struct hshentry *root;
715 /* function: select revisions , starting with root */
718 struct branchhead const *newbranch;
720 if (!root) return;
722 root->selector = extractdelta(root);
723 root->log.string = 0;
724 exttree(root->next);
726 newbranch = root->branches;
727 while( newbranch ) {
728 exttree(newbranch->hsh);
729 newbranch = newbranch->nextbranch;
736 static void
737 getlocker(argv)
738 char * argv;
739 /* function : get the login names of lockers from command line */
740 /* and store in lockerlist. */
743 register char c;
744 struct rcslockers *newlocker;
745 argv--;
746 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
747 continue;
748 if ( c == '\0') {
749 lockerlist = 0;
750 return;
753 while( c != '\0' ) {
754 newlocker = talloc(struct rcslockers);
755 newlocker->lockerlink = lockerlist;
756 newlocker->login = argv;
757 lockerlist = newlocker;
758 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
759 continue;
760 *argv = '\0';
761 if ( c == '\0' ) return;
762 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
763 continue;
769 static void
770 getauthor(argv)
771 char *argv;
772 /* function: get the author's name from command line */
773 /* and store in authorlist */
776 register c;
777 struct authors * newauthor;
779 argv--;
780 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
781 continue;
782 if ( c == '\0' ) {
783 authorlist = talloc(struct authors);
784 authorlist->login = getusername(false);
785 authorlist->nextauthor = 0;
786 return;
789 while( c != '\0' ) {
790 newauthor = talloc(struct authors);
791 newauthor->nextauthor = authorlist;
792 newauthor->login = argv;
793 authorlist = newauthor;
794 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
795 continue;
796 * argv = '\0';
797 if ( c == '\0') return;
798 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
799 continue;
806 static void
807 getstate(argv)
808 char * argv;
809 /* function : get the states of revisions from command line */
810 /* and store in statelist */
813 register char c;
814 struct stateattri *newstate;
816 argv--;
817 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
818 continue;
819 if ( c == '\0'){
820 error("missing state attributes after -s options");
821 return;
824 while( c != '\0' ) {
825 newstate = talloc(struct stateattri);
826 newstate->nextstate = statelist;
827 newstate->status = argv;
828 statelist = newstate;
829 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
830 continue;
831 *argv = '\0';
832 if ( c == '\0' ) return;
833 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
834 continue;
840 static void
841 trunclocks()
842 /* Function: Truncate the list of locks to those that are held by the */
843 /* id's on lockerlist. Do not truncate if lockerlist empty. */
846 struct rcslockers const *plocker;
847 struct rcslock *p, **pp;
849 if (!lockerlist) return;
851 /* shorten Locks to those contained in lockerlist */
852 for (pp = &Locks; (p = *pp); )
853 for (plocker = lockerlist; ; )
854 if (strcmp(plocker->login, p->login) == 0) {
855 pp = &p->nextlock;
856 break;
857 } else if (!(plocker = plocker->lockerlink)) {
858 *pp = p->nextlock;
859 break;
865 static void
866 recentdate(root, pd)
867 struct hshentry const *root;
868 struct Datepairs *pd;
869 /* function: Finds the delta that is closest to the cutoff date given by */
870 /* pd among the revisions selected by exttree. */
871 /* Successively narrows down the interval given by pd, */
872 /* and sets the strtdate of pd to the date of the selected delta */
874 struct branchhead const *newbranch;
876 if (!root) return;
877 if (root->selector) {
878 if ( cmpdate(root->date, pd->strtdate) >= 0 &&
879 cmpdate(root->date, pd->enddate) <= 0)
880 VOID strcpy(pd->strtdate, root->date);
883 recentdate(root->next, pd);
884 newbranch = root->branches;
885 while( newbranch) {
886 recentdate(newbranch->hsh, pd);
887 newbranch = newbranch->nextbranch;
896 static int
897 extdate(root)
898 struct hshentry * root;
899 /* function: select revisions which are in the date range specified */
900 /* in duelst and datelist, start at root */
901 /* Yield number of revisions selected, including those already selected. */
903 struct branchhead const *newbranch;
904 struct Datepairs const *pdate;
905 int revno, ne;
907 if (!root)
908 return 0;
910 if ( datelist || duelst) {
911 pdate = datelist;
912 while( pdate ) {
913 ne = pdate->ne_date;
914 if (
915 (!pdate->strtdate[0]
916 || ne <= cmpdate(root->date, pdate->strtdate))
918 (!pdate->enddate[0]
919 || ne <= cmpdate(pdate->enddate, root->date))
921 break;
922 pdate = pdate->dnext;
924 if (!pdate) {
925 pdate = duelst;
926 for (;;) {
927 if (!pdate) {
928 root->selector = false;
929 break;
931 if (cmpdate(root->date, pdate->strtdate) == 0)
932 break;
933 pdate = pdate->dnext;
937 revno = root->selector + extdate(root->next);
939 newbranch = root->branches;
940 while( newbranch ) {
941 revno += extdate(newbranch->hsh);
942 newbranch = newbranch->nextbranch;
944 return revno;
949 static char
950 extractdelta(pdelta)
951 struct hshentry const *pdelta;
952 /* function: compare information of pdelta to the authorlist, lockerlist,*/
953 /* statelist, revlist and yield true if pdelta is selected. */
956 struct rcslock const *plock;
957 struct stateattri const *pstate;
958 struct authors const *pauthor;
959 struct Revpairs const *prevision;
960 int length;
962 if ((pauthor = authorlist)) /* only certain authors wanted */
963 while (strcmp(pauthor->login, pdelta->author) != 0)
964 if (!(pauthor = pauthor->nextauthor))
965 return false;
966 if ((pstate = statelist)) /* only certain states wanted */
967 while (strcmp(pstate->status, pdelta->state) != 0)
968 if (!(pstate = pstate->nextstate))
969 return false;
970 if (lockflag) /* only locked revisions wanted */
971 for (plock = Locks; ; plock = plock->nextlock)
972 if (!plock)
973 return false;
974 else if (plock->delta == pdelta)
975 break;
976 if ((prevision = Revlst)) /* only certain revs or branches wanted */
977 for (;;) {
978 length = prevision->numfld;
979 if (
980 countnumflds(pdelta->num) == length+(length&1) &&
981 0 <= compartial(pdelta->num, prevision->strtrev, length) &&
982 0 <= compartial(prevision->endrev, pdelta->num, length)
984 break;
985 if (!(prevision = prevision->rnext))
986 return false;
988 return true;
993 static void
994 getdatepair(argv)
995 char * argv;
996 /* function: get time range from command line and store in datelist if */
997 /* a time range specified or in duelst if a time spot specified */
1000 register char c;
1001 struct Datepairs * nextdate;
1002 char const * rawdate;
1003 int switchflag;
1005 argv--;
1006 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
1007 continue;
1008 if ( c == '\0' ) {
1009 error("missing date/time after -d");
1010 return;
1013 while( c != '\0' ) {
1014 switchflag = false;
1015 nextdate = talloc(struct Datepairs);
1016 if ( c == '<' ) { /* case: -d <date */
1017 c = *++argv;
1018 if (!(nextdate->ne_date = c!='='))
1019 c = *++argv;
1020 (nextdate->strtdate)[0] = '\0';
1021 } else if (c == '>') { /* case: -d'>date' */
1022 c = *++argv;
1023 if (!(nextdate->ne_date = c!='='))
1024 c = *++argv;
1025 (nextdate->enddate)[0] = '\0';
1026 switchflag = true;
1027 } else {
1028 rawdate = argv;
1029 while( c != '<' && c != '>' && c != ';' && c != '\0')
1030 c = *++argv;
1031 *argv = '\0';
1032 if ( c == '>' ) switchflag=true;
1033 str2date(rawdate,
1034 switchflag ? nextdate->enddate : nextdate->strtdate);
1035 if ( c == ';' || c == '\0') { /* case: -d date */
1036 VOID strcpy(nextdate->enddate,nextdate->strtdate);
1037 nextdate->dnext = duelst;
1038 duelst = nextdate;
1039 goto end;
1040 } else {
1041 /* case: -d date< or -d date>; see switchflag */
1042 int eq = argv[1]=='=';
1043 nextdate->ne_date = !eq;
1044 argv += eq;
1045 while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
1046 continue;
1047 if ( c == ';' || c == '\0') {
1048 /* second date missing */
1049 if (switchflag)
1050 *nextdate->strtdate= '\0';
1051 else
1052 *nextdate->enddate= '\0';
1053 nextdate->dnext = datelist;
1054 datelist = nextdate;
1055 goto end;
1059 rawdate = argv;
1060 while( c != '>' && c != '<' && c != ';' && c != '\0')
1061 c = *++argv;
1062 *argv = '\0';
1063 str2date(rawdate,
1064 switchflag ? nextdate->strtdate : nextdate->enddate);
1065 nextdate->dnext = datelist;
1066 datelist = nextdate;
1067 end:
1068 if (RCSversion < VERSION(5))
1069 nextdate->ne_date = 0;
1070 if ( c == '\0') return;
1071 while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
1072 continue;
1078 static int
1079 getnumericrev()
1080 /* function: get the numeric name of revisions which stored in revlist */
1081 /* and then stored the numeric names in Revlst */
1082 /* if branchflag, also add default branch */
1085 struct Revpairs * ptr, *pt;
1086 int n;
1087 struct buf s, e;
1088 char const *lrev;
1089 struct buf const *rstart, *rend;
1091 Revlst = 0;
1092 ptr = revlist;
1093 bufautobegin(&s);
1094 bufautobegin(&e);
1095 while( ptr ) {
1096 n = 0;
1097 rstart = &s;
1098 rend = &e;
1100 switch (ptr->numfld) {
1102 case 1: /* -rREV */
1103 if (!expandsym(ptr->strtrev, &s))
1104 goto freebufs;
1105 rend = &s;
1106 n = countnumflds(s.string);
1107 if (!n && (lrev = tiprev())) {
1108 bufscpy(&s, lrev);
1109 n = countnumflds(lrev);
1111 break;
1113 case 2: /* -rREV: */
1114 if (!expandsym(ptr->strtrev, &s))
1115 goto freebufs;
1116 bufscpy(&e, s.string);
1117 n = countnumflds(s.string);
1118 (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0;
1119 break;
1121 case 3: /* -r:REV */
1122 if (!expandsym(ptr->endrev, &e))
1123 goto freebufs;
1124 if ((n = countnumflds(e.string)) < 2)
1125 bufscpy(&s, ".0");
1126 else {
1127 bufscpy(&s, e.string);
1128 VOID strcpy(strrchr(s.string,'.'), ".0");
1130 break;
1132 default: /* -rREV1:REV2 */
1133 if (!(
1134 expandsym(ptr->strtrev, &s)
1135 && expandsym(ptr->endrev, &e)
1136 && checkrevpair(s.string, e.string)
1138 goto freebufs;
1139 n = countnumflds(s.string);
1140 /* Swap if out of order. */
1141 if (compartial(s.string,e.string,n) > 0) {
1142 rstart = &e;
1143 rend = &s;
1145 break;
1148 if (n) {
1149 pt = ftalloc(struct Revpairs);
1150 pt->numfld = n;
1151 pt->strtrev = fstr_save(rstart->string);
1152 pt->endrev = fstr_save(rend->string);
1153 pt->rnext = Revlst;
1154 Revlst = pt;
1156 ptr = ptr->rnext;
1158 /* Now take care of branchflag */
1159 if (branchflag && (Dbranch||Head)) {
1160 pt = ftalloc(struct Revpairs);
1161 pt->strtrev = pt->endrev =
1162 Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
1163 pt->rnext=Revlst; Revlst=pt;
1164 pt->numfld = countnumflds(pt->strtrev);
1167 freebufs:
1168 bufautoend(&s);
1169 bufautoend(&e);
1170 return !ptr;
1175 static int
1176 checkrevpair(num1,num2)
1177 char const *num1, *num2;
1178 /* function: check whether num1, num2 are legal pair,i.e.
1179 only the last field are different and have same number of
1180 fields( if length <= 2, may be different if first field) */
1183 int length = countnumflds(num1);
1185 if (
1186 countnumflds(num2) != length
1187 || (2 < length && compartial(num1, num2, length-1) != 0)
1189 rcserror("invalid branch or revision pair %s : %s", num1, num2);
1190 return false;
1193 return true;
1198 static void
1199 getrevpairs(argv)
1200 register char * argv;
1201 /* function: get revision or branch range from command line, and */
1202 /* store in revlist */
1205 register char c;
1206 struct Revpairs * nextrevpair;
1207 int separator;
1209 c = *argv;
1211 /* Support old ambiguous '-' syntax; this will go away. */
1212 if (strchr(argv,':'))
1213 separator = ':';
1214 else {
1215 if (strchr(argv,'-') && VERSION(5) <= RCSversion)
1216 warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
1217 separator = '-';
1220 for (;;) {
1221 while (c==' ' || c=='\t' || c=='\n')
1222 c = *++argv;
1223 nextrevpair = talloc(struct Revpairs);
1224 nextrevpair->rnext = revlist;
1225 revlist = nextrevpair;
1226 nextrevpair->numfld = 1;
1227 nextrevpair->strtrev = argv;
1228 for (;; c = *++argv) {
1229 switch (c) {
1230 default:
1231 continue;
1232 case '\0': case ' ': case '\t': case '\n':
1233 case ',': case ';':
1234 break;
1235 case ':': case '-':
1236 if (c == separator)
1237 break;
1238 continue;
1240 break;
1242 *argv = '\0';
1243 while (c==' ' || c=='\t' || c=='\n')
1244 c = *++argv;
1245 if (c == separator) {
1246 while ((c = *++argv) == ' ' || c == '\t' || c =='\n')
1247 continue;
1248 nextrevpair->endrev = argv;
1249 for (;; c = *++argv) {
1250 switch (c) {
1251 default:
1252 continue;
1253 case '\0': case ' ': case '\t': case '\n':
1254 case ',': case ';':
1255 break;
1256 case ':': case '-':
1257 if (c == separator)
1258 break;
1259 continue;
1261 break;
1263 *argv = '\0';
1264 while (c==' ' || c=='\t' || c =='\n')
1265 c = *++argv;
1266 nextrevpair->numfld =
1267 !nextrevpair->endrev[0] ? 2 /* -rREV: */ :
1268 !nextrevpair->strtrev[0] ? 3 /* -r:REV */ :
1269 4 /* -rREV1:REV2 */;
1271 if (!c)
1272 break;
1273 else if (c==',' || c==';')
1274 c = *++argv;
1275 else
1276 error("missing `,' near `%c%s'", c, argv+1);