|
|
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.