1: /*
2: * RLOG operation
3: */
4: #ifndef lint
5: static char rcsid[]=
6: "$Header: /usr/src/local/bin/rcs/src/RCS/rlog.c,v 4.5 87/12/18 11:46:38 narten Exp $ Purdue CS";
7: #endif
8: /*****************************************************************************
9: * print contents of RCS files
10: *****************************************************************************
11: *
12: * Copyright (C) 1982 by Walter Tichy
13: * Purdue University
14: * Computer Science Department
15: * West Lafayette, IN 47907
16: *
17: * All rights reserved. No part of this software may be sold or distributed
18: * in any form or by any means without the prior written permission of the
19: * author.
20: * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
21: */
22:
23:
24: /* $Log: rlog.c,v $
25: * Revision 4.5 87/12/18 11:46:38 narten
26: * more lint cleanups (Guy Harris)
27: *
28: * Revision 4.4 87/10/18 10:41:12 narten
29: * Updating version numbers
30: * Changes relative to 1.1 actually relative to 4.2
31: *
32: * Revision 1.3 87/09/24 14:01:10 narten
33: * Sources now pass through lint (if you ignore printf/sprintf/fprintf
34: * warnings)
35: *
36: * Revision 1.2 87/03/27 14:22:45 jenkins
37: * Port to suns
38: *
39: * Revision 1.1 84/01/23 14:50:45 kcs
40: * Initial revision
41: *
42: * Revision 4.2 83/12/05 09:18:09 wft
43: * changed rewriteflag to external.
44: *
45: * Revision 4.1 83/05/11 16:16:55 wft
46: * Added -b, updated getnumericrev() accordingly.
47: * Replaced getpwuid() with getcaller().
48: *
49: * Revision 3.7 83/05/11 14:24:13 wft
50: * Added options -L and -R;
51: * Fixed selection bug with -l on multiple files.
52: * Fixed error on dates of the form -d'>date' (rewrote getdatepair()).
53: *
54: * Revision 3.6 82/12/24 15:57:53 wft
55: * shortened output format.
56: *
57: * Revision 3.5 82/12/08 21:45:26 wft
58: * removed call to checkaccesslist(); used DATEFORM to format all dates;
59: * removed unused variables.
60: *
61: * Revision 3.4 82/12/04 13:26:25 wft
62: * Replaced getdelta() with gettree(); removed updating of field lockedby.
63: *
64: * Revision 3.3 82/12/03 14:08:20 wft
65: * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE.
66: * Fixed printing of nil, removed printing of Suffix,
67: * added shortcut if no revisions are printed, disambiguated struct members.
68: *
69: * Revision 3.2 82/10/18 21:09:06 wft
70: * call to curdir replaced with getfullRCSname(),
71: * fixed call to getlogin(), cosmetic changes on output,
72: * changed conflicting long identifiers.
73: *
74: * Revision 3.1 82/10/13 16:07:56 wft
75: * fixed type of variables receiving from getc() (char -> int).
76: */
77:
78:
79:
80: #include "time.h"
81: #include "rcsbase.h"
82: #ifndef lint
83: static char rcsbaseid[] = RCSBASE;
84: #endif
85:
86: extern char * partialno();
87: extern FILE * fopen();
88: extern char * getcaller(); /*get login of caller */
89: extern char * malloc();
90: extern free();
91: extern struct hshentry * genrevs(); /*generate delta numbers */
92: extern int countnumflds();
93: extern int compartial();
94: extern int expandsym(); /*get numeric name of a revision */
95: extern char * getfullRCSname(); /*get full path name of RCS file */
96: extern int nextc; /*next input character */
97: extern char * Klog;
98: extern char * Ktext;
99: extern int partime();
100: extern long maketime(); /*convert parsed time to unix time. */
101: extern struct tm * localtime(); /*convert unixtime into a tm-structure */
102: extern int pairfilenames();
103: extern struct hshentry * getnum();
104: extern FILE * finptr; /* RCS input file */
105: extern FILE * frewrite; /* new RCS file */
106: extern int rewriteflag; /* indicates whether input should be */
107: /* echoed to frewrite */
108: extern int nerror; /* error counter */
109:
110: char * RCSfilename, * workfilename;
111:
112: char * caller; /* caller's login; */
113: int descflag, selectflag, selectop; /* option to print access list, symbolic */
114: /* names, descriptive text, locks and */
115: /* Head */
116: int onlylockflag; /* option to print only files */
117: /* with locks */
118: int onlyRCSflag; /* option to print only RCS file name */
119: int lockflag; /* whether locker option is set */
120: int revno; /* number of revision chosen */
121:
122: struct lockers { /* lockers in locker option; stored */
123: char * login; /* lockerlist */
124: struct lockers * lockerlink;
125: } ;
126:
127: struct stateattri { /* states in state option; stored in */
128: char * status; /* statelist */
129: struct stateattri * nextstate;
130: } ;
131:
132: struct authors { /* login names in author option; */
133: char * login; /* stored in authorlist */
134: struct authors * nextauthor;
135: } ;
136:
137: struct Revpairs{ /* revision or branch range in -r */
138: int numfld; /* option; stored in revlist */
139: char * strtrev;
140: char * endrev;
141: struct Revpairs * rnext;
142: } ;
143:
144: struct Datepairs{ /* date range in -d option; stored in */
145: char strtdate[datelength]; /* duelst and datelist */
146: char enddate[datelength];
147: struct Datepairs * dnext;
148: };
149:
150: char Dotstring[200]; /* string of numeric revision name */
151: char * Nextdotstring; /* next available place of Dotstring */
152: struct Datepairs * datelist, * duelst;
153: struct Revpairs * revlist, * Revlst;
154: int branchflag; /* set on -b */
155: struct lockers * lockerlist;
156: struct stateattri * statelist;
157: struct authors * authorlist;
158:
159:
160:
161: main (argc, argv)
162: int argc;
163: char * argv[];
164: {
165: struct Datepairs * currdate;
166: struct assoc * curassoc;
167: struct access * curaccess;
168: struct lock * currlock;
169: char * cmdusage;
170:
171: cmdusage = "command format:\nrlog -L -R -h -t -b -ddates -l[lockers] -rrevisions -sstates -w[logins] file ...";
172: cmdid = "rlog";
173: descflag = selectflag = true;
174: lockflag = onlylockflag = selectop = false;
175: onlyRCSflag = false;
176: lockerlist = nil;
177: authorlist = nil;
178: statelist = nil;
179: Revlst = revlist = nil;
180: branchflag= false;
181: duelst = datelist = nil;
182: caller=getcaller();
183:
184: while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
185: switch ((*argv)[1]) {
186:
187: case 'L':
188: onlylockflag = true;
189: break;
190:
191: case 'R':
192: onlyRCSflag =true;
193: break;
194:
195: case 'l':
196: selectop = true;
197: lockflag = true;
198: getlocker( (*argv)+2 );
199: break;
200:
201: case 'b':
202: selectop = true;
203: branchflag = true;
204: break;
205:
206: case 'r':
207: selectop = true;
208: getrevpairs( (*argv)+2 );
209: break;
210:
211: case 'd':
212: selectop = true;
213: getdatepair( (*argv)+2 );
214: break;
215:
216: case 's':
217: selectop = true;
218: getstate( (*argv)+2);
219: break;
220:
221: case 'w':
222: selectop = true;
223: getauthor( (*argv)+2);
224: break;
225:
226: case 'h':
227: if ( ! selectflag ) warn("option -t overrides -h");
228: else descflag = false;
229: break;
230:
231: case 't':
232: selectflag = false;
233: if ( ! descflag ) warn("option -t overrides -h");
234: descflag = true;
235: break;
236:
237: default:
238: faterror("unknown option: %s\n%s", *argv,cmdusage);
239:
240: };
241: } /* end of option processing */
242:
243: if (argc<1) faterror("No input file\n%s",cmdusage);
244:
245:
246: /* now handle all filenames */
247: do {
248: rewriteflag=false;
249: finptr=frewrite=nil;
250:
251:
252: if (!pairfilenames(argc, argv, true,false)) continue;
253:
254: /* now RCSfilename contains the name of the RCS file, and finptr
255: * the file descriptor. Workfilename contains the name of the
256: * working file.
257: */
258:
259: if ( !trysema(RCSfilename, false)) goto loopend; /* give up */
260:
261: /* do nothing if -L is given and there are no locks*/
262: if ( onlylockflag && Locks == nil ) goto loopend;
263:
264: if ( onlyRCSflag ) {
265: VOID fprintf(stdout, "%s\n", RCSfilename);
266: goto loopend;
267: }
268: /* print RCS filename , working filename and optional
269: administrative information */
270: VOID fprintf(stdout, "\nRCS file: %s; ",RCSfilename);
271: /* could use getfullRCSname() here, but that is very slow */
272: VOID fprintf(stdout, "Working file: %s\n", workfilename);
273: VOID fprintf(stdout, "head: %s\n", Head==nil?"":Head->num);
274: VOID fprintf(stdout, "branch: %s\n", Dbranch==nil?"":Dbranch->num);
275:
276: VOID fputs("locks: ", stdout); /* print locker list */
277: currlock = Locks;
278: while( currlock ) {
279: VOID fprintf(stdout," %s: %s;", currlock->login,
280: currlock->delta->num);
281: currlock = currlock->nextlock;
282: }
283: if ( StrictLocks )
284: VOID fputs(Locks==nil?" ; strict":" strict",stdout);
285:
286: VOID fputs("\naccess list: ", stdout); /* print access list */
287: curaccess = AccessList;
288: while(curaccess) {
289: VOID fputs(" ",stdout);
290: VOID fputs(curaccess->login, stdout);
291: curaccess = curaccess->nextaccess;
292: }
293:
294: VOID fputs("\nsymbolic names:", stdout); /* print symbolic names */
295: curassoc = Symbols;
296: while( curassoc ) {
297: VOID fprintf(stdout, " %s: %s;",curassoc->symbol,
298: curassoc->delta->num);
299: curassoc = curassoc->nextassoc;
300: }
301:
302: VOID fprintf(stdout,"\ncomment leader: \"%s\"\n",Comment);
303:
304: gettree();
305: VOID fprintf(stdout, "total revisions: %d; ", TotalDeltas);
306: if ( Head == nil || !selectflag || !descflag) {
307: VOID putc('\n',stdout);
308: if (descflag) VOID fputs("description:\n", stdout);
309: getdesc(descflag);
310: VOID fputs("=============================================================================\n",stdout);
311: goto loopend;
312: }
313:
314:
315: /* keep only those locks given by -l */
316: if (lockflag)
317: trunclocks();
318: getnumericrev(); /* get numeric revision or branch names */
319: revno = 0;
320:
321: exttree(Head);
322:
323: /* get most recently date of the dates pointed by duelst */
324: currdate = duelst;
325: while( currdate) {
326: recentdate(Head, currdate);
327: currdate = currdate->dnext;
328: }
329:
330: extdate(Head);
331:
332: /* reinitialize the date specification list */
333: currdate = duelst;
334: while(currdate) {
335: VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0);
336: currdate = currdate->dnext;
337: }
338:
339: if ( selectop || ( selectflag && descflag) )
340: VOID fprintf(stdout, "selected revisions: %d", revno);
341: VOID putc('\n', stdout);
342: if (descflag) VOID fputs("description:\n", stdout);
343: getdesc(descflag);
344: while( (nexttok != EOFILE) && readdeltalog());
345: if (selectflag && descflag && revno) {
346: putrunk();
347: putree(Head);
348: if (nextlex(), nexttok != EOFILE)
349: fatserror("syntax error; expecting EOF");
350: }
351: VOID fputs("=============================================================================\n",stdout);
352: loopend:
353: VOID fclose(finptr);
354: } while( ++argv, --argc >= 1);
355: exit(nerror!=0);
356: }
357:
358:
359:
360: putrunk()
361: /* function: print revisions chosen, which are in trunk */
362:
363: {
364: struct hshentry * ptr, * pre;
365:
366: if (Head == nil) return; /* empty tree */
367:
368: pre = Head;
369: ptr = Head->next;
370: while( ptr ) {
371: putadelta(pre,ptr,true);
372: pre = ptr;
373: ptr = ptr->next;
374: }
375: putadelta(pre,ptr,true);
376: }
377:
378:
379:
380: putree(root)
381: struct hshentry *root;
382: /* function: print delta tree( not include trunck) in reversed calender
383: order on each branch */
384:
385: {
386: if ( root == nil ) return;
387:
388: putree(root->next);
389:
390: putforest(root->branches);
391: }
392:
393:
394:
395:
396: putforest(branchroot)
397: struct branchhead * branchroot;
398: /* function: print branches that has the same direct ancestor */
399: {
400:
401: if ( branchroot == nil ) return;
402:
403: putforest(branchroot->nextbranch);
404:
405: putabranch(branchroot->hsh);
406: putree(branchroot->hsh);
407: }
408:
409:
410:
411:
412: putabranch(root)
413: struct hshentry *root;
414: /* function : print one branch */
415:
416: {
417:
418: if ( root == nil) return;
419:
420: putabranch(root->next);
421:
422: putadelta(root, root, false);
423: }
424:
425:
426:
427:
428:
429: putadelta(node,editscript,trunk)
430: register struct hshentry * node;
431: register struct hshentry * editscript;
432: int trunk;
433: /* function: print delta node if node->selector is 's'. */
434: /* editscript indicates where the editscript is stored */
435: /* trunk indicated whether this node is in trunk */
436: {
437: struct branchhead * newbranch;
438: char * branchnum, branch[40];
439:
440: if ( ( node == nil) || ( node->selector == 'u'))
441: return;
442:
443: VOID fprintf(stdout,"----------------------------\n");
444: VOID fprintf(stdout, "revision %s ",node->num);
445: if ( node->lockedby )
446: VOID fprintf(stdout, "locked by: %s; ", node->lockedby);
447: VOID putc('\n', stdout);
448:
449: VOID fputs("date: ",stdout);
450: VOID PRINTDATE(stdout,node->date); VOID putc(' ',stdout);
451: VOID PRINTTIME(stdout,node->date);
452: VOID fprintf(stdout, "; author: %s; ", node->author);
453: VOID fprintf(stdout, "state: %s; ", node->state);
454:
455: if ( editscript )
456: if(trunk)
457: VOID fprintf(stdout,"lines added/del: %d/%d",
458: editscript->deletelns, editscript->insertlns);
459: else
460: VOID fprintf(stdout,"lines added/del: %d/%d",
461: editscript->insertlns, editscript->deletelns);
462:
463: VOID putc('\n', stdout);
464:
465: branchnum = & (branch[0]);
466: newbranch = node->branches;
467: if ( newbranch ) {
468: VOID fputs("branches: ", stdout);
469: while( newbranch ) {
470: getbranchno(newbranch->hsh->num, branchnum);
471: VOID fprintf(stdout, "%s; ", branchnum);
472: newbranch = newbranch->nextbranch;
473: }
474: VOID putc('\n', stdout);
475: }
476:
477: VOID fputs(node->log,stdout);
478: }
479:
480:
481:
482:
483:
484: readdeltalog()
485: /* Function : get the log message and skip the text of a deltatext node.
486: * Return false if current block does not start with a number.
487: * Assumes the current lexeme is not yet in nexttok; does not
488: * advance nexttok.
489: */
490: {
491: register struct hshentry * Delta;
492:
493: nextlex();
494: if ( !(Delta = getnum() )) return(false);
495: if ( ! getkey(Klog) || ( nexttok != STRING ) )
496: fatserror("Missing log entry");
497: Delta->log = malloc(logsize);
498: VOID savestring(Delta->log, logsize);
499: nextlex();
500: if ( ! getkey(Ktext) || (nexttok != STRING) )
501: fatserror("Missing delta text");
502: Delta->insertlns = Delta->deletelns = 0;
503: if ( Delta != Head)
504: getscript(Delta);
505: else
506: readstring();
507: return true;
508: }
509:
510:
511:
512: getscript(Delta)
513: struct hshentry * Delta;
514: /* function: read edit script of Delta and count how many lines added */
515: /* and deleted in the script */
516:
517: {
518: int ed; /* editor command */
519: register int c;
520: register int i;
521: int length;
522:
523: while( (ed = getc(finptr)) != EOF) {
524: /* assume first none white character is command name */
525: while( ed == '\n' || ed == ' ' || ed == '\t')
526: ed = getc(finptr);
527: if (ed == SDELIM) break; /* script text is ended */
528: while( ( c = getc(finptr)) == ' ' ); /* skip blank */
529: if ( ! ('0' <= c && c <= '9')) {
530: faterror("Missing line number in edit script");
531: break;
532: }
533: while( '0' <= (c = getc(finptr)) && c <= '9' ) ;
534:
535: while( c == ' ')c = getc(finptr); /* skip blanks */
536: if ( !('0' <= c && c <= '9' ) ) {
537: faterror("Incorrect range in edit script");
538: break;
539: }
540: length = c - '0';
541: while( '0' <= (c = getc(finptr)) && c <= '9' )
542: length = length * 10 + c - '0';
543: while( c != '\n' && c != EOF) c = getc(finptr);
544: switch (ed) {
545: case 'd' :
546: Delta->deletelns += length;
547: break;
548:
549: case 'a' :
550: /* skip scripted lines */
551: for ( i=length; i > 0 && c != EOF; i--){
552: while( (c=getc(finptr)) != '\n' && c != EOF);
553: Delta->insertlns++;
554: }
555: break;
556:
557: default:
558: faterror("Unknown command in edit script: %c", ed);
559: break;
560: }
561: }
562: nextc = getc(finptr);
563: }
564:
565:
566:
567:
568:
569:
570:
571: exttree(root)
572: struct hshentry *root;
573: /* function: select revisions , starting with root */
574:
575: {
576: struct branchhead * newbranch;
577:
578: if (root == nil) return;
579:
580: extractdelta(root);
581: exttree(root->next);
582:
583: newbranch = root->branches;
584: while( newbranch ) {
585: exttree(newbranch->hsh);
586: newbranch = newbranch->nextbranch;
587: }
588: }
589:
590:
591:
592:
593: getlocker(argv)
594: char * argv;
595: /* function : get the login names of lockers from command line */
596: /* and store in lockerlist. */
597:
598: {
599: register char c;
600: struct lockers * newlocker;
601: argv--;
602: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
603: c == '\n' || c == ';') ;
604: if ( c == '\0') {
605: lockerlist=nil;
606: return;
607: }
608:
609: while( c != '\0' ) {
610: newlocker = ( struct lockers *)malloc( sizeof(struct lockers) );
611: newlocker->lockerlink = lockerlist;
612: newlocker->login = argv;
613: lockerlist = newlocker;
614: while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' '
615: && c != '\t' && c != '\n' && c != ';') ;
616: *argv = '\0';
617: if ( c == '\0' ) return;
618: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
619: c == '\n' || c == ';') ;
620: }
621: }
622:
623:
624:
625: getauthor(argv)
626: char *argv;
627: /* function: get the author's name form command line */
628: /* and store in aauthorlist */
629:
630: {
631: register c;
632: struct authors * newauthor;
633:
634: argv--;
635: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
636: c == '\n' || c == ';') ;
637: if ( c == '\0' ) {
638: authorlist = (struct authors *)malloc(sizeof(struct authors));
639: authorlist->login = caller;
640: authorlist->nextauthor = nil;
641: return;
642: }
643:
644: while( c != '\0' ) {
645: newauthor = (struct authors *)malloc(sizeof(struct authors));
646: newauthor->nextauthor = authorlist;
647: newauthor->login = argv;
648: authorlist = newauthor;
649: while( ( c = *++argv) != ',' && c != '\0' && c != ' '
650: && c != '\t' && c != '\n' && c != ';') ;
651: * argv = '\0';
652: if ( c == '\0') return;
653: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
654: c == '\n' || c == ';') ;
655: }
656: }
657:
658:
659:
660:
661: getstate(argv)
662: char * argv;
663: /* function : get the states of revisions from command line */
664: /* and store in statelist */
665:
666: {
667: register char c;
668: struct stateattri *newstate;
669:
670: argv--;
671: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
672: c == '\n' || c == ';') ;
673: if ( c == '\0'){
674: warn(" Missing state attributes after -s options");
675: return;
676: }
677:
678: while( c != '\0' ) {
679: newstate = (struct stateattri *)malloc(sizeof(struct stateattri));
680: newstate->nextstate = statelist;
681: newstate->status = argv;
682: statelist = newstate;
683: while( (c = (*++argv)) != ',' && c != '\0' && c != ' '
684: && c != '\t' && c != '\n' && c != ';') ;
685: *argv = '\0';
686: if ( c == '\0' ) return;
687: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
688: c == '\n' || c == ';') ;
689: }
690: }
691:
692:
693:
694: trunclocks()
695: /* Function: Truncate the list of locks to those that are held by the */
696: /* id's on lockerlist. Do not truncate if lockerlist empty. */
697:
698: {
699: struct lockers * plocker;
700: struct lock * plocked, * nextlocked;
701:
702: if ( (lockerlist == nil) || (Locks == nil)) return;
703:
704: /* shorten Locks to those contained in lockerlist */
705: plocked = Locks;
706: Locks = nil;
707: while( plocked != nil) {
708: plocker = lockerlist;
709: while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0))
710: plocker = plocker->lockerlink;
711: nextlocked = plocked->nextlock;
712: if ( plocker != nil) {
713: plocked->nextlock = Locks;
714: Locks = plocked;
715: }
716: plocked = nextlocked;
717: }
718: }
719:
720:
721:
722: recentdate(root, pd)
723: struct hshentry * root;
724: struct Datepairs * pd;
725: /* function: Finds the delta that is closest to the cutoff date given by */
726: /* pd among the revisions selected by exttree. */
727: /* Successively narrows down the interfal given by pd, */
728: /* and sets the strtdate of pd to the date of the selected delta */
729: {
730: struct branchhead * newbranch;
731:
732: if ( root == nil) return;
733: if ( root->selector == 's') {
734: if ( cmpnum(root->date, pd->strtdate) >= 0 &&
735: cmpnum(root->date, pd->enddate) <= 0)
736: VOID strcpy(pd->strtdate, root->date);
737: }
738:
739: recentdate(root->next, pd);
740: newbranch = root->branches;
741: while( newbranch) {
742: recentdate(newbranch->hsh, pd);
743: newbranch = newbranch->nextbranch;
744: }
745: }
746:
747:
748:
749:
750:
751:
752: extdate(root)
753: struct hshentry * root;
754: /* function: select revisions which are in the date range specified */
755: /* in duelst and datelist, start at root */
756:
757: {
758: struct branchhead * newbranch;
759: struct Datepairs * pdate;
760:
761: if ( root == nil) return;
762:
763: if ( datelist || duelst) {
764: pdate = datelist;
765: while( pdate ) {
766: if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){
767: if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0)
768: break;
769: }
770: pdate = pdate->dnext;
771: }
772: if ( pdate == nil) {
773: pdate = duelst;
774: while(pdate) {
775: if ( cmpnum(root->date, pdate->strtdate) == 0)
776: break;
777: pdate = pdate->dnext;
778: }
779: }
780: if ( pdate == nil)
781: root->selector = 'u';
782: }
783: if (root->selector == 's') revno++;
784:
785: extdate(root->next);
786:
787: newbranch = root->branches;
788: while( newbranch ) {
789: extdate(newbranch->hsh);
790: newbranch = newbranch->nextbranch;
791: }
792: }
793:
794:
795:
796: (pdelta)
797: struct hshentry * pdelta;
798: /* function: compare information of pdelta to the authorlst, lockerlist, */
799: /* statelist, revlist and mark 's' on selector if pdelta is */
800: /* selected; otherwise, mark 'u' */
801:
802: {
803: struct lock * plock;
804: struct stateattri * pstate;
805: struct authors * pauthor;
806: struct Revpairs * prevision;
807: int length;
808:
809: pdelta->selector = 's';
810: if ( authorlist ) { /* certain author's revisions wanted only */
811: pauthor = authorlist;
812: while((pauthor != nil) && ( strcmp(pauthor->login, pdelta->author)!=0))
813: pauthor = pauthor->nextauthor;
814: if ( pauthor == nil ) {
815: pdelta->selector = 'u';
816: return;
817: }
818: }
819: if ( statelist ) { /* revisions with certain state wanted */
820: pstate = statelist;
821: while((pstate != nil) && (strcmp(pstate->status, pdelta->state)!=0))
822: pstate = pstate->nextstate;
823: if ( pstate == nil ) {
824: pdelta->selector = 'u';
825: return;
826: }
827: }
828: if ( lockflag ) { /* locked revisions */
829: plock = Locks;
830: while( plock && (plock->delta != pdelta))
831: plock = plock->nextlock;
832: if (plock == nil ) {
833: pdelta->selector = 'u';
834: return;
835: }
836: }
837: if ( Revlst ) { /* revisions or branches selected */
838:
839: prevision = Revlst;
840: while( prevision != nil ) {
841: length = prevision->numfld;
842: if ( length % 2 == 1) { /* a branch number */
843: if ( countnumflds(pdelta->num) ==(length+1))
844: if ( (compartial(pdelta->num, prevision->strtrev,length) >= 0)&&
845: (compartial(prevision->endrev, pdelta->num, length) >= 0) )
846: break;
847: }
848: else if ( countnumflds(pdelta->num ) == length) /* a revision */
849: if ( (compartial(pdelta->num, prevision->strtrev, length) >= 0) &&
850: (compartial(prevision->endrev, pdelta->num, length) >= 0) )
851: break;
852: prevision = prevision->rnext;
853: }
854: if (prevision == nil) {
855: pdelta->selector = 'u';
856: return;
857: }
858: }
859: }
860:
861:
862:
863: char * procdate(target, source)
864: char * target, * source;
865: /* Function: Parses a free-format date in target, converts it
866: * into RCS internal format, and stores the result into source.
867: * Returns target on success, nil otherwise.
868: */
869: {
870: long unixtime;
871: struct tm parseddate, *ftm;
872:
873: if ( partime(source, &parseddate) == 0) {
874: error("Can't parse date/time: %s", source);
875: *target= '\0';
876: return nil;
877: }
878: if ( (unixtime = maketime(&parseddate)) == 0L) {
879: error("Inconsistent date/time: %s", source);
880: *target='\0';
881: return nil;
882: }
883: ftm = localtime(&unixtime);
884: VOID sprintf(target,DATEFORM,
885: ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
886: return target;
887: }
888:
889:
890:
891: getdatepair(argv)
892: char * argv;
893: /* function: get time range from command line and store in datelist if */
894: /* a time range specified or in duelst if a time spot specified */
895:
896: {
897: register char c;
898: struct Datepairs * nextdate;
899: char * rawdate;
900: int switchflag;
901:
902: argv--;
903: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
904: c == '\n' || c == ';') ;
905: if ( c == '\0' ) {
906: warn("Missing date/time after -d");
907: return;
908: }
909:
910: while( c != '\0' ) {
911: switchflag = false;
912: nextdate = (struct Datepairs *) malloc(sizeof(struct Datepairs));
913: if ( c == '<' ) { /* case: -d <date */
914: c = *++argv;
915: (nextdate->strtdate)[0] = '\0';
916: } elsif (c == '>') { /* case: -d >date */
917: c = *++argv;
918: (nextdate->enddate)[0] = '\0';
919: switchflag = true;
920: } else {
921: rawdate = argv;
922: while( c != '<' && c != '>' && c != ';' && c != '\0')
923: c = *++argv;
924: *argv = '\0';
925: if ( c == '>' ) switchflag=true;
926: if (procdate(switchflag?nextdate->enddate:nextdate->strtdate,
927: rawdate)==nil) continue;
928: if ( c == ';' || c == '\0') { /* case: -d date */
929: VOID strcpy(nextdate->enddate,nextdate->strtdate);
930: VOID sprintf(nextdate->strtdate,DATEFORM,0,0,0,0,0,0);
931: nextdate->dnext = duelst;
932: duelst = nextdate;
933: goto end;
934: } else {
935: /* case: -d date< or -d date>; see switchflag */
936: while ( (c= *++argv) == ' ' || c=='\t' || c=='\n');
937: if ( c == ';' || c == '\0') {
938: /* second date missing */
939: if (switchflag)
940: *nextdate->strtdate= '\0';
941: else
942: *nextdate->enddate= '\0';
943: nextdate->dnext = datelist;
944: datelist = nextdate;
945: goto end;
946: }
947: }
948: }
949: rawdate = argv;
950: while( c != '>' && c != '<' && c != ';' && c != '\0')
951: c = *++argv;
952: *argv = '\0';
953: if (procdate(switchflag?nextdate->strtdate:nextdate->enddate,
954: rawdate)==nil) continue;
955: nextdate->dnext = datelist;
956: datelist = nextdate;
957: end:
958: /*
959: VOID printf("startdate: %s; enddate: %s;\n", nextdate->strtdate,nextdate->enddate);
960: */
961: if ( c == '\0') return;
962: while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n');
963: }
964: }
965:
966:
967:
968:
969:
970: getnumericrev()
971: /* function: get the numeric name of revisions which stored in revlist */
972: /* and then stored the numeric names in Revlst */
973: /* if branchflag, also add default branch */
974:
975: {
976: struct Revpairs * ptr, *pt;
977: int flag;
978: char *temprev;
979:
980: /* free the previous numeric revision list */
981: pt = Revlst;
982: while( pt) {
983: free((char *)pt);
984: pt = pt->rnext;
985: }
986: Nextdotstring = &Dotstring[0]; /* reset buffer */
987:
988:
989: Revlst = nil;
990: ptr = revlist;
991: while( ptr ) {
992: pt = (struct Revpairs *) malloc(sizeof(struct Revpairs));
993: if ( ptr->numfld == 1 ){ /* case: -r rev */
994: if ( (flag = expandsym(ptr->strtrev, Nextdotstring)) == true ) {
995: pt->numfld = countnumflds(Nextdotstring);
996: pt->strtrev = pt->endrev = Nextdotstring;
997: while( *Nextdotstring++ != '\0' ) ;
998: }
999: }
1000: else if( ptr->numfld == 2){ /* case: -r rev- */
1001: if ( (flag = expandsym(ptr->strtrev, Nextdotstring)) == true) {
1002: pt->numfld = countnumflds(Nextdotstring);
1003: pt->strtrev = Nextdotstring;
1004: while( *Nextdotstring++ != '\0' ) ;
1005: pt->endrev = Nextdotstring;
1006: if ( pt->numfld > 2) choptail(pt->strtrev);
1007: * Nextdotstring++ = '\0';
1008: }
1009: }
1010: else if(ptr->numfld == 3) { /* case: -r -rev */
1011: if ( (flag = expandsym(ptr->endrev, Nextdotstring)) == true) {
1012: pt->endrev = Nextdotstring;
1013: while( *Nextdotstring++ != '\0' ) ;
1014: pt->numfld = countnumflds(pt->endrev);
1015: pt->strtrev = Nextdotstring;
1016: if ( pt->numfld == 2)
1017: *Nextdotstring++ = '1';
1018: else
1019: choptail(pt->endrev);
1020: *Nextdotstring++ = '.';
1021: *Nextdotstring++ = '1';
1022: *Nextdotstring++ = '\0';
1023: }
1024: }
1025: else { /* case: -r rev1-rev2 */
1026: if ( (flag = expandsym(ptr->strtrev, Nextdotstring)) == true ) {
1027: pt->strtrev = Nextdotstring;
1028: while( *Nextdotstring++ != '\0' ) ;
1029: if ( ( flag = expandsym(ptr->endrev, Nextdotstring)) == true) {
1030: pt->numfld = countnumflds(pt->strtrev);
1031: pt->endrev = Nextdotstring;
1032: while( *Nextdotstring++ != '\0' ) ;
1033: if((flag = checkrevpair(pt->strtrev, pt->endrev)) == true)
1034: /* switch pt->strtrev with pt->endrev, if pt->strtrev > pt->endre */
1035: if (compartial(pt->strtrev, pt->endrev, pt->numfld) > 0 ) {
1036: temprev = pt->strtrev;
1037: pt->strtrev = pt->endrev;
1038: pt->endrev = temprev;
1039: }
1040: }
1041: }
1042: }
1043:
1044: if ( flag ){
1045: pt->rnext = Revlst;
1046: Revlst = pt;
1047: }
1048: else
1049: free((char *)pt);
1050: ptr = ptr->rnext;
1051: }
1052: /* Now take care of branchflag */
1053: if (branchflag) {
1054: flag =true;
1055: pt = (struct Revpairs *) malloc(sizeof(struct Revpairs));
1056: if (Dbranch) {
1057: pt->strtrev = pt->endrev = Dbranch->num;
1058: } elsif (Head!=nil) {
1059: pt->strtrev = pt->endrev = /* branch number of head */
1060: partialno(Nextdotstring,Head->num,1);
1061: while( *Nextdotstring++ != '\0' ) ;
1062: } else flag = false;
1063: if (flag) { /* prepend new node */
1064: pt->rnext=Revlst; Revlst=pt;
1065: pt->numfld = countnumflds(pt->strtrev);
1066: }
1067: }
1068:
1069: }
1070:
1071:
1072:
1073: checkrevpair(num1,num2)
1074: char *num1, *num2;
1075: /* function: check whether num1, num2 are legal pair,i.e.
1076: only the last field are different and have same number of
1077: feilds( if length <= 2, may be different if first field) */
1078:
1079: {
1080: int length;
1081:
1082: if ( (length = countnumflds(num1)) != countnumflds(num2) ) {
1083: error(" Invalid branch or revision pair %s : %s", num1, num2);
1084: return false;
1085: }
1086: if ( length > 2 )
1087: if (compartial(num1, num2, length-1) != 0) {
1088: error("Invalid branch or revision pair %s : %s", num1, num2);
1089: return false;
1090: }
1091:
1092: return true;
1093: }
1094:
1095:
1096:
1097: getrevpairs(argv)
1098: register char * argv;
1099: /* function: get revision or branch range from command line, and */
1100: /* store in revlist */
1101:
1102: {
1103: register char c;
1104: struct Revpairs * nextrevpair;
1105: int flag;
1106:
1107: argv--;
1108: while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' ||
1109: c == '\n' || c == ';') ;
1110: if ( c == '\0' ) {
1111: warn(" Missing revision or branch number after -r");
1112: return;
1113: }
1114:
1115: while( c != '\0') {
1116: while( c == ',' || c == ' ' || c == '\t' ||
1117: c == '\n' || c == ';') c = *++argv;
1118: if (c == '\0') return;
1119: nextrevpair = (struct Revpairs *) malloc(sizeof(struct Revpairs));
1120: nextrevpair->rnext = revlist;
1121: revlist = nextrevpair;
1122: nextrevpair->numfld = nil;
1123: nextrevpair->strtrev = nil;
1124: nextrevpair->endrev = nil;
1125: flag = false;
1126: if ( c == '<' || c == '-' ) { /* case: -r -rev or -r <rev */
1127: flag = true;
1128: while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ;
1129: }
1130: else {
1131: nextrevpair->strtrev = argv;
1132: /* get a revision or branch name */
1133: while( c != ',' && c != ';' && c != ' ' && c != '\0' && c != '-'
1134: && c != '\t' && c != '\n' && c != '<') c = *++argv;
1135:
1136: *argv = '\0';
1137:
1138: if ( c != '<' && c != '-') { /* case: rev */
1139: nextrevpair->numfld = 1;
1140: continue;
1141: }
1142:
1143: if ( (c =(*++argv)) == ',' || c == '\0' || c == ' '
1144: || c == '\t' || c == '\n' || c == ';') {/* case: rev_ */
1145: nextrevpair->numfld = 2;
1146: continue;
1147: }
1148: }
1149: nextrevpair->endrev = argv;
1150: while( c != ',' && c != ' ' && c != '\0' && c != '\t' && c != '<'
1151: && c != '\n' && c != '-' && c != ';') c = *++argv;
1152:
1153: * argv = '\0';
1154: if ( c == '<'){
1155: error("seperator expected near %s", nextrevpair->endrev);
1156: while( (c = *++argv) != ',' && c != ' ' && c != '\0' &&
1157: c != '\t' && c != '\n' && c != ';' ) ;
1158: revlist = nextrevpair->rnext;
1159: continue;
1160: }
1161: else {
1162: if (flag) /* case: -rev */
1163: nextrevpair->numfld = 3;
1164:
1165: else /* rev1-rev2 appears */
1166: nextrevpair->numfld = 4;
1167: }
1168: }
1169: }
1170:
1171:
1172:
1173: choptail(strhead)
1174: char * strhead;
1175: /* function : chop off the last field of a branch or a revision number */
1176:
1177: {
1178: char *pt, *sp;
1179:
1180: for(pt = Nextdotstring-1; pt != strhead && *pt != '.'; pt--) ;
1181: for(sp = strhead; sp < pt; sp++) *Nextdotstring++ = *sp;
1182: }