Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / gnu / usr.bin / rcs / rlog / rlog.c
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
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
499 exiterr()
500 {
501         _exit(EXIT_FAILURE);
502 }
503
504
505
506         static void
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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         if ((prevision = Revlst)) /* only certain revs or branches wanted */
997             for (;;) {
998                 length = prevision->numfld;
999                 if (
1000                     countnumflds(pdelta->num) == length+(length&1) &&
1001                     0 <= compartial(pdelta->num, prevision->strtrev, length) &&
1002                     0 <= compartial(prevision->endrev, pdelta->num, length)
1003                 )
1004                      break;
1005                 if (!(prevision = prevision->rnext))
1006                     return false;
1007             }
1008         return true;
1009 }
1010
1011
1012
1013         static void
1014 getdatepair(argv)
1015    char   * argv;
1016 /*  function:  get time range from command line and store in datelist if    */
1017 /*             a time range specified or in duelst if a time spot specified */
1018
1019 {
1020         register   char         c;
1021         struct     Datepairs    * nextdate;
1022         char const              * rawdate;
1023         int                     switchflag;
1024
1025         argv--;
1026         while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';')
1027             continue;
1028         if ( c == '\0' ) {
1029             error("missing date/time after -d");
1030             return;
1031         }
1032
1033         while( c != '\0' )  {
1034             switchflag = false;
1035             nextdate = talloc(struct Datepairs);
1036             if ( c == '<' ) {   /*   case: -d <date   */
1037                 c = *++argv;
1038                 if (!(nextdate->ne_date = c!='='))
1039                     c = *++argv;
1040                 (nextdate->strtdate)[0] = '\0';
1041             } else if (c == '>') { /* case: -d'>date' */
1042                 c = *++argv;
1043                 if (!(nextdate->ne_date = c!='='))
1044                     c = *++argv;
1045                 (nextdate->enddate)[0] = '\0';
1046                 switchflag = true;
1047             } else {
1048                 rawdate = argv;
1049                 while( c != '<' && c != '>' && c != ';' && c != '\0')
1050                      c = *++argv;
1051                 *argv = '\0';
1052                 if ( c == '>' ) switchflag=true;
1053                 str2date(rawdate,
1054                          switchflag ? nextdate->enddate : nextdate->strtdate);
1055                 if ( c == ';' || c == '\0') {  /*  case: -d date  */
1056                     VOID strcpy(nextdate->enddate,nextdate->strtdate);
1057                     nextdate->dnext = duelst;
1058                     duelst = nextdate;
1059                     goto end;
1060                 } else {
1061                     /*   case:   -d date<  or -d  date>; see switchflag */
1062                     int eq = argv[1]=='=';
1063                     nextdate->ne_date = !eq;
1064                     argv += eq;
1065                     while ((c = *++argv) == ' ' || c=='\t' || c=='\n')
1066                         continue;
1067                     if ( c == ';' || c == '\0') {
1068                         /* second date missing */
1069                         if (switchflag)
1070                             *nextdate->strtdate= '\0';
1071                         else
1072                             *nextdate->enddate= '\0';
1073                         nextdate->dnext = datelist;
1074                         datelist = nextdate;
1075                         goto end;
1076                     }
1077                 }
1078             }
1079             rawdate = argv;
1080             while( c != '>' && c != '<' && c != ';' && c != '\0')
1081                 c = *++argv;
1082             *argv = '\0';
1083             str2date(rawdate,
1084                      switchflag ? nextdate->strtdate : nextdate->enddate);
1085             nextdate->dnext = datelist;
1086             datelist = nextdate;
1087      end:
1088             if (RCSversion < VERSION(5))
1089                 nextdate->ne_date = 0;
1090             if ( c == '\0')  return;
1091             while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n')
1092                 continue;
1093         }
1094 }
1095
1096
1097
1098         static int
1099 getnumericrev()
1100 /*  function:  get the numeric name of revisions which stored in revlist  */
1101 /*             and then stored the numeric names in Revlst                */
1102 /*             if branchflag, also add default branch                     */
1103
1104 {
1105         struct  Revpairs        * ptr, *pt;
1106         int n;
1107         struct buf s, e;
1108         char const *lrev;
1109         struct buf const *rstart, *rend;
1110
1111         Revlst = 0;
1112         ptr = revlist;
1113         bufautobegin(&s);
1114         bufautobegin(&e);
1115         while( ptr ) {
1116             n = 0;
1117             rstart = &s;
1118             rend = &e;
1119
1120             switch (ptr->numfld) {
1121
1122               case 1: /* -rREV */
1123                 if (!expandsym(ptr->strtrev, &s))
1124                     goto freebufs;
1125                 rend = &s;
1126                 n = countnumflds(s.string);
1127                 if (!n  &&  (lrev = tiprev())) {
1128                     bufscpy(&s, lrev);
1129                     n = countnumflds(lrev);
1130                 }
1131                 break;
1132
1133               case 2: /* -rREV: */
1134                 if (!expandsym(ptr->strtrev, &s))
1135                     goto freebufs;
1136                 bufscpy(&e, s.string);
1137                 n = countnumflds(s.string);
1138                 (n<2 ? e.string : strrchr(e.string,'.'))[0]  =  0;
1139                 break;
1140
1141               case 3: /* -r:REV */
1142                 if (!expandsym(ptr->endrev, &e))
1143                     goto freebufs;
1144                 if ((n = countnumflds(e.string)) < 2)
1145                     bufscpy(&s, ".0");
1146                 else {
1147                     bufscpy(&s, e.string);
1148                     VOID strcpy(strrchr(s.string,'.'), ".0");
1149                 }
1150                 break;
1151
1152               default: /* -rREV1:REV2 */
1153                 if (!(
1154                         expandsym(ptr->strtrev, &s)
1155                     &&  expandsym(ptr->endrev, &e)
1156                     &&  checkrevpair(s.string, e.string)
1157                 ))
1158                     goto freebufs;
1159                 n = countnumflds(s.string);
1160                 /* Swap if out of order.  */
1161                 if (compartial(s.string,e.string,n) > 0) {
1162                     rstart = &e;
1163                     rend = &s;
1164                 }
1165                 break;
1166             }
1167
1168             if (n) {
1169                 pt = ftalloc(struct Revpairs);
1170                 pt->numfld = n;
1171                 pt->strtrev = fstr_save(rstart->string);
1172                 pt->endrev = fstr_save(rend->string);
1173                 pt->rnext = Revlst;
1174                 Revlst = pt;
1175             }
1176             ptr = ptr->rnext;
1177         }
1178         /* Now take care of branchflag */
1179         if (branchflag && (Dbranch||Head)) {
1180             pt = ftalloc(struct Revpairs);
1181             pt->strtrev = pt->endrev =
1182                 Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1));
1183             pt->rnext=Revlst; Revlst=pt;
1184             pt->numfld = countnumflds(pt->strtrev);
1185         }
1186
1187       freebufs:
1188         bufautoend(&s);
1189         bufautoend(&e);
1190         return !ptr;
1191 }
1192
1193
1194
1195         static int
1196 checkrevpair(num1,num2)
1197         char const *num1, *num2;
1198 /*  function:  check whether num1, num2 are legal pair,i.e.
1199     only the last field are different and have same number of
1200     fields( if length <= 2, may be different if first field)   */
1201
1202 {
1203         int length = countnumflds(num1);
1204
1205         if (
1206                         countnumflds(num2) != length
1207                 ||      (2 < length  &&  compartial(num1, num2, length-1) != 0)
1208         ) {
1209             rcserror("invalid branch or revision pair %s : %s", num1, num2);
1210             return false;
1211         }
1212
1213         return true;
1214 }
1215
1216
1217
1218         static void
1219 getrevpairs(argv)
1220 register     char    * argv;
1221 /*  function:  get revision or branch range from command line, and   */
1222 /*             store in revlist                                      */
1223
1224 {
1225         register    char    c;
1226         struct      Revpairs  * nextrevpair;
1227         int separator;
1228
1229         c = *argv;
1230
1231         /* Support old ambiguous '-' syntax; this will go away.  */
1232         if (strchr(argv,':'))
1233             separator = ':';
1234         else {
1235             if (strchr(argv,'-')  &&  VERSION(5) <= RCSversion)
1236                 warn("`-' is obsolete in `-r%s'; use `:' instead", argv);
1237             separator = '-';
1238         }
1239
1240         for (;;) {
1241             while (c==' ' || c=='\t' || c=='\n')
1242                 c = *++argv;
1243             nextrevpair = talloc(struct Revpairs);
1244             nextrevpair->rnext = revlist;
1245             revlist = nextrevpair;
1246             nextrevpair->numfld = 1;
1247             nextrevpair->strtrev = argv;
1248             for (;;  c = *++argv) {
1249                 switch (c) {
1250                     default:
1251                         continue;
1252                     case '\0': case ' ': case '\t': case '\n':
1253                     case ',': case ';':
1254                         break;
1255                     case ':': case '-':
1256                         if (c == separator)
1257                             break;
1258                         continue;
1259                 }
1260                 break;
1261             }
1262             *argv = '\0';
1263             while (c==' ' || c=='\t' || c=='\n')
1264                 c = *++argv;
1265             if (c == separator) {
1266                 while ((c = *++argv) == ' ' || c == '\t' || c =='\n')
1267                     continue;
1268                 nextrevpair->endrev = argv;
1269                 for (;;  c = *++argv) {
1270                     switch (c) {
1271                         default:
1272                             continue;
1273                         case '\0': case ' ': case '\t': case '\n':
1274                         case ',': case ';':
1275                             break;
1276                         case ':': case '-':
1277                             if (c == separator)
1278                                 break;
1279                             continue;
1280                     }
1281                     break;
1282                 }
1283                 *argv = '\0';
1284                 while (c==' ' || c=='\t' || c =='\n')
1285                     c = *++argv;
1286                 nextrevpair->numfld =
1287                     !nextrevpair->endrev[0] ? 2 /* -rREV: */ :
1288                     !nextrevpair->strtrev[0] ? 3 /* -r:REV */ :
1289                     4 /* -rREV1:REV2 */;
1290             }
1291             if (!c)
1292                 break;
1293             else if (c==',' || c==';')
1294                 c = *++argv;
1295             else
1296                 error("missing `,' near `%c%s'", c, argv+1);
1297         }
1298 }