1: /*
2: * RCS create/change operation
3: */
4: #if !defined(lint) && defined(DOSCCS)
5: static char rcsid[]=
6: "$Header: /usr/src/local/bin/rcs/src/RCS/rcs.c,v 4.7 87/12/18 11:37:17 narten Exp $ Purdue CS";
7: #endif
8: /***************************************************************************
9: * create RCS files or change RCS file attributes
10: * Compatibility with release 2: define COMPAT2
11: ***************************************************************************
12: *
13: * Copyright (C) 1982 by Walter F. Tichy
14: * Purdue University
15: * Computer Science Department
16: * West Lafayette, IN 47907
17: *
18: * All rights reserved. No part of this software may be sold or distributed
19: * in any form or by any means without the prior written permission of the
20: * author.
21: */
22:
23:
24:
25: /* $Log: rcs.c,v $
26: * Revision 4.7 87/12/18 11:37:17 narten
27: * lint cleanups (Guy Harris)
28: *
29: * Revision 4.6 87/10/18 10:28:48 narten
30: * Updating verison numbers. Changes relative to 1.1 are actually
31: * relative to 4.3
32: *
33: * Revision 1.4 87/09/24 13:58:52 narten
34: * Sources now pass through lint (if you ignore printf/sprintf/fprintf
35: * warnings)
36: *
37: * Revision 1.3 87/03/27 14:21:55 jenkins
38: * Port to suns
39: *
40: * Revision 1.2 85/12/17 13:59:09 albitz
41: * Changed setstate to rcs_setstate because of conflict with random.o.
42: *
43: * Revision 1.1 84/01/23 14:50:09 kcs
44: * Initial revision
45: *
46: * Revision 4.3 83/12/15 12:27:33 wft
47: * rcs -u now breaks most recent lock if it can't find a lock by the caller.
48: *
49: * Revision 4.2 83/12/05 10:18:20 wft
50: * Added conditional compilation for sending mail.
51: * Alternatives: V4_2BSD, V6, USG, and other.
52: *
53: * Revision 4.1 83/05/10 16:43:02 wft
54: * Simplified breaklock(); added calls to findlock() and getcaller().
55: * Added option -b (default branch). Updated -s and -w for -b.
56: * Removed calls to stat(); now done by pairfilenames().
57: * Replaced most catchints() calls with restoreints().
58: * Removed check for exit status of delivermail().
59: * Directed all interactive output to stderr.
60: *
61: * Revision 3.9.1.1 83/12/02 22:08:51 wft
62: * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
63: *
64: * Revision 3.9 83/02/15 15:38:39 wft
65: * Added call to fastcopy() to copy remainder of RCS file.
66: *
67: * Revision 3.8 83/01/18 17:37:51 wft
68: * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
69: *
70: * Revision 3.7 83/01/15 18:04:25 wft
71: * Removed putree(); replaced with puttree() in rcssyn.c.
72: * Combined putdellog() and scanlogtext(); deleted putdellog().
73: * Cleaned up diagnostics and error messages. Fixed problem with
74: * mutilated files in case of deletions in 2 files in a single command.
75: * Changed marking of selector from 'D' to DELETE.
76: *
77: * Revision 3.6 83/01/14 15:37:31 wft
78: * Added ignoring of interrupts while new RCS file is renamed;
79: * Avoids deletion of RCS files by interrupts.
80: *
81: * Revision 3.5 82/12/10 21:11:39 wft
82: * Removed unused variables, fixed checking of return code from diff,
83: * introduced variant COMPAT2 for skipping Suffix on -A files.
84: *
85: * Revision 3.4 82/12/04 13:18:20 wft
86: * Replaced getdelta() with gettree(), changed breaklock to update
87: * field lockedby, added some diagnostics.
88: *
89: * Revision 3.3 82/12/03 17:08:04 wft
90: * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
91: * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
92: * fixed -u for missing revno. Disambiguated structure members.
93: *
94: * Revision 3.2 82/10/18 21:05:07 wft
95: * rcs -i now generates a file mode given by the umask minus write permission;
96: * otherwise, rcs keeps the mode, but removes write permission.
97: * I added a check for write error, fixed call to getlogin(), replaced
98: * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
99: * conflicting, long identifiers.
100: *
101: * Revision 3.1 82/10/13 16:11:07 wft
102: * fixed type of variables receiving from getc() (char -> int).
103: */
104:
105:
106: #include <paths.h>
107: #include <sys/types.h>
108: #include <sys/stat.h>
109: #include "rcsbase.h"
110: #ifndef lint
111: static char rcsbaseid[] = RCSBASE;
112: #endif
113:
114: extern FILE * fopen();
115: extern char * bindex();
116: extern int expandsym(); /* get numeric revision name */
117: extern struct hshentry * getnum();
118: extern struct lock * addlock(); /* add a lock */
119: extern char * getid();
120: extern char * getkeyval();
121: extern char * Klog, *Khead, *Kaccess, *Ktext;
122: #ifdef COMPAT2
123: extern char * Ksuffix;
124: #endif
125: extern char * getcaller(); /* get login of caller */
126: extern char * malloc();
127: extern struct hshentry * genrevs();
128: extern struct hshentry * breaklock(); /* remove locks (forward) */
129: extern struct hshentry * findlock(); /* find and remove lock */
130: extern char * checkid(); /* check an identifier */
131: extern char * getfullRCSname(); /* get full path name of RCS file */
132: extern char * mktempfile(); /* temporary file name generator */
133: extern free();
134: extern void catchints();
135: extern void ignoreints();
136: extern int nextc; /* next input character */
137: extern int nerror; /* counter for errors */
138: extern int quietflag; /* diagnoses suppressed if true */
139: extern char curlogmsg[]; /* current log message */
140: extern char * resultfile, *editfile; /* filename for fcopy and fedit */
141: extern FILE *fcopy; /* result file during editing */
142: extern FILE *fedit; /* edit file */
143: extern FILE * finptr; /* RCS input file */
144: extern FILE * frewrite; /* new RCS file */
145: extern int rewriteflag; /* indicates whether input should be*/
146: /* echoed to frewrite */
147:
148: char * newRCSfilename, * diffilename, * cutfilename;
149: char * RCSfilename, * workfilename;
150: extern struct stat RCSstat, workstat; /* file status of RCS and work file */
151: extern int haveRCSstat, haveworkstat;/* status indicators */
152:
153: char accessorlst[strtsize];
154: FILE * fcut; /* temporary file to rebuild delta tree */
155: int oldumask; /* save umask */
156:
157: int initflag, strictlock, strict_selected, textflag;
158: char * textfile, * accessfile;
159: char * caller, numrev[30]; /* caller's login; */
160: struct access * newaccessor, * rmvaccessor, * rplaccessor;
161: struct access *curaccess, *rmaccess;
162: struct hshentry * gendeltas[hshsize];
163:
164: struct Lockrev {
165: char * revno;
166: struct Lockrev * nextrev;
167: };
168:
169: struct Symrev {
170: char * revno;
171: char * ssymbol;
172: int override;
173: struct Symrev * nextsym;
174: };
175:
176: struct Status {
177: char * revno;
178: char * status;
179: struct Status * nextstatus;
180: };
181:
182: struct delrevpair {
183: char * strt;
184: char * end;
185: int code;
186: };
187:
188: struct Lockrev * newlocklst, * rmvlocklst;
189: struct Symrev * assoclst, * lastassoc;
190: struct Status * statelst, * laststate;
191: struct delrevpair * delrev;
192: struct hshentry * cuthead, *cuttail, * delstrt;
193: char branchnum[revlength], * branchsym;
194: struct hshentry branchdummy;
195: char command[80], * commsyml;
196: char * headstate;
197: int lockhead,unlockcaller,chgheadstate,branchflag,;
198: int delaccessflag;
199: enum stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
200:
201:
202: main (argc, argv)
203: int argc;
204: char * argv[];
205: {
206: char *comdusge;
207: int result;
208: struct access *removeaccess(), * getaccessor();
209: struct Lockrev *rmnewlocklst();
210: struct Lockrev *curlock, * rmvlock, *lockpt;
211: struct Status * curstate;
212: struct access *temp, *temptr;
213:
214: nerror = 0;
215: catchints();
216: cmdid = "rcs";
217: quietflag = false;
218: comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
219: rplaccessor = nil; delstrt = nil;
220: accessfile = textfile = caller = nil;
221: branchflag = commentflag = chgheadstate = false;
222: lockhead = false; unlockcaller=false;
223: initflag= textflag = false;
224: strict_selected = 0;
225:
226: caller=getcaller();
227: laststate = statelst = nil;
228: lastassoc = assoclst = nil;
229: curlock = rmvlock = newlocklst = rmvlocklst = nil;
230: curaccess = rmaccess = rmvaccessor = newaccessor = nil;
231: delaccessflag = false;
232:
233: /* preprocessing command options */
234: while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
235: switch ((*argv)[1]) {
236:
237: case 'i': /* initail version */
238: initflag = true;
239: break;
240:
241: case 'b': /* change default branch */
242: if (branchflag)warn("Redfinition of option -b");
243: branchflag= true;
244: branchsym = (*argv)+2;
245: break;
246:
247: case 'c': /* change comment symbol */
248: if (commentflag)warn("Redefinition of option -c");
249: commentflag = true;
250: commsyml = (*argv)+2;
251: break;
252:
253: case 'a': /* add new accessor */
254: if ( (*argv)[2] == '\0') {
255: error("Login name missing after -a");
256: }
257: if ( (temp = getaccessor((*argv)+1)) ) {
258: if ( newaccessor )
259: curaccess->nextaccess = temp->nextaccess;
260: else
261: newaccessor = temp->nextaccess;
262: temp->nextaccess = nil;
263: curaccess = temp;
264: }
265: break;
266:
267: case 'A': /* append access list according to accessfile */
268: if ( (*argv)[2] == '\0') {
269: error("Missing file name after -A");
270: break;
271: }
272: if ( accessfile) warn("Redefinition of option -A");
273: *argv = *argv+2;
274: if( pairfilenames(1, argv, true, false) > 0) {
275: releaselst(newaccessor);
276: newaccessor = curaccess = nil;
277: releaselst(rmvaccessor);
278: rmvaccessor = rmaccess = nil;
279: accessfile = RCSfilename;
280: }
281: else
282: accessfile = nil;
283: break;
284:
285: case 'e': /* remove accessors */
286: if ( (*argv)[2] == '\0' ) {
287: delaccessflag = true;
288: break;
289: }
290: if ( (temp = getaccessor((*argv)+1)) ) {
291: if ( rmvaccessor )
292: rmaccess->nextaccess = temp->nextaccess;
293: else
294: rmvaccessor = temp->nextaccess;
295: temptr = temp->nextaccess;
296: temp->nextaccess = nil;
297: rmaccess = temp;
298: while( temptr ) {
299: newaccessor = removeaccess(temptr,newaccessor,false);
300: temptr = temptr->nextaccess;
301: }
302: curaccess = temp = newaccessor;
303: while( temp){
304: curaccess = temp;
305: temp = temp->nextaccess;
306: }
307: }
308: break;
309:
310: case 'l': /* lock a revision if it is unlocked */
311: if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
312: lockhead = true;
313: break;
314: }
315: lockpt = (struct Lockrev *)malloc(sizeof(struct Lockrev));
316: lockpt->revno = (*argv)+2;
317: lockpt->nextrev = nil;
318: if ( curlock )
319: curlock->nextrev = lockpt;
320: else
321: newlocklst = lockpt;
322: curlock = lockpt;
323: break;
324:
325: case 'u': /* release lock of a locked revision */
326: if ( (*argv)[2] == '\0'){ /* unlock head */
327: unlockcaller=true;
328: break;
329: }
330: lockpt = (struct Lockrev *)malloc(sizeof(struct Lockrev));
331: lockpt->revno = (*argv)+2;
332: lockpt->nextrev = nil;
333: if (rmvlock)
334: rmvlock->nextrev = lockpt;
335: else
336: rmvlocklst = lockpt;
337: rmvlock = lockpt;
338:
339: curlock = rmnewlocklst(lockpt);
340: break;
341:
342: case 'L': /* set strict locking */
343: if (strict_selected++) { /* Already selected L or U? */
344: if (!strictlock) /* Already selected -U? */
345: warn("Option -L overrides -U");
346: }
347: strictlock = true;
348: break;
349:
350: case 'U': /* release strict locking */
351: if (strict_selected++) { /* Already selected L or U? */
352: if (strictlock) /* Already selected -L? */
353: warn("Option -L overrides -U");
354: }
355: else
356: strictlock = false;
357: break;
358:
359: case 'n': /* add new association: error, if name exists */
360: if ( (*argv)[2] == '\0') {
361: error("Missing symbolic name after -n");
362: break;
363: }
364: getassoclst(false, (*argv)+1);
365: break;
366:
367: case 'N': /* add or change association */
368: if ( (*argv)[2] == '\0') {
369: error("Missing symbolic name after -N");
370: break;
371: }
372: getassoclst(true, (*argv)+1);
373: break;
374:
375: case 'o': /* delete revisins */
376: if (delrev) warn("Redefinition of option -o");
377: if ( (*argv)[2] == '\0' ) {
378: error("Missing revision range after -o");
379: break;
380: }
381: getdelrev( (*argv)+1 );
382: break;
383:
384: case 's': /* change state attribute of a revision */
385: if ( (*argv)[2] == '\0') {
386: error("State missing after -s");
387: break;
388: }
389: getstates( (*argv)+1);
390: break;
391:
392: case 't': /* change descriptive text */
393: textflag=true;
394: if ((*argv)[2]!='\0'){
395: if (textfile!=nil)warn("Redefinition of -t option");
396: textfile = (*argv)+2;
397: }
398: break;
399:
400: case 'q':
401: quietflag = true;
402: break;
403: default:
404: faterror("Unknown option: %s\n%s", *argv, comdusge);
405: };
406: } /* end processing of options */
407:
408: if (argc<1) faterror("No input file\n%s", comdusge);
409: if (nerror) { /* exit, if any error in command options */
410: diagnose("%s aborted",cmdid);
411: exit(1);
412: }
413: if (accessfile) /* get replacement for access list */
414: getrplaccess();
415: if (nerror) {
416: diagnose("%s aborted",cmdid);
417: exit(1);
418: }
419:
420: /* now handle all filenames */
421: do {
422: rewriteflag = false;
423: finptr=frewrite=NULL;
424: nerror=0;
425:
426: if ( initflag ) {
427: switch( pairfilenames(argc, argv, false, false) ) {
428: case -1: break; /* not exist; ok */
429: case 0: continue; /* error */
430: case 1: error("file %s exists already", RCSfilename);
431: VOID fclose(finptr);
432: continue;
433: }
434: }
435: else {
436: switch( pairfilenames(argc, argv, true, false) ) {
437: case -1: continue; /* not exist */
438: case 0: continue; /* errors */
439: case 1: break; /* file exists; ok*/
440: }
441: }
442:
443:
444: /* now RCSfilename contains the name of the RCS file, and
445: * workfilename contains the name of the working file.
446: * if !initflag, finptr contains the file descriptor for the
447: * RCS file. The admin node is initialized.
448: */
449:
450: diagnose("RCS file: %s", RCSfilename);
451:
452: if (!trydiraccess(RCSfilename)) continue; /* give up */
453: if (!initflag && !checkaccesslist(caller)) continue; /* give up */
454: if (!trysema(RCSfilename,true)) continue; /* give up */
455:
456: gettree(); /* read in delta tree */
457:
458: /* update admin. node */
459: if (strict_selected) StrictLocks = strictlock;
460: if (commentflag) Comment = commsyml;
461:
462: /* update default branch */
463: if (branchflag && expandsym(branchsym, branchnum)) {
464: if (countnumflds(branchnum)>0) {
465: branchdummy.num=branchnum;
466: Dbranch = &branchdummy;
467: } else
468: Dbranch = nil;
469: }
470:
471: /* update access list */
472: if ( delaccessflag ) AccessList = nil;
473: if ( accessfile ) {
474: temp = rplaccessor;
475: while( temp ) {
476: temptr = temp->nextaccess;
477: if ( addnewaccess(temp) )
478: temp->nextaccess = nil;
479: temp = temptr;
480: }
481: }
482: temp = rmvaccessor;
483: while(temp) { /* remove accessors from accesslist */
484: AccessList = removeaccess(temp, AccessList,true);
485: temp = temp->nextaccess;
486: }
487: temp = newaccessor;
488: while( temp) { /* add new accessors */
489: temptr = temp->nextaccess;
490: if ( addnewaccess( temp ) )
491: temp->nextaccess = nil;
492: temp = temptr;
493: }
494:
495: updateassoc(); /* update association list */
496:
497: updatelocks(); /* update locks */
498:
499: /* update state attribution */
500: if (chgheadstate) {
501: /* change state of default branch or head */
502: if (Dbranch==nil) {
503: if (Head==nil)
504: warn("Can't change states in an empty tree");
505: else Head->state = headstate;
506: } else {
507: rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
508: }
509: }
510: curstate = statelst;
511: while( curstate ) {
512: rcs_setstate(curstate->revno,curstate->status);
513: curstate = curstate->nextstatus;
514: }
515:
516: cuthead = cuttail = nil;
517: if ( delrev && removerevs()) {
518: /* rebuild delta tree if some deltas are deleted */
519: if ( cuttail )
520: VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
521: (char *)nil, gendeltas);
522: buildtree();
523: }
524:
525:
526: /* prepare for rewriting the RCS file */
527: newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
528: oldumask = umask(0222); /* turn off write bits */
529: if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
530: VOID fclose(finptr);
531: error("Can't open file %s",newRCSfilename);
532: continue;
533: }
534: VOID umask(oldumask);
535: putadmin(frewrite);
536: if ( Head )
537: puttree(Head, frewrite);
538: VOID putdesc(initflag,textflag,textfile,quietflag);
539: rewriteflag = false;
540:
541: if ( Head) {
542: if (!delrev) {
543: /* no revision deleted */
544: fastcopy(finptr,frewrite);
545: } else {
546: if ( cuttail )
547: buildeltatext(gendeltas);
548: else
549: scanlogtext((struct hshentry *)nil,empty);
550: /* copy rest of delta text nodes that are not deleted */
551: }
552: }
553: ffclose(frewrite); frewrite = NULL;
554: if ( ! nerror ) { /* move temporary file to RCS file if no error */
555: ignoreints(); /* ignore interrupts */
556: if(rename(newRCSfilename,RCSfilename)<0) {
557: error("Can't create RCS file %s; saved in %s",
558: RCSfilename, newRCSfilename);
559: newRCSfilename[0] = '\0'; /* avoid deletion by cleanup */
560: restoreints();
561: VOID cleanup();
562: break;
563: }
564: newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
565: /* update mode */
566: result=0;
567: if (!initflag) /* preserve mode bits */
568: result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
569: elsif (haveworkstat==0) /* initialization, and work file exists */
570: result=chmod(RCSfilename,workstat.st_mode & ~0222);
571: if (result<0) warn("Can't set mode of %s",RCSfilename);
572:
573: restoreints(); /* catch them all again */
574: diagnose("done");
575: } else {
576: diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
577: }
578: } while (cleanup(),
579: ++argv, --argc >=1);
580:
581: exit(nerror!=0);
582: } /* end of main (rcs) */
583:
584:
585:
586: getassoclst(flag, sp)
587: int flag;
588: char * sp;
589: /* Function: associate a symbolic name to a revision or branch, */
590: /* and store in assoclst */
591:
592: {
593: struct Symrev * pt;
594: char * temp, *temp2;
595: int c;
596:
597: while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n') ;
598: temp = sp;
599: temp2=checkid(sp, ':'); /* check for invalid symbolic name */
600: sp = temp2; c = *sp; *sp = '\0';
601: while( c == ' ' || c == '\t' || c == '\n') c = *++sp;
602:
603: if ( c != ':' && c != '\0') {
604: error("Invalid string %s after option -n or -N",sp);
605: return;
606: }
607:
608: pt = (struct Symrev *)malloc(sizeof(struct Symrev));
609: pt->ssymbol = temp;
610: pt->override = flag;
611: if (c == '\0') /* delete symbol */
612: pt->revno = nil;
613: else {
614: while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ;
615: if ( c == '\0' )
616: pt->revno = nil;
617: else
618: pt->revno = sp;
619: }
620: pt->nextsym = nil;
621: if (lastassoc)
622: lastassoc->nextsym = pt;
623: else
624: assoclst = pt;
625: lastassoc = pt;
626: return;
627: }
628:
629:
630:
631: struct access * getaccessor( sp)
632: char *sp;
633: /* Function: get the accessor list of options -e and -a, */
634: /* and store in curpt */
635:
636:
637: {
638: struct access * curpt, * pt, *pre;
639: char *temp;
640: register c;
641:
642: while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
643: if ( c == '\0') {
644: error("Missing login name after option -a or -e");
645: return nil;
646: }
647:
648: curpt = pt = nil;
649: while( c != '\0') {
650: temp=checkid(sp,',');
651: pt = (struct access *)malloc(sizeof(struct access));
652: pt->login = sp;
653: if ( curpt )
654: pre->nextaccess = pt;
655: else
656: curpt = pt;
657: pt->nextaccess = curpt;
658: pre = pt;
659: sp = temp; c = *sp; *sp = '\0';
660: while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
661: }
662: return pt;
663: }
664:
665:
666:
667: getstates(sp)
668: char *sp;
669: /* Function: get one state attribute and the corresponding */
670: /* revision and store in statelst */
671:
672: {
673: char *temp, *temp2;
674: struct Status *pt;
675: register c;
676:
677: while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n') ;
678: temp = sp;
679: temp2=checkid(sp,':'); /* check for invalid state attribute */
680: sp = temp2; c = *sp; *sp = '\0';
681: while( c == ' ' || c == '\t' || c == '\n' ) c = *++sp;
682:
683: if ( c == '\0' ) { /* change state of def. branch or Head */
684: chgheadstate = true;
685: headstate = temp;
686: return;
687: }
688: else if ( c != ':' ) {
689: error("Missing ':' after state in option -s");
690: return;
691: }
692:
693: while( (c = *++sp) == ' ' || c == '\t' || c == '\n') ;
694: pt = (struct Status *)malloc(sizeof(struct Status));
695: pt->status = temp;
696: pt->revno = sp;
697: pt->nextstatus = nil;
698: if (laststate)
699: laststate->nextstatus = pt;
700: else
701: statelst = pt;
702: laststate = pt;
703: }
704:
705:
706:
707: getrplaccess()
708: /* Function : get the accesslist of the 'accessfile' */
709: /* and place in rplaccessor */
710: {
711: register char *id, *nextp;
712: struct access *newaccess, *curaccess;
713:
714: if ( (finptr=fopen(accessfile, "r")) == NULL) {
715: faterror("Can't open file %s", accessfile);
716: }
717: Lexinit();
718: nextp = &accessorlst[0];
719:
720: if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
721: VOID getnum();
722: if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
723:
724: #ifdef COMPAT2
725: /* read suffix. Only in release 2 format */
726: if (getkey(Ksuffix)) {
727: if (nexttok==STRING) {
728: readstring(); nextlex(); /*through away the suffix*/
729: } elsif(nexttok==ID) {
730: nextlex();
731: }
732: if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
733: }
734: #endif
735:
736: if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
737: curaccess = nil;
738: while( id =getid() ) {
739: newaccess = (struct access *)malloc(sizeof(struct access));
740: newaccess->login = nextp;
741: newaccess->nextaccess = nil;
742: while( ( *nextp++ = *id++) != '\0') ;
743: if ( curaccess )
744: curaccess->nextaccess = newaccess;
745: else
746: rplaccessor = newaccess;
747: curaccess = newaccess;
748: }
749: if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
750: return;
751: }
752:
753:
754:
755: getdelrev(sp)
756: char *sp;
757: /* Function: get revision range or branch to be deleted, */
758: /* and place in delrev */
759: {
760: int c;
761: struct delrevpair *pt;
762:
763: if (delrev) free((char *)delrev);
764:
765: pt = (struct delrevpair *)malloc(sizeof(struct delrevpair));
766: while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
767:
768: if ( c == '<' || c == '-' ) { /* -o -rev or <rev */
769: while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
770: pt->strt = sp; pt->code = 1;
771: while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
772: *sp = '\0';
773: pt->end = nil; delrev = pt;
774: return;
775: }
776: else {
777: pt->strt = sp;
778: while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
779: && c != '-' && c != '<' ) c = *++sp;
780: *sp = '\0';
781: while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp;
782: if ( c == '\0' ) { /* -o rev or branch */
783: pt->end = nil; pt->code = 0;
784: delrev = pt;
785: return;
786: }
787: if ( c != '-' && c != '<') {
788: faterror("Invalid range %s %s after -o", pt->strt, sp);
789: free((char *)pt);
790: return;
791: }
792: while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ;
793: if ( c == '\0') { /* -o rev- or rev< */
794: pt->end = nil; pt->code = 2;
795: delrev = pt;
796: return;
797: }
798: }
799: /* -o rev1-rev2 or rev1<rev2 */
800: pt->end = sp; pt->code = 3; delrev = pt;
801: while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
802: *sp = '\0';
803: }
804:
805:
806:
807:
808: scanlogtext(delta,func)
809: struct hshentry * delta; enum stringwork func;
810: /* Function: Scans delta text nodes up to and including the one given
811: * by delta, or up to last one present, if delta==nil.
812: * For the one given by delta (if delta!=nil), the log message is saved into
813: * curlogmsg and the text is processed according to parameter func.
814: * Assumes the initial lexeme must be read in first.
815: * Does not advance nexttok after it is finished, except if delta==nil.
816: */
817: { struct hshentry * nextdelta;
818:
819: do {
820: rewriteflag = false;
821: nextlex();
822: if (!(nextdelta=getnum())) {
823: if(delta)
824: faterror("Can't find delta for revision %s", delta->num);
825: else return; /* no more delta text nodes */
826: }
827: if ( nextdelta->selector != DELETE) {
828: rewriteflag = true;
829: VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
830: }
831: if (!getkey(Klog) || nexttok!=STRING)
832: serror("Missing log entry");
833: elsif (delta==nextdelta) {
834: VOID savestring(curlogmsg,logsize);
835: delta->log=curlogmsg;
836: } else {readstring();
837: if (delta!=nil) delta->log="";
838: }
839: nextlex();
840: if (!getkey(Ktext) || nexttok!=STRING)
841: fatserror("Missing delta text");
842:
843: if(delta==nextdelta)
844: /* got the one we're looking for */
845: switch (func) {
846: case copy: copystring();
847: break;
848: case edit: editstring((struct hshentry *)nil);
849: break;
850: default: faterror("Wrong scanlogtext");
851: }
852: else readstring(); /* skip over it */
853:
854: } while (delta!=nextdelta);
855: }
856:
857:
858:
859: releaselst(sourcelst)
860: struct access * sourcelst;
861: /* Function: release the storages whose address are in sourcelst */
862:
863: {
864: struct access * pt;
865:
866: pt = sourcelst;
867: while(pt) {
868: free((char *)pt);
869: pt = pt->nextaccess;
870: }
871: }
872:
873:
874:
875: struct Lockrev * rmnewlocklst(which)
876: struct Lockrev * which;
877: /* Function: remove lock to revision which->revno form newlocklst */
878:
879: {
880: struct Lockrev * pt, *pre;
881:
882: while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
883: free((char *)newlocklst);
884: newlocklst = newlocklst->nextrev;
885: }
886:
887: pt = pre = newlocklst;
888: while( pt ) {
889: if ( ! strcmp(pt->revno, which->revno) ) {
890: free((char *)pt);
891: pt = pt->nextrev;
892: pre->nextrev = pt;
893: }
894: else {
895: pre = pt;
896: pt = pt->nextrev;
897: }
898: }
899: return pre;
900: }
901:
902:
903:
904: struct access * removeaccess( who, sourcelst,flag)
905: struct access * who, * sourcelst;
906: int flag;
907: /* Function: remove the accessor-- who from sourcelst */
908:
909: {
910: struct access *pt, *pre;
911:
912: pt = sourcelst;
913: while( pt && (! strcmp(who->login, pt->login) )) {
914: free((char *)pt);
915: flag = false;
916: pt = pt->nextaccess;
917: }
918: pre = sourcelst = pt;
919: while( pt ) {
920: if ( ! strcmp(who->login, pt->login) ) {
921: free((char *)pt);
922: flag = false;
923: pt = pt->nextaccess;
924: pre->nextaccess = pt;
925: }
926: else {
927: pre = pt;
928: pt = pt->nextaccess;
929: }
930: }
931: if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
932: return sourcelst;
933: }
934:
935:
936:
937: int addnewaccess( who )
938: struct access * who;
939: /* Function: add new accessor-- who into AccessList */
940:
941: {
942: struct access *pt, *pre;
943:
944: pre = pt = AccessList;
945:
946: while( pt ) {
947: if ( strcmp( who->login, pt->login) ) {
948: pre = pt;
949: pt = pt->nextaccess;
950: }
951: else
952: return 0;
953: }
954: if ( pre == pt )
955: AccessList = who;
956: else
957: pre->nextaccess = who;
958: return 1;
959: }
960:
961:
962: sendmail(Delta, who)
963: char * Delta, *who;
964: /* Function: mail to who, informing him that his lock on delta was
965: * broken by caller. Ask first whether to go ahead. Return false on
966: * error or if user decides not to break the lock.
967: */
968: {
969: char * messagefile;
970: int old1, old2, c, response;
971: FILE * mailmess;
972:
973:
974: VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
975: VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
976: response=c=getchar();
977: while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
978: if (response=='\n'||response=='n'||response=='N') return false;
979:
980: /* go ahead with breaking */
981: messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
982: if ( (mailmess = fopen(messagefile, "w")) == NULL) {
983: faterror("Can't open file %s", messagefile);
984: }
985:
986: VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
987: VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
988: VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
989: VOID fputs("State the reason for breaking the lock:\n", stderr);
990: VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
991:
992: old1 = '\n'; old2 = ' ';
993: for (; ;) {
994: c = getchar();
995: if ( c == EOF ) {
996: VOID putc('\n',stderr);
997: VOID fprintf(mailmess, "%c\n", old1);
998: break;
999: }
1000: else if ( c == '\n' && old1 == '.' && old2 == '\n')
1001: break;
1002: else {
1003: VOID fputc( old1, mailmess);
1004: old2 = old1; old1 = c;
1005: if (c== '\n') VOID fputs(">> ", stderr);
1006: }
1007: }
1008: ffclose(mailmess);
1009:
1010: #ifdef _PATH_SENDMAIL
1011: VOID sprintf(command, "%s %s < %s", _PATH_SENDMAIL, who,messagefile);
1012: #else
1013: # ifdef DELIVERMAIL
1014: VOID sprintf(command, "/etc/delivermail -w %s < %s",who,messagefile);
1015: # else
1016: VOID sprintf(command, "/bin/mail %s < %s",who,messagefile);
1017: # endif DELIVERMAIL
1018: #endif _PATH_SENDMAIL
1019:
1020: VOID system(command);
1021: /* ignore the exit status, even if delivermail unsuccessful */
1022: VOID unlink(messagefile);
1023: return(true);
1024: }
1025:
1026:
1027:
1028: struct hshentry * breaklock(who,delta)
1029: char * who; struct hshentry * delta;
1030: /* function: Finds the lock held by who on delta,
1031: * removes it, and returns a pointer to the delta.
1032: * Sends mail if a lock different from the caller's is broken.
1033: * Prints an error message and returns nil if there is no such lock or error.
1034: */
1035: {
1036: register struct lock * next, * trail;
1037: char * num;
1038: struct lock dummy;
1039: int whor, numr;
1040:
1041: num=delta->num;
1042: dummy.nextlock=next=Locks;
1043: trail = &dummy;
1044: while (next!=nil) {
1045: if (num != nil)
1046: numr = strcmp(num, next->delta->num);
1047:
1048: whor=strcmp(who,next->login);
1049: if (whor==0 && numr==0) break; /* exact match */
1050: if (numr==0 && whor !=0) {
1051: if (!sendmail( num, next->login)){
1052: diagnose("%s still locked by %s",num,next->login);
1053: return nil;
1054: } else break; /* continue after loop */
1055: }
1056: trail=next;
1057: next=next->nextlock;
1058: }
1059: if (next!=nil) {
1060: /*found one */
1061: diagnose("%s unlocked",next->delta->num);
1062: trail->nextlock=next->nextlock;
1063: next->delta->lockedby=nil;
1064: Locks=dummy.nextlock;
1065: return next->delta;
1066: } else {
1067: error("no lock set on revision %s", num);
1068: return nil;
1069: }
1070: }
1071:
1072:
1073:
1074: struct hshentry *searchcutpt(object, length, store)
1075: char * object;
1076: int length;
1077: struct hshentry * * store;
1078: /* Function: Search store and return entry with number being object. */
1079: /* cuttail = nil, if the entry is Head; otherwise, cuttail */
1080: /* is the entry point to the one with number being object */
1081:
1082: {
1083: while( compartial( (*store++)->num, object, length) ) ;
1084: store--;
1085:
1086: if ( *store == Head)
1087: cuthead = nil;
1088: else
1089: cuthead = *(store -1);
1090: return *store;
1091: }
1092:
1093:
1094:
1095: int branchpoint(strt, tail)
1096: struct hshentry *strt, *tail;
1097: /* Function: check whether the deltas between strt and tail */
1098: /* are locked or branch point, return 1 if any is */
1099: /* locked or branch point; otherwise, return 0 and */
1100: /* mark DELETE on selector */
1101:
1102: {
1103: struct hshentry *pt;
1104: struct lock *lockpt;
1105: int flag;
1106:
1107:
1108: pt = strt;
1109: flag = false;
1110: while( pt != tail) {
1111: if ( pt->branches ){ /* a branch point */
1112: flag = true;
1113: error("Can't remove branch point %s", pt->num);
1114: }
1115: lockpt = Locks;
1116: while(lockpt && lockpt->delta != pt)
1117: lockpt = lockpt->nextlock;
1118: if ( lockpt ) {
1119: flag = true;
1120: error("Can't remove locked revision %s",pt->num);
1121: }
1122: pt = pt->next;
1123: }
1124:
1125: if ( ! flag ) {
1126: pt = strt;
1127: while( pt != tail ) {
1128: pt->selector = DELETE;
1129: diagnose("deleting revision %s ",pt->num);
1130: pt = pt->next;
1131: }
1132: }
1133: return flag;
1134: }
1135:
1136:
1137:
1138: removerevs()
1139: /* Function: get the revision range to be removed, and place the */
1140: /* first revision removed in delstrt, the revision before */
1141: /* delstrt in cuthead( nil, if delstrt is head), and the */
1142: /* revision after the last removed revision in cuttail(nil */
1143: /* if the last is a leaf */
1144:
1145: {
1146: struct hshentry *target, *target2, * temp, *searchcutpt();
1147: int length, flag;
1148:
1149: flag = false;
1150: if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
1151: target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
1152: if ( ! target ) return 0;
1153: if ( cmpnum(target->num, &numrev[0]) ) flag = true;
1154: length = countnumflds( &numrev[0] );
1155:
1156: if ( delrev->code == 0 ) { /* -o rev or -o branch */
1157: if ( length % 2)
1158: temp=searchcutpt(target->num,length+1,gendeltas);
1159: else if (flag) {
1160: error("Revision %s does not exist", &numrev[0]);
1161: return 0;
1162: }
1163: else
1164: temp = searchcutpt(&numrev[0],length,gendeltas);
1165: cuttail = target->next;
1166: if ( branchpoint(temp, cuttail) ) {
1167: cuttail = nil;
1168: return 0;
1169: }
1170: delstrt = temp; /* first revision to be removed */
1171: return 1;
1172: }
1173:
1174: if ( length % 2 ) { /* invalid branch after -o */
1175: error("Invalid branch range %s after -o", &numrev[0]);
1176: return 0;
1177: }
1178:
1179: if ( delrev->code == 1 ) { /* -o -rev */
1180: if ( length > 2 ) {
1181: temp = searchcutpt( target->num, length-1, gendeltas);
1182: cuttail = target->next;
1183: }
1184: else {
1185: temp = searchcutpt(target->num, length, gendeltas);
1186: cuttail = target;
1187: while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
1188: cuttail = cuttail->next;
1189: }
1190: if ( branchpoint(temp, cuttail) ){
1191: cuttail = nil;
1192: return 0;
1193: }
1194: delstrt = temp;
1195: return 1;
1196: }
1197:
1198: if ( delrev->code == 2 ) { /* -o rev- */
1199: if ( length == 2 ) {
1200: temp = searchcutpt(target->num, 1,gendeltas);
1201: if ( flag)
1202: cuttail = target;
1203: else
1204: cuttail = target->next;
1205: }
1206: else {
1207: if ( flag){
1208: cuthead = target;
1209: if ( !(temp = target->next) ) return 0;
1210: }
1211: else
1212: temp = searchcutpt(target->num, length, gendeltas);
1213: getbranchno(temp->num, &numrev[0]); /* get branch number */
1214: target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
1215: }
1216: if ( branchpoint( temp, cuttail ) ) {
1217: cuttail = nil;
1218: return 0;
1219: }
1220: delstrt = temp;
1221: return 1;
1222: }
1223:
1224: /* -o rev1-rev2 */
1225: if ( ! expandsym(delrev->end, &numrev[0]) ) return 0;
1226: if ( length != countnumflds( &numrev[0] ) ) {
1227: error("Invalid revision range %s-%s", target->num, &numrev[0]);
1228: return 0;
1229: }
1230: if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
1231: error("Invalid revision range %s-%s", target->num, &numrev[0]);
1232: return 0;
1233: }
1234:
1235: target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
1236: if ( ! target2 ) return 0;
1237:
1238: if ( length > 2) { /* delete revisions on branches */
1239: if ( cmpnum(target->num, target2->num) > 0) {
1240: if ( cmpnum(target2->num, &numrev[0]) )
1241: flag = true;
1242: else
1243: flag = false;
1244: temp = target;
1245: target = target2;
1246: target2 = temp;
1247: }
1248: if ( flag ) {
1249: if ( ! cmpnum(target->num, target2->num) ) {
1250: error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
1251: return 0;
1252: }
1253: cuthead = target;
1254: temp = target->next;
1255: }
1256: else
1257: temp = searchcutpt(target->num, length, gendeltas);
1258: cuttail = target2->next;
1259: }
1260: else { /* delete revisions on trunk */
1261: if ( cmpnum( target->num, target2->num) < 0 ) {
1262: temp = target;
1263: target = target2;
1264: target2 = temp;
1265: }
1266: else
1267: if ( cmpnum(target2->num, &numrev[0]) )
1268: flag = true;
1269: else
1270: flag = false;
1271: if ( flag ) {
1272: if ( ! cmpnum(target->num, target2->num) ) {
1273: error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
1274: return 0;
1275: }
1276: cuttail = target2;
1277: }
1278: else
1279: cuttail = target2->next;
1280: temp = searchcutpt(target->num, length, gendeltas);
1281: }
1282: if ( branchpoint(temp, cuttail) ) {
1283: cuttail = nil;
1284: return 0;
1285: }
1286: delstrt = temp;
1287: return 1;
1288: }
1289:
1290:
1291:
1292: updateassoc()
1293: /* Function: add or delete(if revno is nil) association */
1294: /* which is stored in assoclst */
1295:
1296: {
1297: struct Symrev * curassoc;
1298: struct assoc * pre, * pt;
1299: struct hshentry * target;
1300:
1301: /* add new associations */
1302: curassoc = assoclst;
1303: while( curassoc ) {
1304: if ( curassoc->revno == nil ) { /* delete symbol */
1305: pre = pt = Symbols;
1306: while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
1307: pre = pt;
1308: pt = pt->nextassoc;
1309: }
1310: if ( pt )
1311: if ( pre == pt )
1312: Symbols = pt->nextassoc;
1313: else
1314: pre->nextassoc = pt->nextassoc;
1315: else
1316: warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
1317: }
1318: else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
1319: /* add symbol */
1320: target = (struct hshentry *) malloc(sizeof(struct hshentry));
1321: target->num = &numrev[0];
1322: VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
1323: }
1324: curassoc = curassoc->nextsym;
1325: }
1326:
1327: }
1328:
1329:
1330:
1331: updatelocks()
1332: /* Function: remove lock for caller or first lock if unlockcaller==true;
1333: * remove locks which are stored in rmvlocklst,
1334: * add new locks which are stored in newlocklst,
1335: * add lock for Dbranch or Head if lockhead==true.
1336: */
1337: {
1338: struct hshentry *target;
1339: struct Lockrev *lockpt;
1340:
1341: if(unlockcaller == true) { /* find lock for caller */
1342: if ( Head ) {
1343: if (Locks) {
1344: target=findlock(caller,true);
1345: if (target==nil) {
1346: breaklock(caller, Locks->delta); /* remove most recent lock */
1347: } else {
1348: diagnose("%s unlocked",target->num);
1349: }
1350: } else {
1351: warn("There are no locks set.");
1352: }
1353: } else {
1354: warn("Can't unlock an empty tree");
1355: }
1356: }
1357:
1358: /* remove locks which are stored in rmvlocklst */
1359: lockpt = rmvlocklst;
1360: while( lockpt ) {
1361: if (expandsym(lockpt->revno, numrev)) {
1362: target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
1363: if ( target )
1364: if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
1365: error("Can't unlock nonexisting revision %s",lockpt->revno);
1366: else
1367: breaklock(caller, target);
1368: /* breaklock does its own diagnose */
1369: }
1370: lockpt = lockpt->nextrev;
1371: }
1372:
1373: /* add new locks which stored in newlocklst */
1374: lockpt = newlocklst;
1375: while( lockpt ) {
1376: setlock(lockpt->revno,caller);
1377: lockpt = lockpt->nextrev;
1378: }
1379:
1380: if ( lockhead == true) { /* lock default branch or head */
1381: if (Dbranch) {
1382: setlock(Dbranch->num,caller);
1383: } elsif ( Head) {
1384: if (addlock(Head, caller))
1385: diagnose("%s locked",Head->num);
1386: } else {
1387: warn("Can't lock an empty tree");
1388: }
1389: }
1390:
1391: }
1392:
1393:
1394:
1395: setlock(rev,who)
1396: char * rev, * who;
1397: /* Function: Given a revision or branch number, finds the correponding
1398: * delta and locks it for who.
1399: */
1400: {
1401: struct lock *lpt;
1402: struct hshentry *target;
1403:
1404: if (expandsym(rev, &numrev[0]) ){
1405: target = genrevs(&numrev[0],(char *) nil,(char *) nil,
1406: (char *)nil, gendeltas);
1407: if ( target )
1408: if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
1409: error("Can't lock nonexisting revision %s",numrev);
1410: else
1411: if(lpt=addlock(target, who))
1412: diagnose("%s locked",lpt->delta->num);
1413: }
1414: }
1415:
1416:
1417:
1418: rcs_setstate(rev,status)
1419: char * rev, * status;
1420: /* Function: Given a revision or branch number, finds the corresponding delta
1421: * and sets its state to status.
1422: */
1423: {
1424: struct hshentry *target;
1425:
1426: if ( expandsym(rev, &numrev[0]) ) {
1427: target = genrevs(&numrev[0],(char *) nil, (char *)nil,
1428: (char *) nil, gendeltas);
1429: if ( target )
1430: if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
1431: error("Can't set state of nonexisting revision %s to %s",
1432: numrev,status);
1433: else
1434: target->state = status;
1435: }
1436: }
1437:
1438:
1439:
1440:
1441:
1442: buildeltatext(deltas)
1443: struct hshentry ** deltas;
1444: /* Function: put the delta text on frewrite and make necessary */
1445: /* change to delta text */
1446: {
1447: int i, c, exit_stats;
1448:
1449: cuttail->selector = DELETE;
1450: initeditfiles("/tmp/");
1451: scanlogtext(deltas[0], copy);
1452: i = 1;
1453: if ( cuthead ) {
1454: cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
1455: if ( (fcut = fopen(cutfilename, "w")) == NULL) {
1456: faterror("Can't open temporary file %s", cutfilename);
1457: }
1458:
1459: while( deltas[i-1] != cuthead ) {
1460: scanlogtext(deltas[i++], edit);
1461: }
1462:
1463: finishedit((struct hshentry *)nil); rewind(fcopy);
1464: while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
1465: swapeditfiles(false);
1466: ffclose(fcut);
1467: }
1468:
1469: while( deltas[i-1] != cuttail)
1470: scanlogtext(deltas[i++], edit);
1471: finishedit((struct hshentry *)nil); ffclose(fcopy);
1472:
1473: if ( cuthead ) {
1474: diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
1475: VOID sprintf(command, "%s -n %s %s > %s", DIFF,cutfilename, resultfile, diffilename);
1476: exit_stats = system (command);
1477: if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
1478: faterror ("diff failed");
1479: if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
1480: }
1481: else
1482: if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
1483:
1484: scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
1485: }
1486:
1487:
1488:
1489: buildtree()
1490: /* Function: actually removes revisions whose selector field */
1491: /* is DELETE, and rebuilds the linkage of deltas. */
1492: /* asks for reconfirmation if deleting last revision*/
1493: {
1494: int c, response;
1495:
1496: struct hshentry * Delta;
1497: struct branchhead *pt, *pre;
1498:
1499: if ( cuthead )
1500: if ( cuthead->next == delstrt )
1501: cuthead->next = cuttail;
1502: else {
1503: pre = pt = cuthead->branches;
1504: while( pt && pt->hsh != delstrt ) {
1505: pre = pt;
1506: pt = pt->nextbranch;
1507: }
1508: if ( cuttail )
1509: pt->hsh = cuttail;
1510: else if ( pt == pre )
1511: cuthead->branches = pt->nextbranch;
1512: else
1513: pre->nextbranch = pt->nextbranch;
1514: }
1515: else {
1516: if ( cuttail == nil && !quietflag) {
1517: VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
1518: c = response = getchar();
1519: while( c != EOF && c != '\n') c = getchar();
1520: if ( response != 'y' && response != 'Y') {
1521: diagnose("No revision deleted");
1522: Delta = delstrt;
1523: while( Delta) {
1524: Delta->selector = 'S';
1525: Delta = Delta->next;
1526: }
1527: return;
1528: }
1529: }
1530: Head = cuttail;
1531: }
1532: return;
1533: }