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

Defined functions

addbranch defined in line 641; used 2 times
addelta defined in line 531; used 1 times
getdate defined in line 767; used 3 times
getlogmsg defined in line 856; used 4 times
main defined in line 175; never used
mustcheckin defined in line 806; used 2 times
removelock defined in line 719; used 3 times
xpandfile defined in line 784; used 2 times

Defined variables

RCSfilename defined in line 148; used 8 times
altdate defined in line 164; used 6 times
author defined in line 163; used 10 times
branchpointnum defined in line 170; used 5 times
caller defined in line 162; used 10 times
copyflag defined in line 153; used 1 times
diffilename defined in line 147; used 5 times
expfilename defined in line 148; used 6 times
forceciflag defined in line 159; used 3 times
gendeltas defined in line 167; used 8 times
initflag defined in line 157; used 9 times
keepflag defined in line 158; used 7 times
keepworkingfile defined in line 158; used 7 times
lockflag defined in line 158; used 5 times
logmsg defined in line 173; used 16 times
msg defined in line 155; used 14 times
newRCSfilename defined in line 147; used 7 times
newbranch defined in line 172; used 8 times
newdelnum defined in line 168; used 29 times
newdelta defined in line 171; used 38 times
newdnumlength defined in line 169; used 12 times
newworkfilename defined in line 148; used 4 times
olddeltanum defined in line 166; used 20 times
rcsbaseid defined in line 112; never used
rcsid defined in line 5; never used
rcsinitflag defined in line 157; used 7 times
rev defined in line 155; used 6 times
state defined in line 155; used 10 times
symbol defined in line 160; used 11 times
symrebindflag defined in line 160; used 4 times
targetdelta defined in line 165; used 25 times
textfile defined in line 161; used 5 times
textflag defined in line 161; used 4 times
workfilename defined in line 148; used 25 times
Last modified: 1988-02-18
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4462
Valid CSS Valid XHTML 1.0 Strict