Annotation of 43BSD/contrib/rcs/src/ci.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.