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