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

Defined functions

addbranch defined in line 504; used 2 times
getdate defined in line 649; used 2 times
getlogmsg defined in line 743; used 4 times
main defined in line 120; never used
mustcheckin defined in line 692; used 2 times
removelock defined in line 581; used 4 times
xpandfile defined in line 670; used 2 times

Defined variables

RCSfilename defined in line 95; used 8 times
branchpointnum defined in line 115; used 5 times
caller defined in line 109; used 10 times
copyflag defined in line 100; used 1 times
diffilename defined in line 96; used 5 times
expfilename defined in line 95; used 6 times
forceciflag defined in line 106; used 3 times
gendeltas defined in line 112; used 8 times
initflag defined in line 104; used 9 times
keepflag defined in line 105; used 4 times
keepworkingfile defined in line 105; used 7 times
lockflag defined in line 105; used 5 times
logmsg defined in line 118; used 13 times
msg defined in line 102; used 14 times
newRCSfilename defined in line 96; used 7 times
newbranch defined in line 117; used 8 times
newdelnum defined in line 113; used 24 times
newdelta defined in line 116; used 36 times
newdnumlength defined in line 114; used 9 times
newworkfilename defined in line 95; used 3 times
olddeltanum defined in line 111; used 20 times
rcsbaseid defined in line 68; never used
rcsid defined in line 4; never used
rcsinitflag defined in line 104; used 7 times
rev defined in line 102; used 6 times
rewriteflag defined in line 99; used 3 times
state defined in line 102; used 8 times
symbol defined in line 107; used 9 times
symrebindflag defined in line 107; used 4 times
targetdelta defined in line 110; used 20 times
textfile defined in line 108; used 5 times
textflag defined in line 108; used 4 times
workfilename defined in line 95; used 28 times
Last modified: 1985-10-24
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2461
Valid CSS Valid XHTML 1.0 Strict