xref: /dragonfly/gnu/usr.bin/rcs/rlog/rlog.c (revision ac5927cd1bf74782d0a8f65614d789d15e7d8bc1)
1 /* Print log messages and other information about RCS files.  */
2 
3 /* Copyright 1982, 1988, 1989 Walter Tichy
4    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Distributed under license by the Free Software Foundation, Inc.
6 
7 This file is part of RCS.
8 
9 RCS is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RCS; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 Report problems and direct all questions to:
25 
26     rcs-bugs@cs.purdue.edu
27 
28 */
29 
30 /*
31  * $FreeBSD: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.13 1999/08/27 23:36:59 peter Exp $
32  * $DragonFly: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.3 2007/01/17 17:56:24 y0netan1 Exp $
33  *
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 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 mainProg(rlogId, "rlog", "$DragonFly: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.3 2007/01/17 17:56:24 y0netan1 Exp $")
225 {
226           static char const cmdusage[] =
227                     "\nrlog usage: rlog -{bhLNRt} -v[string] -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ...";
228 
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;
244           int versionlist;
245           char *vstring;
246 
247         descflag = selectflag = shownames = true;
248           versionlist = onlylockflag = onlyRCSflag = false;
249           vstring=0;
250           out = stdout;
251           suffixes = X_DEFAULT;
252 
253           argc = getRCSINIT(argc, argv, &newargv);
254           argv = newargv;
255           while (a = *++argv,  0<--argc && *a++=='-') {
256                     switch (*a++) {
257 
258                     case 'L':
259                               onlylockflag = true;
260                               break;
261 
262                     case 'N':
263                               shownames = false;
264                               break;
265 
266                     case 'R':
267                               onlyRCSflag =true;
268                               break;
269 
270                 case 'l':
271                         lockflag = true;
272                               getlocker(a);
273                         break;
274 
275                 case 'b':
276                         branchflag = true;
277                         break;
278 
279                 case 'r':
280                               getrevpairs(a);
281                         break;
282 
283                 case 'd':
284                               getdatepair(a);
285                         break;
286 
287                 case 's':
288                               getstate(a);
289                         break;
290 
291                 case 'w':
292                               getauthor(a);
293                         break;
294 
295                 case 'h':
296                               descflag = false;
297                         break;
298 
299                 case 't':
300                         selectflag = false;
301                         break;
302 
303                     case 'q':
304                               /* This has no effect; it's here for consistency.  */
305                               quietflag = true;
306                               break;
307 
308                     case 'x':
309                               suffixes = a;
310                               break;
311 
312                     case 'z':
313                               zone_set(a);
314                               break;
315 
316                     case 'T':
317                               /* Ignore -T, so that RCSINIT can contain -T.  */
318                               if (*a)
319                                         goto unknown;
320                               break;
321 
322                     case 'V':
323                               setRCSversion(*argv);
324                               break;
325 
326                     case 'v':
327                               versionlist = true;
328                               vstring = a;
329                               break;
330 
331                 default:
332                     unknown:
333                               error("unknown option: %s%s", *argv, cmdusage);
334 
335                 };
336         } /* end of option processing */
337 
338           if (! (descflag|selectflag)) {
339                     warn("-t overrides -h.");
340                     descflag = true;
341           }
342 
343           pre5 = RCSversion < VERSION(5);
344           if (pre5) {
345               accessListString = "\naccess list:   ";
346               accessFormat = "  %s";
347               headFormat = "RCS file:        %s;   Working file:    %s\nhead:           %s%s\nbranch:         %s%s\nlocks:         ";
348               insDelFormat = "  lines added/del: %ld/%ld";
349               symbolFormat = "  %s: %s;";
350           } else {
351               accessListString = "\naccess list:";
352               accessFormat = "\n\t%s";
353               headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s";
354               insDelFormat = "  lines: +%ld -%ld";
355               symbolFormat = "\n\t%s: %s";
356           }
357 
358           /* Now handle all pathnames.  */
359           if (nerror)
360             cleanup();
361           else if (argc < 1)
362             faterror("no input file%s", cmdusage);
363           else
364             for (;  0 < argc;  cleanup(), ++argv, --argc) {
365               ffree();
366 
367               if (pairnames(argc, argv, rcsreadopen, true, false)  <=  0)
368                     continue;
369 
370               /*
371                * RCSname contains the name of the RCS file,
372                * and finptr the file descriptor;
373                * workname contains the name of the working file.
374              */
375 
376               /* Keep only those locks given by -l.  */
377               if (lockflag)
378                     trunclocks();
379 
380             /* do nothing if -L is given and there are no locks*/
381               if (onlylockflag && !Locks)
382                     continue;
383 
384               if ( versionlist ) {
385                     gettree();
386                     aprintf(out, "%s%s %s\n", vstring, workname, tiprev());
387                     continue;
388               }
389 
390               if ( onlyRCSflag ) {
391                     aprintf(out, "%s\n", RCSname);
392                     continue;
393               }
394 
395               gettree();
396 
397               if (!getnumericrev())
398                     continue;
399 
400               /*
401               * Output the first character with putc, not printf.
402               * Otherwise, an SVR4 stdio bug buffers output inefficiently.
403               */
404               aputc_('\n', out)
405 
406               /*   print RCS pathname, working pathname and optional
407                  administrative information                         */
408             /* could use getfullRCSname() here, but that is very slow */
409               aprintf(out, headFormat, RCSname, workname,
410                         Head ? " " : "",  Head ? Head->num : "",
411                         Dbranch ? " " : "",  Dbranch ? Dbranch : "",
412                         StrictLocks ? " strict" : ""
413               );
414             currlock = Locks;
415             while( currlock ) {
416                     aprintf(out, symbolFormat, currlock->login,
417                                 currlock->delta->num);
418                 currlock = currlock->nextlock;
419             }
420             if (StrictLocks && pre5)
421                 aputs("  ;  strict" + (Locks?3:0), out);
422 
423               aputs(accessListString, out);      /*  print access list  */
424             curaccess = AccessList;
425             while(curaccess) {
426                     aprintf(out, accessFormat, curaccess->login);
427                 curaccess = curaccess->nextaccess;
428             }
429 
430               if (shownames) {
431                     aputs("\nsymbolic names:", out);   /*  print symbolic names   */
432                     for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc)
433                         aprintf(out, symbolFormat, curassoc->symbol, curassoc->num);
434               }
435               if (pre5) {
436                     aputs("\ncomment leader:  \"", out);
437                     awrite(Comment.string, Comment.size, out);
438                     afputc('\"', out);
439               }
440               if (!pre5  ||  Expand != KEYVAL_EXPAND)
441                     aprintf(out, "\nkeyword substitution: %s",
442                               expand_names[Expand]
443                     );
444 
445               aprintf(out, "\ntotal revisions: %d", TotalDeltas);
446 
447               revno = 0;
448 
449               if (Head  &&  selectflag & descflag) {
450 
451                     exttree(Head);
452 
453                     /*  get most recently date of the dates pointed by duelst  */
454                     currdate = duelst;
455                     while( currdate) {
456                         VOID strcpy(currdate->strtdate, "0.0.0.0.0.0");
457                         recentdate(Head, currdate);
458                         currdate = currdate->dnext;
459                     }
460 
461                     revno = extdate(Head);
462 
463                     aprintf(out, ";\tselected revisions: %d", revno);
464               }
465 
466               afputc('\n',out);
467               if (descflag) {
468                     aputs("description:\n", out);
469                     getdesc(true);
470               }
471               if (revno) {
472                     while (! (delta = readdeltalog())->selector  ||  --revno)
473                         continue;
474                     if (delta->next && countnumflds(delta->num)==2)
475                         /* Read through delta->next to get its insertlns.  */
476                         while (readdeltalog() != delta->next)
477                               continue;
478                     putrunk();
479                     putree(Head);
480               }
481               aputs("----------------------------\n", out);
482               aputs("=============================================================================\n",out);
483             }
484           Ofclose(out);
485           exitmain(exitstatus);
486 }
487 
488           static void
cleanup()489 cleanup()
490 {
491           if (nerror) exitstatus = EXIT_FAILURE;
492           Izclose(&finptr);
493 }
494 
495 #if RCS_lint
496 #         define exiterr rlogExit
497 #endif
498           void
exiterr()499 exiterr()
500 {
501           _exit(EXIT_FAILURE);
502 }
503 
504 
505 
506           static void
putrunk()507 putrunk()
508 /*  function:  print revisions chosen, which are in trunk      */
509 
510 {
511           register struct hshentry const *ptr;
512 
513           for (ptr = Head;  ptr;  ptr = ptr->next)
514                     putadelta(ptr, ptr->next, true);
515 }
516 
517 
518 
519           static void
putree(root)520 putree(root)
521           struct hshentry const *root;
522 /*   function: print delta tree (not including trunk) in reverse
523                order on each branch                                        */
524 
525 {
526           if (!root) return;
527 
528         putree(root->next);
529 
530         putforest(root->branches);
531 }
532 
533 
534 
535 
536           static void
putforest(branchroot)537 putforest(branchroot)
538           struct branchhead const *branchroot;
539 /*   function:  print branches that has the same direct ancestor    */
540 {
541           if (!branchroot) return;
542 
543         putforest(branchroot->nextbranch);
544 
545         putabranch(branchroot->hsh);
546         putree(branchroot->hsh);
547 }
548 
549 
550 
551 
552           static void
putabranch(root)553 putabranch(root)
554           struct hshentry const *root;
555 /*   function  :  print one branch     */
556 
557 {
558           if (!root) return;
559 
560         putabranch(root->next);
561 
562         putadelta(root, root, false);
563 }
564 
565 
566 
567 
568 
569           static void
putadelta(node,editscript,trunk)570 putadelta(node,editscript,trunk)
571           register struct hshentry const *node, *editscript;
572           int trunk;
573 /*  function: Print delta node if node->selector is set.        */
574 /*      editscript indicates where the editscript is stored     */
575 /*      trunk indicated whether this node is in trunk           */
576 {
577           static char emptych[] = EMPTYLOG;
578 
579           register FILE *out;
580           char const *s;
581           size_t n;
582           struct branchhead const *newbranch;
583           struct buf branchnum;
584           char datebuf[datesize + zonelenmax];
585           int pre5 = RCSversion < VERSION(5);
586 
587           if (!node->selector)
588             return;
589 
590           out = stdout;
591           aprintf(out,
592                     "----------------------------\nrevision %s%s",
593                     node->num,  pre5 ? "        " : ""
594           );
595         if ( node->lockedby )
596               aprintf(out, pre5+"\tlocked by: %s;", node->lockedby);
597 
598           aprintf(out, "\ndate: %s;  author: %s;  state: %s;",
599                     date2str(node->date, datebuf),
600                     node->author, node->state
601           );
602 
603         if ( editscript ) {
604            if(trunk)
605                 aprintf(out, insDelFormat,
606                              editscript->deletelns, editscript->insertlns);
607            else
608                 aprintf(out, insDelFormat,
609                              editscript->insertlns, editscript->deletelns);
610           }
611 
612           if ( node->commitid )
613              aprintf(out, "%s commitid: %s", (editscript) ? ";" : "",
614                        node->commitid);
615 
616         newbranch = node->branches;
617         if ( newbranch ) {
618              bufautobegin(&branchnum);
619              aputs("\nbranches:", out);
620            while( newbranch ) {
621                     getbranchno(newbranch->hsh->num, &branchnum);
622                     aprintf(out, "  %s;", branchnum.string);
623                 newbranch = newbranch->nextbranch;
624            }
625              bufautoend(&branchnum);
626         }
627 
628           afputc('\n', out);
629           s = node->log.string;
630           if (!(n = node->log.size)) {
631                     s = emptych;
632                     n = sizeof(emptych)-1;
633           }
634           awrite(s, n, out);
635           if (s[n-1] != '\n')
636                     afputc('\n', out);
637 }
638 
639 
640           static struct hshentry const *
readdeltalog()641 readdeltalog()
642 /*  Function : get the log message and skip the text of a deltatext node.
643  *               Return the delta found.
644  *             Assumes the current lexeme is not yet in nexttok; does not
645  *             advance nexttok.
646  */
647 {
648         register struct  hshentry  * Delta;
649           struct buf logbuf;
650           struct cbuf cb;
651 
652           if (eoflex())
653                     fatserror("missing delta log");
654         nextlex();
655           if (!(Delta = getnum()))
656                     fatserror("delta number corrupted");
657           getkeystring(Klog);
658           if (Delta->log.string)
659                     fatserror("duplicate delta log");
660           bufautobegin(&logbuf);
661           cb = savestring(&logbuf);
662           Delta->log = bufremember(&logbuf, cb.size);
663 
664           ignorephrases(Ktext);
665           getkeystring(Ktext);
666         Delta->insertlns = Delta->deletelns = 0;
667         if ( Delta != Head)
668                 getscript(Delta);
669         else
670                 readstring();
671           return Delta;
672 }
673 
674 
675           static void
getscript(Delta)676 getscript(Delta)
677 struct    hshentry   * Delta;
678 /*   function:  read edit script of Delta and count how many lines added  */
679 /*              and deleted in the script                                 */
680 
681 {
682         int ed;   /*  editor command  */
683           declarecache;
684           register RILE *fin;
685         register  int   c;
686           register long i;
687           struct diffcmd dc;
688 
689           fin = finptr;
690           setupcache(fin);
691           initdiffcmd(&dc);
692           while (0  <=  (ed = getdiffcmd(fin,true,(FILE *)0,&dc)))
693               if (!ed)
694                  Delta->deletelns += dc.nlines;
695               else {
696                  /*  skip scripted lines  */
697                      i = dc.nlines;
698                      Delta->insertlns += i;
699                      cache(fin);
700                      do {
701                          for (;;) {
702                               cacheget_(c)
703                               switch (c) {
704                                   default:
705                                         continue;
706                                   case SDELIM:
707                                         cacheget_(c)
708                                         if (c == SDELIM)
709                                             continue;
710                                         if (--i)
711                                                   unexpected_EOF();
712                                         nextc = c;
713                                         uncache(fin);
714                                         return;
715                                   case '\n':
716                                         break;
717                               }
718                               break;
719                          }
720                          ++rcsline;
721                      } while (--i);
722                      uncache(fin);
723             }
724 }
725 
726 
727 
728 
729 
730 
731 
732           static void
exttree(root)733 exttree(root)
734 struct hshentry  *root;
735 /*  function: select revisions , starting with root             */
736 
737 {
738           struct branchhead const *newbranch;
739 
740           if (!root) return;
741 
742           root->selector = extractdelta(root);
743           root->log.string = 0;
744         exttree(root->next);
745 
746         newbranch = root->branches;
747         while( newbranch ) {
748             exttree(newbranch->hsh);
749             newbranch = newbranch->nextbranch;
750         }
751 }
752 
753 
754 
755 
756           static void
getlocker(argv)757 getlocker(argv)
758 char    * argv;
759 /*   function : get the login names of lockers from command line   */
760 /*              and store in lockerlist.                           */
761 
762 {
763         register char c;
764           struct rcslockers *newlocker;
765         argv--;
766           while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
767               continue;
768         if (  c == '\0') {
769               lockerlist = 0;
770             return;
771         }
772 
773         while( c != '\0' ) {
774               newlocker = talloc(struct rcslockers);
775             newlocker->lockerlink = lockerlist;
776             newlocker->login = argv;
777             lockerlist = newlocker;
778               while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
779                     continue;
780             *argv = '\0';
781             if ( c == '\0' ) return;
782               while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
783                     continue;
784         }
785 }
786 
787 
788 
789           static void
getauthor(argv)790 getauthor(argv)
791 char   *argv;
792 /*   function:  get the author's name from command line   */
793 /*              and store in authorlist                   */
794 
795 {
796         register int c;
797         struct     authors  * newauthor;
798 
799         argv--;
800           while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
801               continue;
802         if ( c == '\0' ) {
803               authorlist = talloc(struct authors);
804               authorlist->login = getusername(false);
805               authorlist->nextauthor = 0;
806             return;
807         }
808 
809         while( c != '\0' ) {
810               newauthor = talloc(struct authors);
811             newauthor->nextauthor = authorlist;
812             newauthor->login = argv;
813             authorlist = newauthor;
814               while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
815                     continue;
816             * argv = '\0';
817             if ( c == '\0') return;
818               while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
819                     continue;
820         }
821 }
822 
823 
824 
825 
826           static void
getstate(argv)827 getstate(argv)
828 char   * argv;
829 /*   function :  get the states of revisions from command line  */
830 /*               and store in statelist                         */
831 
832 {
833         register  char  c;
834         struct    stateattri    *newstate;
835 
836         argv--;
837           while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
838               continue;
839         if ( c == '\0'){
840               error("missing state attributes after -s options");
841             return;
842         }
843 
844         while( c != '\0' ) {
845               newstate = talloc(struct stateattri);
846             newstate->nextstate = statelist;
847             newstate->status = argv;
848             statelist = newstate;
849               while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';')
850                     continue;
851             *argv = '\0';
852             if ( c == '\0' ) return;
853               while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
854                     continue;
855         }
856 }
857 
858 
859 
860           static void
trunclocks()861 trunclocks()
862 /*  Function:  Truncate the list of locks to those that are held by the  */
863 /*             id's on lockerlist. Do not truncate if lockerlist empty.  */
864 
865 {
866           struct rcslockers const *plocker;
867           struct rcslock *p, **pp;
868 
869           if (!lockerlist) return;
870 
871         /* shorten Locks to those contained in lockerlist */
872           for (pp = &Locks;  (p = *pp);  )
873               for (plocker = lockerlist;  ;  )
874                     if (strcmp(plocker->login, p->login) == 0) {
875                         pp = &p->nextlock;
876                         break;
877                     } else if (!(plocker = plocker->lockerlink)) {
878                         *pp = p->nextlock;
879                         break;
880                     }
881 }
882 
883 
884 
885           static void
recentdate(root,pd)886 recentdate(root, pd)
887           struct hshentry const *root;
888           struct Datepairs *pd;
889 /*  function:  Finds the delta that is closest to the cutoff date given by   */
890 /*             pd among the revisions selected by exttree.                   */
891 /*             Successively narrows down the interval given by pd,           */
892 /*             and sets the strtdate of pd to the date of the selected delta */
893 {
894           struct branchhead const *newbranch;
895 
896           if (!root) return;
897           if (root->selector) {
898                if ( cmpdate(root->date, pd->strtdate) >= 0 &&
899                       cmpdate(root->date, pd->enddate) <= 0)
900                     VOID strcpy(pd->strtdate, root->date);
901         }
902 
903         recentdate(root->next, pd);
904         newbranch = root->branches;
905         while( newbranch) {
906            recentdate(newbranch->hsh, pd);
907            newbranch = newbranch->nextbranch;
908           }
909 }
910 
911 
912 
913 
914 
915 
916           static int
extdate(root)917 extdate(root)
918 struct  hshentry        * root;
919 /*  function:  select revisions which are in the date range specified     */
920 /*             in duelst  and datelist, start at root                     */
921 /* Yield number of revisions selected, including those already selected.  */
922 {
923           struct branchhead const *newbranch;
924           struct Datepairs const *pdate;
925           int revno, ne;
926 
927           if (!root)
928               return 0;
929 
930         if ( datelist || duelst) {
931             pdate = datelist;
932             while( pdate ) {
933                     ne = pdate->ne_date;
934                     if (
935                               (!pdate->strtdate[0]
936                               || ne <= cmpdate(root->date, pdate->strtdate))
937                         &&
938                               (!pdate->enddate[0]
939                               || ne <= cmpdate(pdate->enddate, root->date))
940                     )
941                         break;
942                 pdate = pdate->dnext;
943             }
944               if (!pdate) {
945                 pdate = duelst;
946                     for (;;) {
947                        if (!pdate) {
948                               root->selector = false;
949                               break;
950                        }
951                        if (cmpdate(root->date, pdate->strtdate) == 0)
952                       break;
953                    pdate = pdate->dnext;
954                 }
955             }
956         }
957           revno = root->selector + extdate(root->next);
958 
959         newbranch = root->branches;
960         while( newbranch ) {
961              revno += extdate(newbranch->hsh);
962            newbranch = newbranch->nextbranch;
963         }
964           return revno;
965 }
966 
967 
968 
969           static char
extractdelta(pdelta)970 extractdelta(pdelta)
971           struct hshentry const *pdelta;
972 /*  function:  compare information of pdelta to the authorlist, lockerlist,*/
973 /*             statelist, revlist and yield true if pdelta is selected.    */
974 
975 {
976           struct rcslock const *plock;
977           struct stateattri const *pstate;
978           struct authors const *pauthor;
979           struct Revpairs const *prevision;
980           int length;
981 
982           if ((pauthor = authorlist)) /* only certain authors wanted */
983               while (strcmp(pauthor->login, pdelta->author) != 0)
984                     if (!(pauthor = pauthor->nextauthor))
985                         return false;
986           if ((pstate = statelist)) /* only certain states wanted */
987               while (strcmp(pstate->status, pdelta->state) != 0)
988                     if (!(pstate = pstate->nextstate))
989                         return false;
990           if (lockflag) { /* only locked revisions wanted */
991               for (plock = Locks;  ;  plock = plock->nextlock) {
992                     if (!plock)
993                         return false;
994                     else if (plock->delta == pdelta)
995                         break;
996               }
997           }
998           if ((prevision = Revlst)) /* only certain revs or branches wanted */
999               for (;;) {
1000                 length = prevision->numfld;
1001                     if (
1002                         countnumflds(pdelta->num) == length+(length&1) &&
1003                         0 <= compartial(pdelta->num, prevision->strtrev, length) &&
1004                         0 <= compartial(prevision->endrev, pdelta->num, length)
1005                     )
1006                          break;
1007                     if (!(prevision = prevision->rnext))
1008                         return false;
1009             }
1010           return true;
1011 }
1012 
1013 
1014 
1015           static void
getdatepair(argv)1016 getdatepair(argv)
1017    char   * argv;
1018 /*  function:  get time range from command line and store in datelist if    */
1019 /*             a time range specified or in duelst if a time spot specified */
1020 
1021 {
1022         register   char         c;
1023         struct     Datepairs    * nextdate;
1024           char const                    * rawdate;
1025           int                     switchflag;
1026 
1027         argv--;
1028           while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
1029               continue;
1030         if ( c == '\0' ) {
1031               error("missing date/time after -d");
1032             return;
1033         }
1034 
1035         while( c != '\0' )  {
1036               switchflag = false;
1037               nextdate = talloc(struct Datepairs);
1038             if ( c == '<' ) {   /*   case: -d <date   */
1039                 c = *++argv;
1040                     if (!(nextdate->ne_date = c!='='))
1041                         c = *++argv;
1042                 (nextdate->strtdate)[0] = '\0';
1043               } else if (c == '>') { /* case: -d'>date' */
1044                     c = *++argv;
1045                     if (!(nextdate->ne_date = c!='='))
1046                         c = *++argv;
1047                     (nextdate->enddate)[0] = '\0';
1048                     switchflag = true;
1049               } else {
1050                 rawdate = argv;
1051                     while( c != '<' && c != '>' && c != ';' && c != '\0')
1052                          c = *++argv;
1053                 *argv = '\0';
1054                     if ( c == '>' ) switchflag=true;
1055                     str2date(rawdate,
1056                                switchflag ? nextdate->enddate : nextdate->strtdate);
1057                     if ( c == ';' || c == '\0') {  /*  case: -d date  */
1058                         VOID strcpy(nextdate->enddate,nextdate->strtdate);
1059                     nextdate->dnext = duelst;
1060                     duelst = nextdate;
1061                         goto end;
1062                     } else {
1063                         /*   case:   -d date<  or -d  date>; see switchflag */
1064                         int eq = argv[1]=='=';
1065                         nextdate->ne_date = !eq;
1066                         argv += eq;
1067                         while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
1068                               continue;
1069                         if ( c == ';' || c == '\0') {
1070                               /* second date missing */
1071                               if (switchflag)
1072                                   *nextdate->strtdate= '\0';
1073                               else
1074                                   *nextdate->enddate= '\0';
1075                               nextdate->dnext = datelist;
1076                               datelist = nextdate;
1077                               goto end;
1078                         }
1079                 }
1080             }
1081             rawdate = argv;
1082               while( c != '>' && c != '<' && c != ';' && c != '\0')
1083                     c = *++argv;
1084             *argv = '\0';
1085               str2date(rawdate,
1086                          switchflag ? nextdate->strtdate : nextdate->enddate);
1087             nextdate->dnext = datelist;
1088               datelist = nextdate;
1089      end:
1090               if (RCSversion < VERSION(5))
1091                     nextdate->ne_date = 0;
1092               if ( c == '\0')  return;
1093               while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
1094                     continue;
1095         }
1096 }
1097 
1098 
1099 
1100           static int
getnumericrev()1101 getnumericrev()
1102 /*  function:  get the numeric name of revisions which stored in revlist  */
1103 /*             and then stored the numeric names in Revlst                */
1104 /*             if branchflag, also add default branch                     */
1105 
1106 {
1107         struct  Revpairs        * ptr, *pt;
1108           int n;
1109           struct buf s, e;
1110           char const *lrev;
1111           struct buf const *rstart, *rend;
1112 
1113           Revlst = 0;
1114         ptr = revlist;
1115           bufautobegin(&s);
1116           bufautobegin(&e);
1117         while( ptr ) {
1118               n = 0;
1119               rstart = &s;
1120               rend = &e;
1121 
1122               switch (ptr->numfld) {
1123 
1124                 case 1: /* -rREV */
1125                     if (!expandsym(ptr->strtrev, &s))
1126                         goto freebufs;
1127                     rend = &s;
1128                     n = countnumflds(s.string);
1129                     if (!n  &&  (lrev = tiprev())) {
1130                         bufscpy(&s, lrev);
1131                         n = countnumflds(lrev);
1132                     }
1133                     break;
1134 
1135                 case 2: /* -rREV: */
1136                     if (!expandsym(ptr->strtrev, &s))
1137                         goto freebufs;
1138                     bufscpy(&e, s.string);
1139                     n = countnumflds(s.string);
1140                     (n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
1141                     break;
1142 
1143                 case 3: /* -r:REV */
1144                     if (!expandsym(ptr->endrev, &e))
1145                         goto freebufs;
1146                     if ((n = countnumflds(e.string)) < 2)
1147                         bufscpy(&s, ".0");
1148                     else {
1149                         bufscpy(&s, e.string);
1150                         VOID strcpy(strrchr(s.string,'.'), ".0");
1151                     }
1152                     break;
1153 
1154                 default: /* -rREV1:REV2 */
1155                     if (!(
1156                               expandsym(ptr->strtrev, &s)
1157                         &&    expandsym(ptr->endrev, &e)
1158                         &&    checkrevpair(s.string, e.string)
1159                     ))
1160                         goto freebufs;
1161                     n = countnumflds(s.string);
1162                     /* Swap if out of order.  */
1163                     if (compartial(s.string,e.string,n) > 0) {
1164                         rstart = &e;
1165                         rend = &s;
1166                     }
1167                     break;
1168               }
1169 
1170               if (n) {
1171                     pt = ftalloc(struct Revpairs);
1172                     pt->numfld = n;
1173                     pt->strtrev = fstr_save(rstart->string);
1174                     pt->endrev = fstr_save(rend->string);
1175                 pt->rnext = Revlst;
1176                 Revlst = pt;
1177               }
1178               ptr = ptr->rnext;
1179         }
1180         /* Now take care of branchflag */
1181           if (branchflag && (Dbranch||Head)) {
1182               pt = ftalloc(struct Revpairs);
1183               pt->strtrev = pt->endrev =
1184                     Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
1185               pt->rnext=Revlst; Revlst=pt;
1186               pt->numfld = countnumflds(pt->strtrev);
1187         }
1188 
1189       freebufs:
1190           bufautoend(&s);
1191           bufautoend(&e);
1192           return !ptr;
1193 }
1194 
1195 
1196 
1197           static int
checkrevpair(num1,num2)1198 checkrevpair(num1,num2)
1199           char const *num1, *num2;
1200 /*  function:  check whether num1, num2 are legal pair,i.e.
1201     only the last field are different and have same number of
1202     fields( if length <= 2, may be different if first field)   */
1203 
1204 {
1205           int length = countnumflds(num1);
1206 
1207           if (
1208                               countnumflds(num2) != length
1209                     ||        (2 < length  &&  compartial(num1, num2, length-1) != 0)
1210           ) {
1211               rcserror("invalid branch or revision pair %s : %s", num1, num2);
1212             return false;
1213         }
1214 
1215         return true;
1216 }
1217 
1218 
1219 
1220           static void
getrevpairs(argv)1221 getrevpairs(argv)
1222 register     char    * argv;
1223 /*  function:  get revision or branch range from command line, and   */
1224 /*             store in revlist                                      */
1225 
1226 {
1227         register    char    c;
1228         struct      Revpairs  * nextrevpair;
1229           int separator;
1230 
1231           c = *argv;
1232 
1233           /* Support old ambiguous '-' syntax; this will go away.  */
1234           if (strchr(argv,':'))
1235               separator = ':';
1236           else {
1237               if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
1238                     warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
1239               separator = '-';
1240           }
1241 
1242           for (;;) {
1243               while (c==' ' || c=='\t' || c=='\n')
1244                     c = *++argv;
1245               nextrevpair = talloc(struct Revpairs);
1246             nextrevpair->rnext = revlist;
1247             revlist = nextrevpair;
1248               nextrevpair->numfld = 1;
1249               nextrevpair->strtrev = argv;
1250               for (;;  c = *++argv) {
1251                     switch (c) {
1252                         default:
1253                               continue;
1254                         case '\0': case ' ': case '\t': case '\n':
1255                         case ',': case ';':
1256                               break;
1257                         case ':': case '-':
1258                               if (c == separator)
1259                                   break;
1260                               continue;
1261                     }
1262                     break;
1263               }
1264               *argv = '\0';
1265               while (c==' ' || c=='\t' || c=='\n')
1266                     c = *++argv;
1267               if (c == separator) {
1268                     while ((c = *++argv) == ' ' || c == '\t' || c =='\n')
1269                         continue;
1270                     nextrevpair->endrev = argv;
1271                     for (;;  c = *++argv) {
1272                         switch (c) {
1273                               default:
1274                                   continue;
1275                               case '\0': case ' ': case '\t': case '\n':
1276                               case ',': case ';':
1277                                   break;
1278                               case ':': case '-':
1279                                   if (c == separator)
1280                                         break;
1281                                   continue;
1282                         }
1283                         break;
1284                     }
1285                     *argv = '\0';
1286                     while (c==' ' || c=='\t' || c =='\n')
1287                         c = *++argv;
1288                     nextrevpair->numfld =
1289                         !nextrevpair->endrev[0] ? 2 /* -rREV: */ :
1290                         !nextrevpair->strtrev[0] ? 3 /* -r:REV */ :
1291                         4 /* -rREV1:REV2 */;
1292             }
1293               if (!c)
1294                     break;
1295               else if (c==',' || c==';')
1296                     c = *++argv;
1297               else
1298                     error("missing `,' near `%c%s'", c, argv+1);
1299           }
1300 }
1301