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