|
|
1.1 root 1: /*
2: * RCS checkin operation
3: */
4: #ifndef lint
5: static char rcsid[]=
6: "$Header: /usr/src/local/bin/rcs/src/RCS/ci.c,v 4.6 87/12/18 11:34:41 narten Exp $ Purdue CS";
7: #endif
8: /*******************************************************************
9: * check revisions into RCS files
10: *******************************************************************
11: *
12: * Copyright (C) 1982 by Walter F. Tichy
13: * Purdue University
14: * Computer Science Department
15: * West Lafayette, IN 47907
16: *
17: * All rights reserved. No part of this software may be sold or distributed
18: * in any form or by any means without the prior written permission of the
19: * author.
20: * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
21: */
22:
23:
24:
25: /* $Log: ci.c,v $
26: * Revision 4.6 87/12/18 11:34:41 narten
27: * lint cleanups (from Guy Harris)
28: *
29: * Revision 4.5 87/10/18 10:18:48 narten
30: * Updating version numbers. Changes relative to revision 1.1 are actually
31: * relative to 4.3
32: *
33: * Revision 1.3 87/09/24 13:57:19 narten
34: * Sources now pass through lint (if you ignore printf/sprintf/fprintf
35: * warnings)
36: *
37: * Revision 1.2 87/03/27 14:21:33 jenkins
38: * Port to suns
39: *
40: * Revision 1.1 84/01/23 14:49:54 kcs
41: * Initial revision
42: *
43: * Revision 4.3 83/12/15 12:28:54 wft
44: * ci -u and ci -l now set mode of working file properly.
45: *
46: * Revision 4.2 83/12/05 13:40:54 wft
47: * Merged with 3.9.1.1: added calls to clearerr(stdin).
48: * made rewriteflag external.
49: *
50: * Revision 4.1 83/05/10 17:03:06 wft
51: * Added option -d and -w, and updated assingment of date, etc. to new delta.
52: * Added handling of default branches.
53: * Option -k generates std. log message; fixed undef. pointer in reading of log.
54: * Replaced getlock() with findlock(), link--unlink with rename(),
55: * getpwuid() with getcaller().
56: * Moved all revision number generation to new routine addelta().
57: * Removed calls to stat(); now done by pairfilenames().
58: * Changed most calls to catchints() with restoreints().
59: * Directed all interactive messages to stderr.
60: *
61: * Revision 3.9.1.1 83/10/19 04:21:03 lepreau
62: * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
63: *
64: * Revision 3.9 83/02/15 15:25:44 wft
65: * 4.2 prerelease
66: *
67: * Revision 3.9 83/02/15 15:25:44 wft
68: * Added call to fastcopy() to copy remainder of RCS file.
69: *
70: * Revision 3.8 83/01/14 15:34:05 wft
71: * Added ignoring of interrupts while new RCS file is renamed;
72: * Avoids deletion of RCS files by interrupts.
73: *
74: * Revision 3.7 82/12/10 16:09:20 wft
75: * Corrected checking of return code from diff.
76: *
77: * Revision 3.6 82/12/08 21:34:49 wft
78: * Using DATEFORM to prepare date of checked-in revision;
79: * Fixed return from addbranch().
80: *
81: * Revision 3.5 82/12/04 18:32:42 wft
82: * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
83: * field lockedby in removelock(), moved getlogmsg() before calling diff.
84: *
85: * Revision 3.4 82/12/02 13:27:13 wft
86: * added option -k.
87: *
88: * Revision 3.3 82/11/28 20:53:31 wft
89: * Added mustcheckin() to check for redundant checkins.
90: * Added xpandfile() to do keyword expansion for -u and -l;
91: * -m appends linefeed to log message if necessary.
92: * getlogmsg() suppresses prompt if stdin is not a terminal.
93: * Replaced keeplock with lockflag, fclose() with ffclose(),
94: * %02d with %.2d, getlogin() with getpwuid().
95: *
96: * Revision 3.2 82/10/18 20:57:23 wft
97: * An RCS file inherits its mode during the first ci from the working file,
98: * otherwise it stays the same, except that write permission is removed.
99: * Fixed ci -l, added ci -u (both do an implicit co after the ci).
100: * Fixed call to getlogin(), added call to getfullRCSname(), added check
101: * for write error.
102: * Changed conflicting identifiers.
103: *
104: * Revision 3.1 82/10/13 16:04:59 wft
105: * fixed type of variables receiving from getc() (char -> int).
106: * added include file dbm.h for getting BYTESIZ. This is used
107: * to check the return code from diff portably.
108: */
109:
110: #include "rcsbase.h"
111: #ifndef lint
112: static char rcsbaseid[] = RCSBASE;
113: #endif
114: #include <sys/types.h>
115: #include <sys/stat.h>
116: #include "time.h"
117:
118: extern int rename(); /*rename files */
119: extern char * getcaller(); /*login of caller */
120: extern struct hshentry * genrevs(); /*generate delta numbers */
121: extern int nextc; /*next input character */
122: extern quietflag; /*suppresses diagnostics if true */
123: extern int nerror; /*counter for errors */
124: extern char * buildrevision(); /*constructs desired revision */
125: extern char * checkid(); /*check identifiers */
126: extern int partime(); /*parse free-format date/time */
127: extern long maketime(); /*convert parsed time to unix time. */
128: extern long time(); /*get date and time */
129: extern struct tm * localtime(); /*convert unixtime into tm-structure */
130: extern char * getdate(); /*formates current date (forward) */
131: extern char * mktempfile(); /*temporary file name generator */
132: extern struct lock * addlock(); /*adds a new lock */
133: extern char * getlogmsg(); /*obtains log message; forward */
134: extern struct hshentry * removelock(); /*finds a caller's lock (forward) */
135: extern struct hshentry * findlock(); /*finds a lock */
136: extern char * xpandfile(); /*perform keyword expansion; forward */
137:
138: extern char prevauthor[];
139: extern char prevdate[];
140: extern char prevrev[];
141: extern char prevstate [];
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;
148: char * RCSfilename,*workfilename,*expfilename,*newworkfilename;
149: extern struct stat RCSstat, workstat; /* file status of RCS and work file */
150: extern int haveRCSstat, haveworkstat;/* status indicators */
151:
152:
153: int copyflag; /* indicates whether a string should be copied into memory*/
154:
155: char * rev, * state, *msg;
156:
157: int initflag, rcsinitflag;
158: int lockflag, keepworkingfile,keepflag;
159: int forceciflag; /* forces check in */
160: int symrebindflag; char * symbol;
161: int textflag; char * textfile;
162: char * caller; /* caller's login; */
163: char * author; /* alternate author for -w option */
164: char altdate[datelength]; /* alternate date for -d */
165: struct hshentry * targetdelta; /* old delta to be generated */
166: char * olddeltanum; /* number of old delta */
167: struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated */
168: char newdelnum[revlength]; /* holds new revision number */
169: int newdnumlength; /* actual length of new rev. num. */
170: char branchpointnum[revlength]; /* number of branchpoint */
171: struct hshentry newdelta; /* new delta to be inserted */
172: struct branchhead newbranch; /* new branch to be inserted */
173: char logmsg[logsize]; /* buffer for log message */
174:
175: main (argc, argv)
176: int argc;
177: char * argv[];
178: {
179: char * nametest;
180: char * cmdusage; /* holds command format */
181: char command[NCPPN+50]; /* holds diff commands */
182: int msglen; /* length of message given by -m */
183: int exit_stats; /* return code for system() calls */
184: int newRCSmode; /* mode for RCS file */
185: long unixtime;
186: struct tm parseddate, *ftm;
187:
188: catchints();
189: cmdid = "ci";
190: cmdusage = "command format:\nci -r[rev] -l[rev] -u[rev] -f[rev] -k[rev] -q[rev] -mmsg -nname -Nname -sstate -t[txtfile] file ...";
191: rev = state = msg = symbol = textfile = nil;
192: initflag= rcsinitflag= symrebindflag= textflag= quietflag= false;
193: forceciflag= lockflag= keepworkingfile= keepflag= false;
194: caller = getcaller(); author = nil; /* author may be reset by -w */
195: altdate[0]= '\0'; /* empty alternate date for -d */
196:
197: while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
198: switch ((*argv)[1]) {
199:
200: case 'r':
201: lockflag=false;
202: revno: if ((*argv)[2]!='\0') {
203: if (rev!=nil) warn("Redefinition of revision number");
204: rev = (*argv)+2;
205: }
206: break;
207:
208: case 'l':
209: keepworkingfile=lockflag=true;
210: goto revno;
211:
212: case 'u':
213: keepworkingfile=true; lockflag=false;
214: goto revno;
215:
216: case 'q':
217: quietflag=true;
218: goto revno;
219:
220: case 'f':
221: forceciflag=true;
222: goto revno;
223:
224: case 'k':
225: keepflag=true;
226: goto revno;
227:
228: case 'm':
229: if ((*argv)[2]!='\0'){
230: if (msg!=nil)warn("Redefinition of -m option");
231: msg = (*argv)+2;
232: msglen=strlen(msg);
233: if (msglen >= logsize) {
234: warn("log message truncated to %d characters",
235: logsize);
236: msg[logsize-2]='\n';
237: msg[logsize-1]='\0';
238: }
239: if (msg[msglen-1]!='\n') {
240: /*append linefeed*/
241: VOID strcpy(logmsg,msg);msg=logmsg;
242: msg[msglen] = '\n';
243: msg[++msglen]= '\0';
244: }
245: } else warn("Missing message for -m option");
246: break;
247:
248: case 'n':
249: symrebindflag=false;
250: if ((*argv)[2]!='\0'){
251: if (symbol!=nil)warn("Redefinition of symbolic name");
252: symbol = (*argv)+2;
253: if (!(nametest=checkid(symbol,' '))||*nametest)
254: faterror("Name %s must be one word",symbol);
255: } else warn("Missing name for -n option");
256: break;
257:
258: case 'N':
259: symrebindflag=true;
260: if ((*argv)[2]!='\0'){
261: if (symbol!=nil)warn("Redefinition of symbolic name");
262: symbol = (*argv)+2;
263: if (!(nametest=checkid(symbol,' '))||*nametest)
264: faterror("Name %s must be one word",symbol);
265: } else warn("Missing name for -N option");
266: break;
267:
268: case 's':
269: if ((*argv)[2]!='\0'){
270: if (state!=nil)warn("Redefinition of -s option");
271: state = (*argv)+2;
272: VOID checkid(state,' ');
273: } else warn("Missing state for -s option");
274: break;
275:
276: case 't':
277: textflag=true;
278: if ((*argv)[2]!='\0'){
279: if (textfile!=nil)warn("Redefinition of -t option");
280: textfile = (*argv)+2;
281: }
282: break;
283:
284: case 'd':
285: if ((*argv)[2]!='\0'){
286: if (altdate[0]!='\0')warn("Redefinition of -d option");
287: /* process the date */
288: if ( partime((*argv)+2, &parseddate) == 0) {
289: faterror("Can't parse date/time: %s", (*argv)+2);
290: break;
291: }
292: if ( (unixtime = maketime(&parseddate)) == 0L) {
293: faterror("Inconsistent date/time: %s",(*argv)+2);
294: break;
295: }
296: ftm = localtime(&unixtime);
297: VOID sprintf(altdate,DATEFORM,
298: ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
299: } else warn("Missing date for -d option");
300: break;
301:
302: case 'w':
303: if ((*argv)[2]!='\0'){
304: if (author!=nil)warn("Redefinition of -w option");
305: author = (*argv)+2;
306: VOID checkid(author,' ');
307: } else warn("Missing author for -w option");
308: break;
309:
310:
311:
312:
313: default:
314: faterror("unknown option: %s\n%s", *argv,cmdusage);
315: };
316: } /* end processing of options */
317:
318: if (argc<1) faterror("No input file\n%s",cmdusage);
319:
320: if (!isatty(fileno(stdin)) && msg==nil && textflag && textfile==nil) {
321: /* would need both log message and descriptive text from a file */
322: faterror("Can't take both log and description from redirected stdin; use -ttextfile");
323: }
324: /* now handle all filenames */
325: do {
326: gendeltas[0] = nil;
327: copyflag=rewriteflag=false;
328: finptr=frewrite=NULL;
329: targetdelta=nil;
330: olddeltanum=nil;
331:
332: switch (pairfilenames(argc,argv,false,false)) {
333:
334: case -1: /* New RCS file */
335: initflag=true; rcsinitflag=false;
336: break;
337:
338: case 0: /* Error */
339: continue;
340:
341: case 1: /* Normal checkin with prev . RCS file */
342: initflag=false; rcsinitflag=(Head==nil);
343: }
344:
345: /* now RCSfilename contains the name of the RCS file, and
346: * workfilename contains the name of the working file.
347: * if !initflag, finptr contains the file descriptor for the
348: * RCS file. The admin node is initialized.
349: * workstat and RCSstat are set.
350: */
351:
352: diagnose("%s <-- %s", RCSfilename,workfilename);
353:
354: if (access(workfilename,4)!=0) {
355: error("working file %s not readable or nonexistent",
356: workfilename);
357: continue;
358: }
359:
360: if (!trydiraccess(RCSfilename)) continue; /* give up */
361: if (!initflag && !checkaccesslist(caller)) continue; /* give up */
362: if (!trysema(RCSfilename,true)) continue; /* give up */
363:
364: if (keepflag) {
365: /* get keyword values from working file */
366: if (!getoldkeys(workfilename)) continue;
367: if (rev==nil && *(rev=prevrev)=='\0') {
368: error("Can't find a revision number in %s",workfilename);
369: continue;
370: }
371: if (*prevdate=='\0' && *altdate=='\0')
372: warn("Can't find a date in %s",workfilename);
373: if (*prevauthor=='\0' && author==nil)
374: warn("Can't find an author in %s", workfilename);
375: if (*prevstate=='\0' && state==nil)
376: warn("Can't find a state in %s", workfilename);
377: } /* end processing keepflag */
378:
379: gettree(); /* reads in the delta tree.*/
380:
381: /* expand symbolic revision number */
382: if (!expandsym(rev,newdelnum)) continue;
383:
384: /* splice new delta into tree */
385: if (!addelta()) continue;
386:
387: if (initflag||rcsinitflag) {
388: diagnose("initial revision: %s",newdelnum);
389: } else diagnose("new revision: %s; previous revision: %s",
390: newdelnum,olddeltanum);
391:
392: newdelta.num=newdelnum;
393: newdelta.branches=nil;
394: newdelta.log=nil;
395: newdelta.lockedby=nil; /*might be changed by addlock() */
396: /* set author */
397: if (author!=nil)
398: newdelta.author=author; /* set author given by -w */
399: elsif (keepflag && *prevauthor!='\0')
400: newdelta.author=prevauthor; /* preserve old author of possible*/
401: else newdelta.author=caller; /* otherwise use caller's id */
402: if (state!=nil)
403: newdelta.state=state; /* set state given by -s */
404: elsif (keepflag && *prevstate!='\0')
405: newdelta.state=prevstate; /* preserve old state if possilbe */
406: else newdelta.state=DEFAULTSTATE;/* otherwise use default state */
407: if (*altdate!='\0')
408: newdelta.date=altdate; /* set date given by -d */
409: elsif (keepflag && *prevdate!='\0') /* preserve old date if possible */
410: newdelta.date =prevdate;
411: else
412: newdelta.date = getdate(); /* use current date */
413: /* now check validity of date -- needed because of -d and -k */
414: if (targetdelta!=nil &&
415: cmpnum(newdelta.date,targetdelta->date)<=0) {
416: error("Date %s is not later than %s in existing revision %s",
417: newdelta.date,targetdelta->date, targetdelta->num);
418: continue;
419: }
420:
421:
422: if (lockflag && !addlock(&newdelta,caller)) continue;
423: if (symbol && !addsymbol(&newdelta,symbol,symrebindflag)) continue;
424:
425: /* prepare for rewriting the RCS file */
426: newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
427: if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
428: error("Can't open file %s",newRCSfilename);
429: continue;
430: }
431: putadmin(frewrite);
432: puttree(Head,frewrite);
433: VOID putdesc(initflag,textflag,textfile,quietflag);
434:
435:
436: /* build rest of file */
437: if (initflag||rcsinitflag) {
438: /* get logmessage */
439: newdelta.log=getlogmsg();
440: if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
441: ffclose(frewrite); frewrite=NULL;
442: } else {
443: diffilename=mktempfile("/tmp/",DIFFILE);
444: if (&newdelta==Head) {
445: /* prepend new one */
446: rewriteflag=false;
447: if (!(expfilename=
448: buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
449: if (!mustcheckin(expfilename,targetdelta)) continue;
450: /* don't check in files that aren't different, unless forced*/
451: newdelta.log=getlogmsg();
452: VOID sprintf(command,"%s -n %s %s > %s\n", DIFF,
453: workfilename,expfilename,diffilename);
454: exit_stats = system (command);
455: if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
456: faterror ("diff failed");
457: /* diff returns 2 in the upper byte on failure */
458: if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
459: if(!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite)) continue;
460: } else {
461: /* insert new delta text */
462: rewriteflag=true;
463: if (!(expfilename=
464: buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
465: if (!mustcheckin(expfilename,targetdelta)) continue;
466: /* don't check in files that aren't different, unless forced*/
467: newdelta.log=getlogmsg();
468: VOID sprintf(command,"%s -n %s %s > %s\n", DIFF,
469: expfilename,workfilename,diffilename);
470: exit_stats = system (command);
471: if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
472: faterror ("diff failed");
473: if(!putdtext(newdelnum,newdelta.log,diffilename,frewrite)) continue;
474: }
475:
476: /* rewrite rest of RCS file */
477: fastcopy(finptr,frewrite);
478: ffclose(frewrite); frewrite=NULL;
479: }
480: ignoreints();
481: if (rename(newRCSfilename,RCSfilename)<0) {
482: error("Can't write new RCS file %s; saved in %s",
483: RCSfilename,newRCSfilename);
484: newRCSfilename[0]='\0'; /* avoid deletion by cleanup*/
485: restoreints();
486: VOID cleanup();
487: break;
488: }
489: newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
490:
491: newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~0222;
492: /* newRCSmode is also used to adjust mode of working file for -u and -l */
493: if (chmod(RCSfilename,newRCSmode)<0)
494: warn("Can't set mode of %s",RCSfilename);
495:
496: restoreints();
497: # ifdef SNOOPFILE
498: logcommand("ci",&newdelta,gendeltas,caller);
499: # endif
500:
501: if (!keepworkingfile) {
502: VOID unlink(workfilename); /* get rid of old file */
503: } else {
504: /* expand keywords in file */
505: newworkfilename=
506: xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
507: if (!newworkfilename) continue; /* expand failed */
508: ignoreints();
509: if (rename(newworkfilename,workfilename) <0) {
510: error("Can't expand keywords in %s",workfilename);
511: restoreints();
512: continue;
513: }
514: newworkfilename[0]='\0'; /* avoid re-unlink by cleanup */
515: if (chmod(workfilename, WORKMODE(newRCSmode))<0)
516: warn("Can't adjust mode of %s",workfilename);
517: restoreints();
518: }
519: diagnose("done");
520:
521: } while (cleanup(),
522: ++argv, --argc >=1);
523:
524: exit(nerror!=0);
525: /*NOTREACHED*/
526: } /* end of main (ci) */
527: /*****************************************************************/
528: /* the rest are auxiliary routines */
529:
530:
531: int addelta()
532: /* Function: Appends a delta to the delta tree, whose number is
533: * given by newdelnum[]. Updates Head, newdelnum, newdenumlength,
534: * olddeltanum and the links in newdelta.
535: * Retruns false on error, true on success.
536: */
537: {
538: register char * sp, * tp;
539: register int i;
540:
541: newdnumlength=countnumflds(newdelnum);
542:
543: if (initflag || rcsinitflag ) {
544: /* this covers non-existing RCS file and a file initialized with rcs -i */
545: if ((newdnumlength==0)&&(Dbranch!=nil)) {
546: VOID strcpy(newdelnum,Dbranch->num);
547: newdnumlength=countnumflds(newdelnum);
548: }
549: if (newdnumlength==0) VOID strcpy(newdelnum,"1.1");
550: elsif (newdnumlength==1) VOID strcat(newdelnum,".1");
551: elsif (newdnumlength>2) {
552: error("Branch point does not exist for %s",newdelnum);
553: return false;
554: } /* newdnumlength == 2 is OK; */
555: olddeltanum=nil;
556: Head = &newdelta;
557: newdelta.next=nil;
558: return true;
559: }
560: if (newdnumlength==0) {
561: /* derive new revision number from locks */
562: targetdelta=findlock(caller,true); /*find and delete it*/
563: if (targetdelta) {
564: /* found an old lock */
565: olddeltanum=targetdelta->num;
566: /* check whether locked revision exists */
567: if (!genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas)) return false;
568: if (targetdelta==Head) {
569: /* make new head */
570: newdelta.next=Head;
571: Head= &newdelta;
572: incnum(olddeltanum, newdelnum);
573: } elsif ((targetdelta->next==nil)&&(countnumflds(olddeltanum)>2)) {
574: /* new tip revision on side branch */
575: targetdelta->next= &newdelta;
576: newdelta.next = nil;
577: incnum(olddeltanum, newdelnum);
578: } else {
579: /* middle revision; start a new branch */
580: newdelnum[0]='\0';
581: if (!addbranch(targetdelta,newdelnum)) return false;
582: }
583: return true; /* successfull use of existing lock */
584: } else {
585: /* no existing lock; try Dbranch */
586: /* update newdelnum */
587: if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
588: error("no lock set by %s",caller);
589: return false;
590: }
591: if (Dbranch) {
592: VOID strcpy(newdelnum,Dbranch->num);
593: } else {
594: incnum(Head->num,newdelnum);
595: }
596: newdnumlength=countnumflds(newdelnum);
597: /* now fall into next statement */
598: }
599: }
600: if (newdnumlength<=2) {
601: /* add new head per given number */
602: olddeltanum=Head->num;
603: if(newdnumlength==1) {
604: /* make a two-field number out of it*/
605: if (cmpnumfld(newdelnum,olddeltanum,1)==0)
606: incnum(olddeltanum,newdelnum);
607: else VOID strcat(newdelnum, ".1");
608: }
609: if (cmpnum(newdelnum,olddeltanum) <= 0) {
610: error("deltanumber %s too low; must be higher than %s",
611: newdelnum,Head->num);
612: return false;
613: }
614: if (!(targetdelta=removelock(caller,Head))) return false;
615: if (!(genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas))) return false;
616: newdelta.next=Head;
617: Head= &newdelta;
618: } else {
619: /* put new revision on side branch */
620: /*first, get branch point */
621: tp=branchpointnum; sp=newdelnum;
622: for(i=newdnumlength-(newdnumlength%2==1?1:2);i>0;i--) {
623: while (*sp != '.') *tp++ = *sp++; /*copy field*/
624: *tp++ = *sp++; /*copy dot */
625: }
626: *(tp-1) = '\0'; /* kill final dot */
627: olddeltanum=branchpointnum; /*temporary old delta*/
628: if (!(targetdelta=genrevs(branchpointnum,(char *)nil,(char *)nil,(char *)nil,gendeltas)))
629: return false;
630: if (cmpnum(targetdelta->num,branchpointnum)!=0) {
631: error("Cannot find branchpoint %s",branchpointnum);
632: return false;
633: }
634: if (!addbranch(targetdelta,newdelnum)) return false;
635: }
636: return true;
637: }
638:
639:
640:
641: int addbranch(branchpoint,num)
642: struct hshentry * branchpoint;
643: char * num;
644: /* adds a new branch and branch delta at branchpoint.
645: * If num is the null string, appends the new branch, incrementing
646: * the highest branch number (initially 1), and setting the level number to 1.
647: * the new delta and branchhead are in globals newdelta and newbranch, resp.
648: * the new number is placed into num.
649: * returns false on error.
650: */
651: {
652: struct branchhead * bhead, * btrail;
653: char branchnum[revlength];
654: int numlength, result, field;
655:
656: numlength = countnumflds(num);
657:
658: if (branchpoint->branches==nil) {
659: /* start first branch */
660: branchpoint->branches = &newbranch;
661: if (numlength==0) {
662: VOID strcpy(num, branchpoint->num);
663: VOID strcat(num,".1.1");
664: } elsif(countnumflds(num)%2 == 1)
665: VOID strcat(num, ".1");
666: newbranch.nextbranch=nil;
667:
668: } elsif (numlength==0) {
669: /* append new branch to the end */
670: bhead=branchpoint->branches;
671: while (bhead->nextbranch) bhead=bhead->nextbranch;
672: bhead->nextbranch = &newbranch;
673: getbranchno(bhead->hsh->num,branchnum);
674: incnum(branchnum,num);
675: VOID strcat(num,".1");
676: newbranch.nextbranch=nil;
677: } else {
678: /* place the branch properly */
679: field = numlength - (numlength%2 ==1?0:1);
680: /* field of branch number */
681: bhead=branchpoint->branches;
682: while ((bhead!=nil) &&
683: ((result=cmpnumfld(num,bhead->hsh->num,field))>0)) {
684: btrail=bhead;
685: bhead=bhead->nextbranch;
686: }
687: if (bhead==nil || result<0) {
688: /* insert/append new branchhead */
689: if (bhead==branchpoint->branches)
690: branchpoint->branches= &newbranch;
691: else btrail->nextbranch= &newbranch;
692: newbranch.nextbranch=bhead;
693: if (numlength%2 ==1) VOID strcat(num,".1");
694: } else {
695: /* branch exists; append to end */
696: getbranchno(num,branchnum);
697: if (!(targetdelta=genrevs(branchnum,(char *)nil,(char *)nil,(char *)nil,
698: gendeltas))) return false;
699: olddeltanum=targetdelta->num;
700: if (cmpnum(num,olddeltanum) <= 0) {
701: error("deltanumber %s too low; must be higher than %s",
702: num,olddeltanum);
703: return false;
704: }
705: if (!removelock(caller,targetdelta)) return false;
706: if (numlength%2==1) incnum(olddeltanum,num);
707: targetdelta->next= &newdelta;
708: newdelta.next=nil;
709: return true; /* Don't do anything to newbranch */
710: }
711: }
712: newbranch.hsh = &newdelta;
713: newdelta.next=nil;
714: return true;
715: }
716:
717:
718:
719: struct hshentry * removelock(who,delta)
720: char * who; struct hshentry * delta;
721: /* function: Finds the lock held by who on delta,
722: * removes it, and returns a pointer to the delta.
723: * Prints an error message and returns nil if there is no such lock.
724: * An exception is if StrictLocks==false, and who is the owner of
725: * the RCS file. If who does not have a lock in this case,
726: * delta is returned.
727: */
728: {
729: register struct lock * next, * trail;
730: char * num;
731: struct lock dummy;
732: int whomatch, nummatch;
733:
734: num=delta->num;
735: dummy.nextlock=next=Locks;
736: trail = &dummy;
737: while (next!=nil) {
738: whomatch=strcmp(who,next->login);
739: nummatch=strcmp(num,next->delta->num);
740: if ((whomatch==0) && (nummatch==0)) break;
741: /*found a lock on delta by who*/
742: if ((whomatch!=0)&&(nummatch==0)) {
743: error("revision %s locked by %s",num,next->login);
744: return nil;
745: }
746: trail=next;
747: next=next->nextlock;
748: }
749: if (next!=nil) {
750: /*found one; delete it */
751: trail->nextlock=next->nextlock;
752: Locks=dummy.nextlock;
753: next->delta->lockedby=nil; /* reset locked-by */
754: return next->delta;
755: } else {
756: if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
757: error("no lock set by %s for revision %s",who,num);
758: return nil;
759: } else {
760: return delta;
761: }
762: }
763: }
764:
765:
766:
767: char * getdate()
768: /* Function: returns a pointer to the current date in the form
769: * YY.MM.DD.hh.mm.ss\0
770: */
771: {
772: long clock;
773: struct tm * tm;
774: static char buffer[datelength]; /* date buffer */
775: clock=time((long *)0);
776: tm=localtime(&clock);
777: VOID sprintf(buffer, DATEFORM,
778: tm->tm_year, tm->tm_mon+1, tm->tm_mday,
779: tm->tm_hour, tm->tm_min, tm->tm_sec);
780: return buffer;
781: }
782:
783:
784: char * xpandfile (unexfname,dir,delta)
785: char * unexfname, * dir;
786: struct hshentry * delta;
787: /* Function: Reads file unexpfname and copies it to a
788: * file in dir, performing keyword substitution with data from delta.
789: * returns the name of the expanded file if successful, nil otherwise.
790: */
791: { char * targetfname;
792: FILE * unexfile, *exfile;
793:
794: targetfname=mktempfile(dir,TMPFILE3);
795: if ((unexfile=fopen(unexfname, "r" ))==NULL ||
796: (exfile =fopen(targetfname,"w"))==NULL) {
797: error("Can't expand file %s",unexfname);
798: return nil;
799: }
800: while (expandline(unexfile,exfile,delta,false,false)); /*expand*/
801: ffclose(unexfile);ffclose(exfile);
802: return targetfname;
803: }
804:
805:
806: mustcheckin (unexfname,delta)
807: char * unexfname; struct hshentry * delta;
808: /* Function: determines whether checkin should proceed.
809: * Compares the wrkfilename with unexfname, disregarding keywords.
810: * If the 2 files differ, returns true. If they do not differ, asks the user
811: * whether to return true or false (i.e., whether to checkin the file anyway.
812: * If the files do not differ, and quietflag==true, returns false.
813: * Shortcut: If forceciflag==true, mustcheckin() always returns true.
814: */
815: { register int c;
816: int response, result;
817:
818: if (forceciflag) return true;
819:
820: if (!rcsfcmp(workfilename,unexfname,delta)) return true;
821: /* If files are different, must check them in. */
822:
823: /* files are the same */
824: diagnose("File %s is unchanged with respect to revision %s",
825: workfilename,delta->num);
826: if (quietflag || !isatty(fileno(stdin))) {
827: /* Files are the same, but can't ask, so don't checkin*/
828: result=false;
829: } else {
830: /* ask user whether to check in */
831: VOID fputs("checkin anyway? [ny](n): ",stderr);
832: response=c=getchar();
833: while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
834: result=(response=='y'||response=='Y');
835: }
836: if (result==false) {
837: if (quietflag) {
838: warn("checkin aborted since %s was not changed; %s %sdeleted.",
839: workfilename,workfilename,keepworkingfile?"not ":"");
840: } else {
841: diagnose("checkin aborted; %s %sdeleted.",
842: workfilename,keepworkingfile?"not ":"");
843: }
844: if (!keepworkingfile) VOID unlink(workfilename);
845: }
846: return result;
847: }
848:
849:
850:
851:
852: /* --------------------- G E T L O G M S G --------------------------------*/
853: extern int stdinread; /* is >0 if redirected stdin has been read once. */
854:
855:
856: char * getlogmsg()
857: /* Function: obtains a log message and returns a pointer to it.
858: * If a log message is given via the -m option, a pointer to that
859: * string is returned.
860: * If this is the initial revision, a standard log message is returned.
861: * Otherwise, reads a character string from the terminal.
862: * The string must be terminated with a control-d or a single '.' on a
863: * line. getlogmsg prompts the first time it is called for the
864: * log message; during all later calls it asks whether the previous
865: * log message can be reused.
866: * returns a pointer to the character string; the pointer is always non-nil.
867: */
868: {
869: static logyet = false; /*indicates whether previous log present*/
870: static char emptylog[] = "*** empty log message ***\n";
871: static char initiallog[]= "Initial revision\n";
872: char response;
873: int cin;
874: register char c, old1, old2, * tp;
875:
876: if (msg) return msg;
877:
878: if ((olddeltanum==nil)&&
879: ((cmpnum(newdelnum,"1.1")==0)||(cmpnum(newdelnum,"1.0")==0))) {
880: return initiallog;
881: }
882: if (keepflag) {
883: /* generate std. log message */
884: VOID sprintf(logmsg, "checked in with -k by %s at %s.\n",caller,getdate());
885: return(logmsg);
886: }
887: if (logyet) {
888: /*previous log available*/
889: if (!isatty(fileno(stdin))) return logmsg; /* reuse if stdin is not a terminal*/
890: /* otherwise ask */
891: clearerr(stdin); /* reset EOF ptr */
892: VOID fputs("reuse log message of previous file? [yn](y): ",stderr);
893: cin=getchar();
894: response=cin;
895: while (!(cin==EOF || cin=='\n')) cin=getchar();/*skip to end of line*/
896: if (response=='\n'||response=='y'||response=='Y')
897: return logmsg;
898: else
899: logmsg[0]='\0'; /*kill existing log message */
900: }
901:
902: /* now read string from stdin */
903: if (isatty(fileno(stdin))) {
904: VOID fputs("enter log message:\n(terminate with ^D or single '.')\n>> ",stderr);
905: } else { /* redirected stdin */
906: if (stdinread>0)
907: faterror("Can't reread redirected stdin for log message; use -m");
908: stdinread++;
909: }
910:
911: tp=logmsg; old1='\n'; old2=' ';
912: if (feof(stdin))
913: clearerr(stdin);
914: for (;;) {
915: cin=getchar();
916: if (cin==EOF) {
917: if(isatty(fileno(stdin))) VOID putc('\n',stderr);
918: if ((tp==logmsg)||(*(tp-1)!='\n')) *tp++ = '\n'; /* append newline */
919: *tp = '\0'; /*terminate*/
920: break;
921: }
922: if (cin=='\n' && old1=='.' && old2=='\n') {
923: *(tp-1) = '\0'; /*kill last period */
924: break;
925: }
926: if (tp>=logmsg+logsize-2) { /* overflow */
927: if (!isatty(fileno(stdin))) {
928: warn("log message truncated to %d characters",logsize);
929: logmsg[logsize-2]='\n';logmsg[logsize-1]='\0';
930: return logmsg;
931: }
932: VOID fprintf(stderr,"log message too long. Maximum: %d\n",logsize);
933: VOID fputs("reenter log message:\n>> ",stderr);
934: tp=logmsg; old1='\n'; old2=' ';
935: while (cin!='\n') cin=getchar(); /*skip line */
936: continue;
937: }
938: if (cin=='\n' && isatty(fileno(stdin))) VOID fputs(">> ",stderr);
939: *tp++ = cin; old2=old1; old1=cin; /* this is the actual work!*/
940: /*SDELIM will be changed to double SDELIM by putdtext*/
941: } /* end for */
942:
943: /* now check whether the log message is not empty */
944: tp=logmsg;
945: while ((c= *tp++)==' '||c=='\t'||c=='\n'||c=='\f');
946: if (*tp=='\0') {
947: logyet=false;
948: return emptylog;
949: } else {
950: logyet=true;
951: return logmsg;
952: }
953: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.