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