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