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