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