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: }