# 2.11BSD#431 - /usr/src/new/rcs/src/ci.c

##### Functions

    addbranch         X
    addelta           X
    getdate           X
    getlogmsg         X
    main              X
    mustcheckin       X
    removelock        X
    xpandfile         X

##### Variables

    RCSfilename       X
    altdate           X
    author            X
    branchpointnum    X
    caller            X
    copyflag          X
    diffilename       X
    expfilename       X
    forceciflag       X
    gendeltas         X
    initflag          X
    keepflag          X
    keepworkingfile   X
    lockflag          X
    logmsg            X
    msg               X
    newRCSfilename    X
    newbranch         X
    newdelnum         X
    newdelta          X
    newdnumlength     X
    newworkfilename   X
    olddeltanum       X
    rcsinitflag       X
    rev               X
    state             X
    symbol            X
    symrebindflag     X
    targetdelta       X
    textfile          X
    textflag          X
    workfilename      X

       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

- in line 581, 634

addelta defined in line 531; used 1 times

- in line 385

getdate defined in line 767; used 3 times

- in line 130, 412, 884

getlogmsg defined in line 856; used 4 times

- in line 133, 439, 451, 467

main defined in line 175; never used
mustcheckin defined in line 806; used 2 times

- in line 449, 465

removelock defined in line 719; used 3 times

- in line 134, 614, 705

xpandfile defined in line 784; used 2 times

- in line 136, 506

#### Defined variables

RCSfilename defined in line 148; used 8 times

- in line 352, 360-362(2), 426, 481-483(2), 493-494(2)

altdate defined in line 164; used 6 times

- in line 195, 286, 297, 371, 407-408(2)

author defined in line 163; used 10 times

- in line 194, 304-306(3), 373, 397-401(5)

branchpointnum defined in line 170; used 5 times

- in line 621, 627-631(4)

caller defined in line 162; used 10 times

- in line 194, 361, 401, 422, 498, 562, 588, 614, 705, 884

copyflag defined in line 153; used 1 times

- in line 327

diffilename defined in line 147; used 5 times

- in line 443, 453, 459, 469-473(2)

expfilename defined in line 148; used 6 times

- in line 447-453(3), 463-469(3)

forceciflag defined in line 159; used 3 times

- in line 193, 221, 818

gendeltas defined in line 167; used 8 times

- in line 326, 448, 464, 498, 567, 615, 628, 698

initflag defined in line 157; used 9 times

- in line 192, 335, 342, 361, 387, 433-437(2), 491, 543

keepflag defined in line 158; used 7 times

- in line 193, 225, 364, 399-409(3), 882

keepworkingfile defined in line 158; used 7 times

- in line 193, 209-213(2), 501, 839-844(3)

lockflag defined in line 158; used 5 times

- in line 193, 201, 209-213(2), 422

logmsg defined in line 173; used 16 times

- in line 241(2), 884-889(3), 897-899(2), 911, 918, 926-934(5), 944, 951

msg defined in line 155; used 14 times

- in line 191, 230-243(10), 320, 876(2)

newRCSfilename defined in line 147; used 7 times

- in line 426-428(3), 481-489(4)

newbranch defined in line 172; used 8 times

- in line 660, 666, 672-676(2), 690-692(3), 712

newdelnum defined in line 168; used 29 times

- in line 382, 388-392(3), 440, 458, 473, 541-552(6), 572-581(4), 592-596(3), 605-611(5), 621, 634, 879(2)

newdelta defined in line 171; used 38 times

- in line 392-423(17), 439-444(3), 451, 458, 467, 473, 498, 506, 556-557(2), 570-576(4), 616-617(2), 707-713(4)

newdnumlength defined in line 169; used 12 times

- in line 541-551(6), 560, 596-603(3), 622(2)

newworkfilename defined in line 148; used 4 times

- in line 505-514(4)

olddeltanum defined in line 166; used 20 times

- in line 330, 390, 459, 555, 565-577(5), 602-609(4), 615, 627, 699-706(4), 878

rcsbaseid defined in line 112; never used
rcsid defined in line 5; never used
rcsinitflag defined in line 157; used 7 times

- in line 192, 335, 342, 387, 437, 491, 543

rev defined in line 155; used 6 times

- in line 191, 203-204(2), 367(2), 382

state defined in line 155; used 10 times

- in line 191, 270-272(3), 375, 402-406(5)

symbol defined in line 160; used 11 times

- in line 191, 251-254(4), 261-264(4), 423(2)

symrebindflag defined in line 160; used 4 times

- in line 192, 249, 259, 423

targetdelta defined in line 165; used 25 times

- in line 329, 414-417(4), 448-449(2), 459-465(3), 562-575(6), 581, 614, 628-634(3), 697-699(2), 705-707(2)

textfile defined in line 161; used 5 times

- in line 191, 279-280(2), 320, 433

textflag defined in line 161; used 4 times

- in line 192, 277, 320, 433

workfilename defined in line 148; used 25 times

- in line 352-356(3), 366-376(5), 440, 453-458(2), 469, 502-516(7), 820-825(2), 839-844(4)

Last modified: 1988-02-18 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2028 |