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

1.1       root        1: /*
                      2:  *                      RCS create/change operation
                      3:  */
                      4: #ifndef lint
                      5: static char rcsid[]=
                      6: "$Header: /usr/src/local/bin/rcs/src/RCS/rcs.c,v 4.11 89/05/01 15:12:06 narten Exp $ Purdue CS";
                      7: #endif
                      8: /* Copyright (C) 1982, 1988, 1989 Walter Tichy
                      9:  * All rights reserved.
                     10:  *
                     11:  * Redistribution and use in source and binary forms are permitted
                     12:  * provided that the above copyright notice and this paragraph are
                     13:  * duplicated in all such forms and that any documentation,
                     14:  * advertising materials, and other materials related to such
                     15:  * distribution and use acknowledge that the software was developed
                     16:  * by Walter Tichy.
                     17:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
                     18:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
                     19:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     20:  *
                     21:  * Report all problems and direct all questions to:
                     22:  *   [email protected]
                     23:  * 
                     24: 
                     25: 
                     26: 
                     27: 
                     28: 
                     29: 
                     30: 
                     31: */
                     32: 
                     33: 
                     34: 
                     35: 
                     36: /* $Log:       rcs.c,v $
                     37:  * Revision 4.11  89/05/01  15:12:06  narten
                     38:  * changed copyright header to reflect current distribution rules
                     39:  * 
                     40:  * Revision 4.10  88/11/08  16:01:54  narten
                     41:  * didn't install previous patch correctly
                     42:  * 
                     43:  * Revision 4.9  88/11/08  13:56:01  narten
                     44:  * removed include <sysexits.h> (not needed)
                     45:  * minor fix for -A option
                     46:  * 
                     47:  * Revision 4.8  88/11/08  12:01:58  narten
                     48:  * changes from  [email protected] (Paul Eggert)
                     49:  * 
                     50:  * Revision 4.8  88/08/09  19:12:27  eggert
                     51:  * Don't access freed storage.
                     52:  * Use execv(), not system(); yield proper exit status; remove lint.
                     53:  * 
                     54:  * Revision 4.7  87/12/18  11:37:17  narten
                     55:  * lint cleanups (Guy Harris)
                     56:  * 
                     57:  * Revision 4.6  87/10/18  10:28:48  narten
                     58:  * Updating verison numbers. Changes relative to 1.1 are actually 
                     59:  * relative to 4.3
                     60:  * 
                     61:  * Revision 1.4  87/09/24  13:58:52  narten
                     62:  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
                     63:  * warnings)
                     64:  * 
                     65:  * Revision 1.3  87/03/27  14:21:55  jenkins
                     66:  * Port to suns
                     67:  * 
                     68:  * Revision 1.2  85/12/17  13:59:09  albitz
                     69:  * Changed setstate to rcs_setstate because of conflict with random.o.
                     70:  * 
                     71:  * Revision 1.1  84/01/23  14:50:09  kcs
                     72:  * Initial revision
                     73:  * 
                     74:  * Revision 4.3  83/12/15  12:27:33  wft
                     75:  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
                     76:  * 
                     77:  * Revision 4.2  83/12/05  10:18:20  wft
                     78:  * Added conditional compilation for sending mail.
                     79:  * Alternatives: V4_2BSD, V6, USG, and other.
                     80:  * 
                     81:  * Revision 4.1  83/05/10  16:43:02  wft
                     82:  * Simplified breaklock(); added calls to findlock() and getcaller().
                     83:  * Added option -b (default branch). Updated -s and -w for -b.
                     84:  * Removed calls to stat(); now done by pairfilenames().
                     85:  * Replaced most catchints() calls with restoreints().
                     86:  * Removed check for exit status of delivermail().
                     87:  * Directed all interactive output to stderr.
                     88:  * 
                     89:  * Revision 3.9.1.1  83/12/02  22:08:51  wft
                     90:  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
                     91:  * 
                     92:  * Revision 3.9  83/02/15  15:38:39  wft
                     93:  * Added call to fastcopy() to copy remainder of RCS file.
                     94:  *
                     95:  * Revision 3.8  83/01/18  17:37:51  wft
                     96:  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
                     97:  *
                     98:  * Revision 3.7  83/01/15  18:04:25  wft
                     99:  * Removed putree(); replaced with puttree() in rcssyn.c.
                    100:  * Combined putdellog() and scanlogtext(); deleted putdellog().
                    101:  * Cleaned up diagnostics and error messages. Fixed problem with
                    102:  * mutilated files in case of deletions in 2 files in a single command.
                    103:  * Changed marking of selector from 'D' to DELETE.
                    104:  *
                    105:  * Revision 3.6  83/01/14  15:37:31  wft
                    106:  * Added ignoring of interrupts while new RCS file is renamed;
                    107:  * Avoids deletion of RCS files by interrupts.
                    108:  *
                    109:  * Revision 3.5  82/12/10  21:11:39  wft
                    110:  * Removed unused variables, fixed checking of return code from diff,
                    111:  * introduced variant COMPAT2 for skipping Suffix on -A files.
                    112:  *
                    113:  * Revision 3.4  82/12/04  13:18:20  wft
                    114:  * Replaced getdelta() with gettree(), changed breaklock to update
                    115:  * field lockedby, added some diagnostics.
                    116:  *
                    117:  * Revision 3.3  82/12/03  17:08:04  wft
                    118:  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
                    119:  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
                    120:  * fixed -u for missing revno. Disambiguated structure members.
                    121:  *
                    122:  * Revision 3.2  82/10/18  21:05:07  wft
                    123:  * rcs -i now generates a file mode given by the umask minus write permission;
                    124:  * otherwise, rcs keeps the mode, but removes write permission.
                    125:  * I added a check for write error, fixed call to getlogin(), replaced
                    126:  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
                    127:  * conflicting, long identifiers.
                    128:  *
                    129:  * Revision 3.1  82/10/13  16:11:07  wft
                    130:  * fixed type of variables receiving from getc() (char -> int).
                    131:  */
                    132: 
                    133: 
                    134: #include <sys/types.h>
                    135: #include <sys/stat.h>
                    136: #include "rcsbase.h"
                    137: #include <paths.h>
                    138: #ifndef lint
                    139: static char rcsbaseid[] = RCSBASE;
                    140: #endif
                    141: 
                    142: extern FILE * fopen();
                    143: extern char * bindex();
                    144: extern int  expandsym();                /* get numeric revision name        */
                    145: extern struct  hshentry  * getnum();
                    146: extern struct  lock      * addlock();   /* add a lock                       */
                    147: extern char              * getid();
                    148: extern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
                    149: #ifdef COMPAT2
                    150: extern char Ksuffix[];
                    151: #endif
                    152: extern char * getcaller();              /* get login of caller              */
                    153: extern struct hshentry   * findlock();  /* find and remove lock             */
                    154: extern struct hshentry   * genrevs();
                    155: extern char * checkid();                /* check an identifier              */
                    156: extern char * getfullRCSname();         /* get full path name of RCS file   */
                    157: extern char * mktempfile();             /* temporary file name generator    */
                    158: extern free();
                    159: extern void catchints();
                    160: extern void ignoreints();
                    161: extern int  nerror;                     /* counter for errors               */
                    162: extern int  quietflag;                  /* diagnoses suppressed if true     */
                    163: extern char curlogmsg[];                /* current log message              */
                    164: extern char * resultfile;               /* filename for fcopy              */
                    165: extern FILE *fcopy;                     /* result file during editing       */
                    166: extern FILE * finptr;                   /* RCS input file                   */
                    167: extern FILE * frewrite;                 /* new RCS file                     */
                    168: extern int    rewriteflag;              /* indicates whether input should be*/
                    169:                                        /* echoed to frewrite               */
                    170: 
                    171: char * newRCSfilename, * diffilename, * cutfilename;
                    172: char * RCSfilename, * workfilename;
                    173: extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
                    174: extern int  haveRCSstat, haveworkstat;/* status indicators                  */
                    175: 
                    176: char accessorlst[strtsize];
                    177: FILE * fcut;        /* temporary file to rebuild delta tree                 */
                    178: int    oldumask;    /* save umask                                           */
                    179: 
                    180: int initflag, strictlock, strict_selected, textflag;
                    181: char * textfile, * accessfile;
                    182: char * caller, numrev[30];            /* caller's login;               */
                    183: struct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
                    184: struct  access  *curaccess,  *rmaccess;
                    185: struct  hshentry        * gendeltas[hshsize];
                    186: 
                    187: struct  Lockrev {
                    188:         char    * revno;
                    189:         struct  Lockrev   * nextrev;
                    190: };
                    191: 
                    192: struct  Symrev {
                    193:         char    * revno;
                    194:         char    * ssymbol;
                    195:         int     override;
                    196:         struct  Symrev  * nextsym;
                    197: };
                    198: 
                    199: struct  Status {
                    200:         char    * revno;
                    201:         char    * status;
                    202:         struct  Status  * nextstatus;
                    203: };
                    204: 
                    205: struct delrevpair {
                    206:         char    * strt;
                    207:         char    * end;
                    208:         int     code;
                    209: };
                    210: 
                    211: struct  Lockrev * newlocklst,   * rmvlocklst;
                    212: struct  Symrev  * assoclst,  * lastassoc;
                    213: struct  Status  * statelst,  * laststate;
                    214: struct  delrevpair      * delrev;
                    215: struct  hshentry        * cuthead,  *cuttail,  * delstrt;
                    216: char    branchnum[revlength], * branchsym;
                    217: struct  hshentry branchdummy;
                    218: char   * commsyml;
                    219: char    * headstate;
                    220: int     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
                    221: int     delaccessflag;
                    222: enum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
                    223: 
                    224: 
                    225: main (argc, argv)
                    226: int argc;
                    227: char * argv[];
                    228: {
                    229:         char    *comdusge;
                    230:         int     result;
                    231:        struct  access  *removeaccess(),  * getaccessor();
                    232:         struct  Lockrev *rmnewlocklst();
                    233:         struct  Lockrev *curlock,  * rmvlock, *lockpt;
                    234:         struct  Status  * curstate;
                    235:         struct  access    *temp, *temptr;
                    236:        int status;
                    237: 
                    238:        status = 0;
                    239:         nerror = 0;
                    240:        catchints();
                    241:         cmdid = "rcs";
                    242:         quietflag = false;
                    243:         comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
                    244:         rplaccessor = nil;     delstrt = nil;
                    245:         accessfile = textfile = caller = nil;
                    246:         branchflag = commentflag = chgheadstate = false;
                    247:         lockhead = false; unlockcaller=false;
                    248:         initflag= textflag = false;
                    249:         strict_selected = 0;
                    250: 
                    251:        caller=getcaller();
                    252:         laststate = statelst = nil;
                    253:         lastassoc = assoclst = nil;
                    254:         curlock = rmvlock = newlocklst = rmvlocklst = nil;
                    255:         curaccess = rmaccess = rmvaccessor = newaccessor = nil;
                    256:         delaccessflag = false;
                    257: 
                    258:         /*  preprocessing command options    */
                    259:         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
                    260:                 switch ((*argv)[1]) {
                    261: 
                    262:                 case 'i':   /*  initail version  */
                    263:                         initflag = true;
                    264:                         break;
                    265: 
                    266:                 case 'b':  /* change default branch */
                    267:                         if (branchflag)warn("Redfinition of option -b");
                    268:                         branchflag= true;
                    269:                         branchsym = (*argv)+2;
                    270:                         break;
                    271: 
                    272:                 case 'c':   /*  change comment symbol   */
                    273:                         if (commentflag)warn("Redefinition of option -c");
                    274:                         commentflag = true;
                    275:                         commsyml = (*argv)+2;
                    276:                         break;
                    277: 
                    278:                 case 'a':  /*  add new accessor   */
                    279:                         if ( (*argv)[2] == '\0') {
                    280:                             error("Login name missing after -a");
                    281:                         }
                    282:                         if ( (temp = getaccessor((*argv)+1)) ) {
                    283:                             if ( newaccessor )
                    284:                                 curaccess->nextaccess = temp->nextaccess;
                    285:                             else
                    286:                                 newaccessor = temp->nextaccess;
                    287:                             temp->nextaccess = nil;
                    288:                             curaccess = temp;
                    289:                         }
                    290:                         break;
                    291: 
                    292:                 case 'A':  /*  append access list according to accessfile  */
                    293:                         if ( (*argv)[2] == '\0') {
                    294:                             error("Missing file name after -A");
                    295:                             break;
                    296:                         }
                    297:                         if ( accessfile) warn("Redefinition of option -A");
                    298:                         *argv = *argv+2;
                    299:                         if( pairfilenames(1, argv, true, false) > 0) {
                    300:                             releaselst(newaccessor);
                    301:                             newaccessor = curaccess = nil;
                    302:                             releaselst(rmvaccessor);
                    303:                             rmvaccessor = rmaccess = nil;
                    304:                             accessfile = RCSfilename;
                    305:                         }
                    306:                         else
                    307:                             accessfile = nil;
                    308:                         break;
                    309: 
                    310:                 case 'e':    /*  remove accessors   */
                    311:                         if ( (*argv)[2] == '\0' ) {
                    312:                             delaccessflag = true;
                    313:                             break;
                    314:                         }
                    315:                         if ( (temp = getaccessor((*argv)+1))  ) {
                    316:                             if ( rmvaccessor )
                    317:                                 rmaccess->nextaccess = temp->nextaccess;
                    318:                             else
                    319:                                 rmvaccessor = temp->nextaccess;
                    320:                             temptr = temp->nextaccess;
                    321:                             temp->nextaccess = nil;
                    322:                             rmaccess = temp;
                    323:                             while( temptr ) {
                    324:                                 newaccessor = removeaccess(temptr,newaccessor,false);
                    325:                                 temptr = temptr->nextaccess;
                    326:                             }
                    327:                             curaccess = temp = newaccessor;
                    328:                             while( temp){
                    329:                                 curaccess = temp;
                    330:                                 temp = temp->nextaccess;
                    331:                             }
                    332:                         }
                    333:                         break;
                    334: 
                    335:                 case 'l':    /*   lock a revision if it is unlocked   */
                    336:                         if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
                    337:                             lockhead = true;
                    338:                             break;
                    339:                         }
                    340:                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
                    341:                         lockpt->revno = (*argv)+2;
                    342:                         lockpt->nextrev = nil;
                    343:                         if ( curlock )
                    344:                             curlock->nextrev = lockpt;
                    345:                         else
                    346:                             newlocklst = lockpt;
                    347:                         curlock = lockpt;
                    348:                         break;
                    349: 
                    350:                 case 'u':   /*  release lock of a locked revision   */
                    351:                         if ( (*argv)[2] == '\0'){ /*  unlock head  */
                    352:                             unlockcaller=true;
                    353:                             break;
                    354:                         }
                    355:                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
                    356:                         lockpt->revno = (*argv)+2;
                    357:                         lockpt->nextrev = nil;
                    358:                         if (rmvlock)
                    359:                             rmvlock->nextrev = lockpt;
                    360:                         else
                    361:                             rmvlocklst = lockpt;
                    362:                         rmvlock = lockpt;
                    363: 
                    364:                         curlock = rmnewlocklst(lockpt);
                    365:                         break;
                    366: 
                    367:                 case 'L':   /*  set strict locking */
                    368:                         if (strict_selected++) {  /* Already selected L or U? */
                    369:                           if (!strictlock)       /* Already selected -U? */
                    370:                               warn("Option -L overrides -U");
                    371:                         }
                    372:                         strictlock = true;
                    373:                         break;
                    374: 
                    375:                 case 'U':   /*  release strict locking */
                    376:                         if (strict_selected++) {  /* Already selected L or U? */
                    377:                           if (strictlock)        /* Already selected -L? */
                    378:                               warn("Option -L overrides -U");
                    379:                         }
                    380:                        else
                    381:                            strictlock = false;
                    382:                         break;
                    383: 
                    384:                 case 'n':    /*  add new association: error, if name exists */
                    385:                         if ( (*argv)[2] == '\0') {
                    386:                             error("Missing symbolic name after -n");
                    387:                             break;
                    388:                         }
                    389:                         getassoclst(false, (*argv)+1);
                    390:                         break;
                    391: 
                    392:                 case 'N':   /*  add or change association   */
                    393:                         if ( (*argv)[2] == '\0') {
                    394:                             error("Missing symbolic name after -N");
                    395:                             break;
                    396:                         }
                    397:                         getassoclst(true, (*argv)+1);
                    398:                         break;
                    399: 
                    400:                 case 'o':   /*  delete revisins  */
                    401:                         if (delrev) warn("Redefinition of option -o");
                    402:                         if ( (*argv)[2] == '\0' ) {
                    403:                             error("Missing revision range after -o");
                    404:                             break;
                    405:                         }
                    406:                         getdelrev( (*argv)+1 );
                    407:                         break;
                    408: 
                    409:                 case 's':   /*  change state attribute of a revision  */
                    410:                         if ( (*argv)[2] == '\0') {
                    411:                             error("State missing after -s");
                    412:                             break;
                    413:                         }
                    414:                         getstates( (*argv)+1);
                    415:                         break;
                    416: 
                    417:                 case 't':   /*  change descriptive text   */
                    418:                         textflag=true;
                    419:                         if ((*argv)[2]!='\0'){
                    420:                                 if (textfile!=nil)warn("Redefinition of -t option");
                    421:                                 textfile = (*argv)+2;
                    422:                         }
                    423:                         break;
                    424: 
                    425:                 case 'q':
                    426:                         quietflag = true;
                    427:                         break;
                    428:                 default:
                    429:                         faterror("Unknown option: %s\n%s", *argv, comdusge);
                    430:                 };
                    431:         }  /* end processing of options */
                    432: 
                    433:         if (argc<1) faterror("No input file\n%s", comdusge);
                    434:         if (nerror) {   /*  exit, if any error in command options  */
                    435:             diagnose("%s aborted",cmdid);
                    436:             exit(1);
                    437:         }
                    438:         if (accessfile) /*  get replacement for access list   */
                    439:             getrplaccess();
                    440:         if (nerror) {
                    441:             diagnose("%s aborted",cmdid);
                    442:             exit(1);
                    443:         }
                    444: 
                    445:         /* now handle all filenames */
                    446:         do {
                    447:         rewriteflag = false;
                    448:         finptr=frewrite=NULL;
                    449: 
                    450:         if ( initflag ) {
                    451:             switch( pairfilenames(argc, argv, false, false) ) {
                    452:                 case -1: break;        /*  not exist; ok */
                    453:                 case  0: continue;     /*  error         */
                    454:                 case  1: error("file %s exists already", RCSfilename);
                    455:                          VOID fclose(finptr);
                    456:                          continue;
                    457:             }
                    458:        }
                    459:         else  {
                    460:             switch( pairfilenames(argc, argv, true, false) ) {
                    461:                 case -1: continue;    /*  not exist      */
                    462:                 case  0: continue;    /*  errors         */
                    463:                 case  1: break;       /*  file exists; ok*/
                    464:             }
                    465:        }
                    466: 
                    467: 
                    468:         /* now RCSfilename contains the name of the RCS file, and
                    469:          * workfilename contains the name of the working file.
                    470:          * if !initflag, finptr contains the file descriptor for the
                    471:          * RCS file. The admin node is initialized.
                    472:          */
                    473: 
                    474:         diagnose("RCS file: %s", RCSfilename);
                    475: 
                    476:         if (!trydiraccess(RCSfilename))            continue; /* give up */
                    477:         if (!initflag && !checkaccesslist(caller)) continue; /* give up */
                    478:         if (!trysema(RCSfilename,true))            continue; /* give up */
                    479: 
                    480:         gettree(); /* read in delta tree */
                    481: 
                    482:         /*  update admin. node    */
                    483:         if (strict_selected) StrictLocks = strictlock;
                    484:         if (commentflag) Comment = commsyml;
                    485: 
                    486:         /* update default branch */
                    487:         if (branchflag && expandsym(branchsym, branchnum)) {
                    488:             if (countnumflds(branchnum)>0) {
                    489:                 branchdummy.num=branchnum;
                    490:                 Dbranch = &branchdummy;
                    491:             } else
                    492:                 Dbranch = nil;
                    493:         }
                    494: 
                    495:         /*  update access list   */
                    496:         if ( delaccessflag ) AccessList = nil;
                    497:         if ( accessfile ) {
                    498:             temp = rplaccessor;
                    499:             while( temp ) {
                    500:                 temptr = temp->nextaccess;
                    501:                 if ( addnewaccess(temp) )
                    502:                     temp->nextaccess = nil;
                    503:                 temp = temptr;
                    504:             }
                    505:         }
                    506:         temp = rmvaccessor;
                    507:         while(temp)   {         /*  remove accessors from accesslist   */
                    508:             AccessList = removeaccess(temp, AccessList,true);
                    509:             temp = temp->nextaccess;
                    510:         }
                    511:         temp = newaccessor;
                    512:         while( temp)  {         /*  add new accessors   */
                    513:             temptr = temp->nextaccess;
                    514:             if ( addnewaccess( temp ) )
                    515:                 temp->nextaccess = nil;
                    516:             temp = temptr;
                    517:         }
                    518: 
                    519:         updateassoc();          /*  update association list   */
                    520: 
                    521:         updatelocks();          /*  update locks              */
                    522: 
                    523:         /*  update state attribution  */
                    524:         if (chgheadstate) {
                    525:             /* change state of default branch or head */
                    526:             if (Dbranch==nil) {
                    527:                 if (Head==nil)
                    528:                      warn("Can't change states in an empty tree");
                    529:                 else Head->state = headstate;
                    530:             } else {
                    531:                 rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
                    532:             }
                    533:         }
                    534:         curstate = statelst;
                    535:         while( curstate ) {
                    536:             rcs_setstate(curstate->revno,curstate->status);
                    537:             curstate = curstate->nextstatus;
                    538:         }
                    539: 
                    540:         cuthead = cuttail = nil;
                    541:         if ( delrev && removerevs()) {
                    542:             /*  rebuild delta tree if some deltas are deleted   */
                    543:             if ( cuttail )
                    544:                VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
                    545:                             (char *)nil, gendeltas);
                    546:             buildtree();
                    547:         }
                    548: 
                    549: 
                    550:         /* prepare for rewriting the RCS file */
                    551:         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
                    552:         oldumask = umask(0222); /* turn off write bits */
                    553:         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
                    554:                 VOID fclose(finptr);
                    555:                 error("Can't open file %s",newRCSfilename);
                    556:                 continue;
                    557:         }
                    558:         VOID umask(oldumask);
                    559:         putadmin(frewrite);
                    560:         if ( Head )
                    561:            puttree(Head, frewrite);
                    562:        putdesc(initflag,textflag,textfile,quietflag);
                    563:         rewriteflag = false;
                    564: 
                    565:         if ( Head) {
                    566:             if (!delrev) {
                    567:                 /* no revision deleted */
                    568:                 fastcopy(finptr,frewrite);
                    569:             } else {
                    570:                 if ( cuttail )
                    571:                     buildeltatext(gendeltas);
                    572:                 else
                    573:                     scanlogtext((struct hshentry *)nil,empty);
                    574:                     /* copy rest of delta text nodes that are not deleted      */
                    575:             }
                    576:         }
                    577:         ffclose(frewrite);   frewrite = NULL;
                    578:         if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
                    579:            ignoreints();               /* ignore interrupts */
                    580:             if(rename(newRCSfilename,RCSfilename)<0) {
                    581:                 error("Can't create RCS file %s; saved in %s",
                    582:                    RCSfilename, newRCSfilename);
                    583:                 newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
                    584:                 restoreints();
                    585:                 VOID cleanup();
                    586:                 break;
                    587:             }
                    588:             newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
                    589:             /* update mode */
                    590:             result=0;
                    591:             if (!initflag) /* preserve mode bits */
                    592:                 result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
                    593:             elsif (haveworkstat==0)  /* initialization, and work file exists */
                    594:                 result=chmod(RCSfilename,workstat.st_mode & ~0222);
                    595:             if (result<0) warn("Can't set mode of %s",RCSfilename);
                    596: 
                    597:             restoreints();                /* catch them all again */
                    598:             diagnose("done");
                    599:         } else {
                    600:            diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
                    601:            status = 1;
                    602:            nerror = 0;
                    603:         }
                    604:         } while (cleanup(),
                    605:                  ++argv, --argc >=1);
                    606: 
                    607:         exit(status);
                    608: }       /* end of main (rcs) */
                    609: 
                    610: 
                    611: 
                    612: getassoclst(flag, sp)
                    613: int     flag;
                    614: char    * sp;
                    615: /*  Function:   associate a symbolic name to a revision or branch,      */
                    616: /*              and store in assoclst                                   */
                    617: 
                    618: {
                    619:         struct   Symrev  * pt;
                    620:         char             * temp, *temp2;
                    621:         int                c;
                    622: 
                    623:         while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
                    624:         temp = sp;
                    625:         temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
                    626:         sp = temp2; c = *sp;   *sp = '\0';
                    627:         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
                    628: 
                    629:         if ( c != ':' && c != '\0') {
                    630:            error("Invalid string %s after option -n or -N",sp);
                    631:             return;
                    632:         }
                    633: 
                    634:         pt = (struct Symrev *)talloc(sizeof(struct Symrev));
                    635:         pt->ssymbol = temp;
                    636:         pt->override = flag;
                    637:        if (c == '\0')  /*  delete symbol  */
                    638:             pt->revno = nil;
                    639:         else {
                    640:             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
                    641:            if ( c == '\0' )
                    642:                 pt->revno = nil;
                    643:            else
                    644:                 pt->revno = sp;
                    645:         }
                    646:         pt->nextsym = nil;
                    647:         if (lastassoc)
                    648:             lastassoc->nextsym = pt;
                    649:         else
                    650:             assoclst = pt;
                    651:         lastassoc = pt;
                    652:         return;
                    653: }
                    654: 
                    655: 
                    656: 
                    657: struct access * getaccessor( sp)
                    658: char            *sp;
                    659: /*   Function:  get the accessor list of options -e and -a,     */
                    660: /*              and store in curpt                              */
                    661: 
                    662: 
                    663: {
                    664:         struct  access  * curpt, * pt,  *pre;
                    665:         char    *temp;
                    666:         register c;
                    667: 
                    668:         while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
                    669:         if ( c == '\0') {
                    670:             error("Missing login name after option -a or -e");
                    671:             return nil;
                    672:         }
                    673: 
                    674:         curpt = pt = nil;
                    675:         while( c != '\0') {
                    676:                 temp=checkid(sp,',');
                    677:                 pt = (struct access *)talloc(sizeof(struct access));
                    678:                 pt->login = sp;
                    679:                 if ( curpt )
                    680:                     pre->nextaccess = pt;
                    681:                 else
                    682:                     curpt = pt;
                    683:                 pt->nextaccess = curpt;
                    684:                 pre = pt;
                    685:                 sp = temp;    c = *sp;   *sp = '\0';
                    686:                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
                    687:         }
                    688:         return pt;
                    689: }
                    690: 
                    691: 
                    692: 
                    693: getstates(sp)
                    694: char    *sp;
                    695: /*   Function:  get one state attribute and the corresponding   */
                    696: /*              revision and store in statelst                  */
                    697: 
                    698: {
                    699:         char    *temp, *temp2;
                    700:         struct  Status  *pt;
                    701:         register        c;
                    702: 
                    703:         while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
                    704:         temp = sp;
                    705:         temp2=checkid(sp,':');  /* check for invalid state attribute */
                    706:         sp = temp2;   c = *sp;   *sp = '\0';
                    707:         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
                    708: 
                    709:         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
                    710:             chgheadstate = true;
                    711:             headstate  = temp;
                    712:             return;
                    713:         }
                    714:         else if ( c != ':' ) {
                    715:             error("Missing ':' after state in option -s");
                    716:             return;
                    717:         }
                    718: 
                    719:         while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
                    720:         pt = (struct Status *)talloc(sizeof(struct Status));
                    721:         pt->status     = temp;
                    722:         pt->revno      = sp;
                    723:         pt->nextstatus = nil;
                    724:         if (laststate)
                    725:             laststate->nextstatus = pt;
                    726:         else
                    727:             statelst = pt;
                    728:         laststate = pt;
                    729: }
                    730: 
                    731: 
                    732: 
                    733: getrplaccess()
                    734: /*   Function : get the accesslist of the 'accessfile'  */
                    735: /*              and place in rplaccessor                */
                    736: {
                    737:         register        char    *id, *nextp;
                    738:         struct          access  *newaccess, *oldaccess;
                    739: 
                    740:         if ( (finptr=fopen(accessfile, "r")) == NULL) {
                    741:             faterror("Can't open file %s", accessfile);
                    742:         }
                    743:         Lexinit();
                    744:         nextp = &accessorlst[0];
                    745: 
                    746:         if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
                    747:         VOID getnum();
                    748:         if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
                    749: 
                    750:         if (getkey(Kbranch)) { /* optional */
                    751:                 Dbranch=getnum();
                    752:                 if (!getlex(SEMI)) serror("Missing ';' after branch list");
                    753:         }
                    754: 
                    755: 
                    756: #ifdef COMPAT2
                    757:         /* read suffix. Only in release 2 format */
                    758:         if (getkey(Ksuffix)) {
                    759:             if (nexttok==STRING) {
                    760:                 readstring(); nextlex(); /*through away the suffix*/
                    761:             } elsif(nexttok==ID) {
                    762:                 nextlex();
                    763:             }
                    764:             if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
                    765:         }
                    766: #endif
                    767: 
                    768:         if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
                    769:         oldaccess = nil;
                    770:         while( id =getid() ) {
                    771:             newaccess = (struct access *)talloc(sizeof(struct access));
                    772:             newaccess->login = nextp;
                    773:             newaccess->nextaccess = nil;
                    774:             while( ( *nextp++ = *id++) != '\0')  ;
                    775:             if ( oldaccess )
                    776:                 oldaccess->nextaccess = newaccess;
                    777:             else
                    778:                 rplaccessor = newaccess;
                    779:             oldaccess = newaccess;
                    780:         }
                    781:         if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
                    782:         return;
                    783: }
                    784: 
                    785: 
                    786: 
                    787: getdelrev(sp)
                    788: char    *sp;
                    789: /*   Function:  get revision range or branch to be deleted,     */
                    790: /*              and place in delrev                             */
                    791: {
                    792:         int    c;
                    793:         struct  delrevpair      *pt;
                    794: 
                    795:         if (delrev) free((char *)delrev);
                    796: 
                    797:         pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
                    798:         while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
                    799: 
                    800:         if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
                    801:             while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
                    802:             pt->strt = sp;    pt->code = 1;
                    803:             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
                    804:             *sp = '\0';
                    805:             pt->end = nil;  delrev = pt;
                    806:             return;
                    807:         }
                    808:         else {
                    809:             pt->strt = sp;
                    810:             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
                    811:                    && c != '-' && c != '<' )  c = *++sp;
                    812:             *sp = '\0';
                    813:             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
                    814:             if ( c == '\0' )  {  /*   -o rev or branch   */
                    815:                 pt->end = nil;   pt->code = 0;
                    816:                 delrev = pt;
                    817:                 return;
                    818:             }
                    819:             if ( c != '-' && c != '<') {
                    820:                 faterror("Invalid range %s %s after -o", pt->strt, sp);
                    821:                 free((char *)pt);
                    822:                 return;
                    823:             }
                    824:             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
                    825:             if ( c == '\0') {  /*  -o   rev-   or   rev<   */
                    826:                 pt->end = nil;   pt->code = 2;
                    827:                 delrev = pt;
                    828:                 return;
                    829:             }
                    830:         }
                    831:         /*   -o   rev1-rev2    or   rev1<rev2   */
                    832:         pt->end = sp;  pt->code = 3;   delrev = pt;
                    833:         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
                    834:         *sp = '\0';
                    835: }
                    836: 
                    837: 
                    838: 
                    839: 
                    840: scanlogtext(delta,func)
                    841: struct hshentry * delta; enum stringwork func;
                    842: /* Function: Scans delta text nodes up to and including the one given
                    843:  * by delta, or up to last one present, if delta==nil.
                    844:  * For the one given by delta (if delta!=nil), the log message is saved into
                    845:  * curlogmsg and the text is processed according to parameter func.
                    846:  * Assumes the initial lexeme must be read in first.
                    847:  * Does not advance nexttok after it is finished, except if delta==nil.
                    848:  */
                    849: {       struct hshentry * nextdelta;
                    850: 
                    851:         do {
                    852:                 rewriteflag = false;
                    853:                 nextlex();
                    854:                 if (!(nextdelta=getnum())) {
                    855:                     if(delta)
                    856:                         faterror("Can't find delta for revision %s", delta->num);
                    857:                     else return; /* no more delta text nodes */
                    858:                 }
                    859:                 if ( nextdelta->selector != DELETE) {
                    860:                         rewriteflag = true;
                    861:                         VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
                    862:                 }
                    863:                 if (!getkey(Klog) || nexttok!=STRING)
                    864:                         serror("Missing log entry");
                    865:                 elsif (delta==nextdelta) {
                    866:                         VOID savestring(curlogmsg,logsize);
                    867:                         delta->log=curlogmsg;
                    868:                 } else {readstring();
                    869:                         if (delta!=nil) delta->log="";
                    870:                 }
                    871:                 nextlex();
                    872:                 if (!getkey(Ktext) || nexttok!=STRING)
                    873:                         fatserror("Missing delta text");
                    874: 
                    875:                 if(delta==nextdelta)
                    876:                         /* got the one we're looking for */
                    877:                         switch (func) {
                    878:                         case copy:      copystring();
                    879:                                         break;
                    880:                         case edit:      editstring((struct hshentry *)nil);
                    881:                                         break;
                    882:                         default:        faterror("Wrong scanlogtext");
                    883:                         }
                    884:                 else    readstring(); /* skip over it */
                    885: 
                    886:         } while (delta!=nextdelta);
                    887: }
                    888: 
                    889: 
                    890: 
                    891: releaselst(sourcelst)
                    892: struct  access  * sourcelst;
                    893: /*   Function:  release the storages whose address are in sourcelst   */
                    894: 
                    895: {
                    896:         struct  access  * pt;
                    897: 
                    898:         pt = sourcelst;
                    899:         while(pt) {
                    900:            struct access *pn = pt->nextaccess;
                    901:             free((char *)pt);
                    902:             pt = pn;
                    903:         }
                    904: }
                    905: 
                    906: 
                    907: 
                    908: struct  Lockrev  * rmnewlocklst(which)
                    909: struct  Lockrev  * which;
                    910: /*   Function:  remove lock to revision which->revno from newlocklst   */
                    911: 
                    912: {
                    913:         struct  Lockrev   * pt, *pre;
                    914: 
                    915:         while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
                    916:            struct Lockrev *pn = newlocklst->nextrev;
                    917:             free((char *)newlocklst);
                    918:            newlocklst = pn;
                    919:         }
                    920: 
                    921:         pt = pre = newlocklst;
                    922:         while( pt ) {
                    923:             if ( ! strcmp(pt->revno, which->revno) ) {
                    924:                 pre->nextrev = pt->nextrev;
                    925:                 free((char *)pt);
                    926:                pt = pre->nextrev;
                    927:             }
                    928:             else {
                    929:                 pre = pt;
                    930:                 pt = pt->nextrev;
                    931:             }
                    932:         }
                    933:         return pre;
                    934: }
                    935: 
                    936: 
                    937: 
                    938: struct  access  * removeaccess( who, sourcelst,flag)
                    939: struct  access  * who, * sourcelst;
                    940: int     flag;
                    941: /*   Function:  remove the accessor-- who from sourcelst     */
                    942: 
                    943: {
                    944:         struct  access  *pt, *pre;
                    945: 
                    946:         pt = sourcelst;
                    947:         while( pt && (! strcmp(who->login, pt->login) )) {
                    948:            pre = pt->nextaccess;
                    949:             free((char *)pt);
                    950:            pt = pre;
                    951:             flag = false;
                    952:        }
                    953:         pre = sourcelst = pt;
                    954:         while( pt ) {
                    955:             if ( ! strcmp(who->login, pt->login) ) {
                    956:                pre->nextaccess = pt->nextaccess;
                    957:                free((char *)pt);
                    958:                pt = pre->nextaccess;
                    959:                 flag = false;
                    960:             }
                    961:             else {
                    962:                 pre = pt;
                    963:                 pt = pt->nextaccess;
                    964:             }
                    965:         }
                    966:         if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
                    967:         return sourcelst;
                    968: }
                    969: 
                    970: 
                    971: 
                    972: int addnewaccess( who )
                    973: struct  access  * who;
                    974: /*   Function:  add new accessor-- who into AccessList    */
                    975: 
                    976: {
                    977:         struct  access  *pt,  *pre;
                    978: 
                    979:         pre = pt = AccessList;
                    980: 
                    981:         while( pt ) {
                    982:             if ( strcmp( who->login, pt->login) ) {
                    983:                 pre = pt;
                    984:                 pt = pt->nextaccess;
                    985:             }
                    986:             else
                    987:                 return 0;
                    988:         }
                    989:         if ( pre == pt )
                    990:             AccessList = who;
                    991:         else
                    992:             pre->nextaccess = who;
                    993:         return 1;
                    994: }
                    995: 
                    996: 
                    997: sendmail(Delta, who)
                    998: char    * Delta,  *who;
                    999: /*   Function:  mail to who, informing him that his lock on delta was
                   1000:  *   broken by caller. Ask first whether to go ahead. Return false on
                   1001:  *   error or if user decides not to break the lock.
                   1002:  */
                   1003: {
                   1004:         char    * messagefile;
                   1005:         int   old1, old2, c, response;
                   1006:         FILE    * mailmess;
                   1007: 
                   1008: 
                   1009:        VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
                   1010:         VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
                   1011:         response=c=getchar();
                   1012:         while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
                   1013:         if (response=='\n'||response=='n'||response=='N') return false;
                   1014: 
                   1015:         /* go ahead with breaking  */
                   1016:         messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
                   1017:         if ( (mailmess = fopen(messagefile, "w")) == NULL) {
                   1018:             faterror("Can't open file %s", messagefile);
                   1019:         }
                   1020: 
                   1021:        VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
                   1022:         VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
                   1023:         VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
                   1024:         VOID fputs("State the reason for breaking the lock:\n", stderr);
                   1025:         VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
                   1026: 
                   1027:         old1 = '\n';    old2 = ' ';
                   1028:         for (; ;) {
                   1029:             c = getchar();
                   1030:             if ( c == EOF ) {
                   1031:                 VOID putc('\n',stderr);
                   1032:                 VOID fprintf(mailmess, "%c\n", old1);
                   1033:                 break;
                   1034:             }
                   1035:             else if ( c == '\n' && old1 == '.' && old2 == '\n')
                   1036:                 break;
                   1037:             else {
                   1038:                 VOID fputc( old1, mailmess);
                   1039:                 old2 = old1;   old1 = c;
                   1040:                 if (c== '\n') VOID fputs(">> ", stderr);
                   1041:             }
                   1042:         }
                   1043:         ffclose(mailmess);
                   1044: 
                   1045:        /* ignore the exit status, even if delivermail unsuccessful */
                   1046:         VOID run(messagefile,(char*)nil,
                   1047:                _PATH_SENDMAIL,
                   1048:                who,(char*)nil);
                   1049:         VOID unlink(messagefile);
                   1050:        return(true);
                   1051: }
                   1052: 
                   1053: 
                   1054: 
                   1055: static breaklock(who,delta)
                   1056: char * who; struct hshentry * delta;
                   1057: /* function: Finds the lock held by who on delta,
                   1058:  * and removes it.
                   1059:  * Sends mail if a lock different from the caller's is broken.
                   1060:  * Prints an error message if there is no such lock or error.
                   1061:  */
                   1062: {
                   1063:         register struct lock * next, * trail;
                   1064:         char * num;
                   1065:         struct lock dummy;
                   1066:         int whor, numr;
                   1067: 
                   1068:        num=delta->num;
                   1069:         dummy.nextlock=next=Locks;
                   1070:         trail = &dummy;
                   1071:         while (next!=nil) {
                   1072:                if (num != nil)
                   1073:                        numr = strcmp(num, next->delta->num);
                   1074: 
                   1075:                whor=strcmp(who,next->login);
                   1076:                if (whor==0 && numr==0) break; /* exact match */
                   1077:                if (numr==0 && whor !=0) {
                   1078:                         if (!sendmail( num, next->login)){
                   1079:                             diagnose("%s still locked by %s",num,next->login);
                   1080:                            return;
                   1081:                         } else break; /* continue after loop */
                   1082:                 }
                   1083:                 trail=next;
                   1084:                 next=next->nextlock;
                   1085:         }
                   1086:         if (next!=nil) {
                   1087:                 /*found one */
                   1088:                 diagnose("%s unlocked",next->delta->num);
                   1089:                 trail->nextlock=next->nextlock;
                   1090:                 next->delta->lockedby=nil;
                   1091:                 Locks=dummy.nextlock;
                   1092:         } else  {
                   1093:                error("no lock set on revision %s", num);
                   1094:         }
                   1095: }
                   1096: 
                   1097: 
                   1098: 
                   1099: struct hshentry *searchcutpt(object, length, store)
                   1100: char    * object;
                   1101: int     length;
                   1102: struct  hshentry   * * store;
                   1103: /*   Function:  Search store and return entry with number being object. */
                   1104: /*              cuttail = nil, if the entry is Head; otherwise, cuttail */
                   1105: /*              is the entry point to the one with number being object  */
                   1106: 
                   1107: {
                   1108:         while( compartial( (*store++)->num, object, length)  )  ;
                   1109:         store--;
                   1110: 
                   1111:         if ( *store == Head)
                   1112:             cuthead = nil;
                   1113:         else
                   1114:             cuthead = *(store -1);
                   1115:         return *store;
                   1116: }
                   1117: 
                   1118: 
                   1119: 
                   1120: int  branchpoint(strt, tail)
                   1121: struct  hshentry        *strt,  *tail;
                   1122: /*   Function: check whether the deltas between strt and tail  */
                   1123: /*             are locked or branch point, return 1 if any is  */
                   1124: /*             locked or branch point; otherwise, return 0 and */
                   1125: /*              mark DELETE on selector                         */
                   1126: 
                   1127: {
                   1128:         struct  hshentry    *pt;
                   1129:        struct lock  *lockpt;
                   1130:         int     flag;
                   1131: 
                   1132: 
                   1133:         pt = strt;
                   1134:         flag = false;
                   1135:         while( pt != tail) {
                   1136:             if ( pt->branches ){ /*  a branch point  */
                   1137:                 flag = true;
                   1138:                 error("Can't remove branch point %s", pt->num);
                   1139:             }
                   1140:            lockpt = Locks;
                   1141:            while(lockpt && lockpt->delta != pt)
                   1142:                lockpt = lockpt->nextlock;
                   1143:            if ( lockpt ) {
                   1144:                flag = true;
                   1145:                error("Can't remove locked revision %s",pt->num);
                   1146:            }
                   1147:             pt = pt->next;
                   1148:         }
                   1149: 
                   1150:         if ( ! flag ) {
                   1151:             pt = strt;
                   1152:             while( pt != tail ) {
                   1153:                 pt->selector = DELETE;
                   1154:                 diagnose("deleting revision %s ",pt->num);
                   1155:                 pt = pt->next;
                   1156:             }
                   1157:         }
                   1158:         return flag;
                   1159: }
                   1160: 
                   1161: 
                   1162: 
                   1163: removerevs()
                   1164: /*   Function:  get the revision range to be removed, and place the     */
                   1165: /*              first revision removed in delstrt, the revision before  */
                   1166: /*              delstrt in cuthead( nil, if delstrt is head), and the   */
                   1167: /*              revision after the last removed revision in cuttail(nil */
                   1168: /*              if the last is a leaf                                   */
                   1169: 
                   1170: {
                   1171:         struct  hshentry    *target, *target2, * temp, *searchcutpt();
                   1172:         int     length, flag;
                   1173: 
                   1174:         flag = false;
                   1175:         if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
                   1176:         target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
                   1177:         if ( ! target ) return 0;
                   1178:         if ( cmpnum(target->num, &numrev[0]) ) flag = true;
                   1179:         length = countnumflds( &numrev[0] );
                   1180: 
                   1181:         if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
                   1182:            if ( length % 2)
                   1183:                temp=searchcutpt(target->num,length+1,gendeltas);
                   1184:            else if (flag) {
                   1185:                 error("Revision %s does not exist", &numrev[0]);
                   1186:                return 0;
                   1187:            }
                   1188:            else
                   1189:                temp = searchcutpt(&numrev[0],length,gendeltas);
                   1190:            cuttail = target->next;
                   1191:             if ( branchpoint(temp, cuttail) ) {
                   1192:                 cuttail = nil;
                   1193:                 return 0;
                   1194:             }
                   1195:             delstrt = temp;     /* first revision to be removed   */
                   1196:             return 1;
                   1197:         }
                   1198: 
                   1199:         if ( length % 2 ) {   /*  invalid branch after -o   */
                   1200:             error("Invalid branch range %s after -o", &numrev[0]);
                   1201:             return 0;
                   1202:         }
                   1203: 
                   1204:         if ( delrev->code == 1 )  {  /*  -o  -rev   */
                   1205:             if ( length > 2 ) {
                   1206:                 temp = searchcutpt( target->num, length-1, gendeltas);
                   1207:                 cuttail = target->next;
                   1208:             }
                   1209:             else {
                   1210:                 temp = searchcutpt(target->num, length, gendeltas);
                   1211:                 cuttail = target;
                   1212:                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
                   1213:                     cuttail = cuttail->next;
                   1214:             }
                   1215:             if ( branchpoint(temp, cuttail) ){
                   1216:                 cuttail = nil;
                   1217:                 return 0;
                   1218:             }
                   1219:             delstrt = temp;
                   1220:             return 1;
                   1221:         }
                   1222: 
                   1223:         if ( delrev->code == 2 )  {   /*  -o  rev-   */
                   1224:             if ( length == 2 ) {
                   1225:                 temp = searchcutpt(target->num, 1,gendeltas);
                   1226:                 if ( flag)
                   1227:                     cuttail = target;
                   1228:                 else
                   1229:                     cuttail = target->next;
                   1230:             }
                   1231:             else  {
                   1232:                 if ( flag){
                   1233:                     cuthead = target;
                   1234:                     if ( !(temp = target->next) ) return 0;
                   1235:                 }
                   1236:                 else
                   1237:                     temp = searchcutpt(target->num, length, gendeltas);
                   1238:                 getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
                   1239:                 target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
                   1240:             }
                   1241:             if ( branchpoint( temp, cuttail ) ) {
                   1242:                 cuttail = nil;
                   1243:                 return 0;
                   1244:             }
                   1245:             delstrt = temp;
                   1246:             return 1;
                   1247:         }
                   1248: 
                   1249:         /*   -o   rev1-rev2   */
                   1250:         if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
                   1251:         if ( length != countnumflds( &numrev[0] ) ) {
                   1252:             error("Invalid revision range %s-%s", target->num, &numrev[0]);
                   1253:             return 0;
                   1254:         }
                   1255:         if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
                   1256:             error("Invalid revision range %s-%s", target->num, &numrev[0]);
                   1257:             return 0;
                   1258:         }
                   1259: 
                   1260:         target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
                   1261:         if ( ! target2 ) return 0;
                   1262: 
                   1263:         if ( length > 2) {  /* delete revisions on branches  */
                   1264:             if ( cmpnum(target->num, target2->num) > 0) {
                   1265:                 if ( cmpnum(target2->num, &numrev[0]) )
                   1266:                     flag = true;
                   1267:                 else
                   1268:                     flag = false;
                   1269:                 temp = target;
                   1270:                 target = target2;
                   1271:                 target2 = temp;
                   1272:             }
                   1273:             if ( flag ) {
                   1274:                 if ( ! cmpnum(target->num, target2->num) ) {
                   1275:                     error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
                   1276:                     return 0;
                   1277:                 }
                   1278:                 cuthead = target;
                   1279:                 temp = target->next;
                   1280:             }
                   1281:             else
                   1282:                 temp = searchcutpt(target->num, length, gendeltas);
                   1283:             cuttail = target2->next;
                   1284:         }
                   1285:         else { /*  delete revisions on trunk  */
                   1286:             if ( cmpnum( target->num, target2->num) < 0 ) {
                   1287:                 temp = target;
                   1288:                 target = target2;
                   1289:                 target2 = temp;
                   1290:             }
                   1291:             else
                   1292:                 if ( cmpnum(target2->num, &numrev[0]) )
                   1293:                     flag = true;
                   1294:                 else
                   1295:                     flag = false;
                   1296:             if ( flag ) {
                   1297:                 if ( ! cmpnum(target->num, target2->num) ) {
                   1298:                     error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
                   1299:                     return 0;
                   1300:                 }
                   1301:                 cuttail = target2;
                   1302:             }
                   1303:             else
                   1304:                 cuttail = target2->next;
                   1305:             temp = searchcutpt(target->num, length, gendeltas);
                   1306:         }
                   1307:         if ( branchpoint(temp, cuttail) )  {
                   1308:             cuttail = nil;
                   1309:             return 0;
                   1310:         }
                   1311:         delstrt = temp;
                   1312:         return 1;
                   1313: }
                   1314: 
                   1315: 
                   1316: 
                   1317: updateassoc()
                   1318: /*   Function: add or delete(if revno is nil) association      */
                   1319: /*             which is stored in assoclst                     */
                   1320: 
                   1321: {
                   1322:         struct  Symrev  * curassoc;
                   1323:        struct  assoc   * pre,  * pt;
                   1324:         struct  hshentry    * target;
                   1325: 
                   1326:         /*  add new associations   */
                   1327:         curassoc = assoclst;
                   1328:         while( curassoc ) {
                   1329:             if ( curassoc->revno == nil ) {  /* delete symbol  */
                   1330:                pre = pt = Symbols;
                   1331:                 while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
                   1332:                    pre = pt;
                   1333:                    pt = pt->nextassoc;
                   1334:                }
                   1335:                if ( pt )
                   1336:                    if ( pre == pt )
                   1337:                        Symbols = pt->nextassoc;
                   1338:                    else
                   1339:                        pre->nextassoc = pt->nextassoc;
                   1340:                else
                   1341:                     warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
                   1342:            }
                   1343:             else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
                   1344:            /*   add symbol  */
                   1345:                target = (struct hshentry *) talloc(sizeof(struct hshentry));
                   1346:                target->num = &numrev[0];
                   1347:                VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
                   1348:             }
                   1349:             curassoc = curassoc->nextsym;
                   1350:         }
                   1351: 
                   1352: }
                   1353: 
                   1354: 
                   1355: 
                   1356: updatelocks()
                   1357: /* Function: remove lock for caller or first lock if unlockcaller==true;
                   1358:  *           remove locks which are stored in rmvlocklst,
                   1359:  *           add new locks which are stored in newlocklst,
                   1360:  *           add lock for Dbranch or Head if lockhead==true.
                   1361:  */
                   1362: {
                   1363:         struct  hshentry        *target;
                   1364:         struct  Lockrev         *lockpt;
                   1365: 
                   1366:         if(unlockcaller == true) { /*  find lock for caller  */
                   1367:             if ( Head ) {
                   1368:                if (Locks) {
                   1369:                    target=findlock(caller,true);
                   1370:                    if (target==nil) {
                   1371:                        breaklock(caller, Locks->delta); /* remove most recent lock */
                   1372:                    } else {
                   1373:                        diagnose("%s unlocked",target->num);
                   1374:                    }
                   1375:                } else {
                   1376:                    warn("There are no locks set.");
                   1377:                }
                   1378:             } else {
                   1379:                 warn("Can't unlock an empty tree");
                   1380:             }
                   1381:         }
                   1382: 
                   1383:         /*  remove locks which are stored in rmvlocklst   */
                   1384:         lockpt = rmvlocklst;
                   1385:         while( lockpt ) {
                   1386:            if (expandsym(lockpt->revno, numrev)) {
                   1387:                target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
                   1388:                 if ( target )
                   1389:                   if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
                   1390:                        error("Can't unlock nonexisting revision %s",lockpt->revno);
                   1391:                    else
                   1392:                         breaklock(caller, target);
                   1393:                         /* breaklock does its own diagnose */
                   1394:             }
                   1395:             lockpt = lockpt->nextrev;
                   1396:         }
                   1397: 
                   1398:         /*  add new locks which stored in newlocklst  */
                   1399:         lockpt = newlocklst;
                   1400:         while( lockpt ) {
                   1401:             setlock(lockpt->revno,caller);
                   1402:             lockpt = lockpt->nextrev;
                   1403:         }
                   1404: 
                   1405:         if ( lockhead == true) {  /*  lock default branch or head  */
                   1406:             if (Dbranch) {
                   1407:                 setlock(Dbranch->num,caller);
                   1408:             } elsif ( Head) {
                   1409:                 if (addlock(Head, caller))
                   1410:                     diagnose("%s locked",Head->num);
                   1411:             } else {
                   1412:                 warn("Can't lock an empty tree");
                   1413:             }
                   1414:         }
                   1415: 
                   1416: }
                   1417: 
                   1418: 
                   1419: 
                   1420: setlock(rev,who)
                   1421: char * rev, * who;
                   1422: /* Function: Given a revision or branch number, finds the correponding
                   1423:  * delta and locks it for who.
                   1424:  */
                   1425: {
                   1426:         struct  lock     *lpt;
                   1427:         struct  hshentry *target;
                   1428: 
                   1429:         if (expandsym(rev, &numrev[0]) ){
                   1430:             target = genrevs(&numrev[0],(char *) nil,(char *) nil,
                   1431:                             (char *)nil, gendeltas);
                   1432:             if ( target )
                   1433:                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
                   1434:                     error("Can't lock nonexisting revision %s",numrev);
                   1435:                else
                   1436:                     if(lpt=addlock(target, who))
                   1437:                         diagnose("%s locked",lpt->delta->num);
                   1438:         }
                   1439: }
                   1440: 
                   1441: 
                   1442: 
                   1443: rcs_setstate(rev,status)
                   1444: char * rev, * status;
                   1445: /* Function: Given a revision or branch number, finds the corresponding delta
                   1446:  * and sets its state to status.
                   1447:  */
                   1448: {
                   1449:         struct  hshentry *target;
                   1450: 
                   1451:         if ( expandsym(rev, &numrev[0]) ) {
                   1452:             target = genrevs(&numrev[0],(char *) nil, (char *)nil,
                   1453:                             (char *) nil, gendeltas);
                   1454:             if ( target )
                   1455:                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
                   1456:                     error("Can't set state of nonexisting revision %s to %s",
                   1457:                            numrev,status);
                   1458:                else
                   1459:                     target->state = status;
                   1460:         }
                   1461: }
                   1462: 
                   1463: 
                   1464: 
                   1465: 
                   1466: 
                   1467: buildeltatext(deltas)
                   1468: struct  hshentry        ** deltas;
                   1469: /*   Function:  put the delta text on frewrite and make necessary   */
                   1470: /*              change to delta text                                */
                   1471: {
                   1472:         int  i, c, exit_stats;
                   1473: 
                   1474:         cuttail->selector = DELETE;
                   1475:         initeditfiles("/tmp/");
                   1476:         scanlogtext(deltas[0], copy);
                   1477:         i = 1;
                   1478:         if ( cuthead )  {
                   1479:             cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
                   1480:             if ( (fcut = fopen(cutfilename, "w")) == NULL) {
                   1481:                 faterror("Can't open temporary file %s", cutfilename);
                   1482:             }
                   1483: 
                   1484:             while( deltas[i-1] != cuthead )  {
                   1485:                 scanlogtext(deltas[i++], edit);
                   1486:             }
                   1487: 
                   1488:             finishedit((struct hshentry *)nil);    rewind(fcopy);
                   1489:             while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
                   1490:             swapeditfiles(false);
                   1491:             ffclose(fcut);
                   1492:         }
                   1493: 
                   1494:         while( deltas[i-1] != cuttail)
                   1495:             scanlogtext(deltas[i++], edit);
                   1496:         finishedit((struct hshentry *)nil);    ffclose(fcopy);
                   1497: 
                   1498:         if ( cuthead ) {
                   1499:             diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
                   1500:             exit_stats = run((char*)nil,diffilename,
                   1501:                        DIFF,"-n",cutfilename,resultfile,(char*)nil);
                   1502:             if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
                   1503:                 faterror ("diff failed");
                   1504:             if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
                   1505:         }
                   1506:         else
                   1507:             if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
                   1508: 
                   1509:         scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
                   1510: }
                   1511: 
                   1512: 
                   1513: 
                   1514: buildtree()
                   1515: /*   Function:  actually removes revisions whose selector field  */
                   1516: /*              is DELETE, and rebuilds  the linkage of deltas.  */
                   1517: /*              asks for reconfirmation if deleting last revision*/
                   1518: {
                   1519:        int c,  response;
                   1520: 
                   1521:        struct  hshentry   * Delta;
                   1522:         struct  branchhead      *pt, *pre;
                   1523: 
                   1524:         if ( cuthead )
                   1525:            if ( cuthead->next == delstrt )
                   1526:                 cuthead->next = cuttail;
                   1527:            else {
                   1528:                 pre = pt = cuthead->branches;
                   1529:                 while( pt && pt->hsh != delstrt )  {
                   1530:                     pre = pt;
                   1531:                     pt = pt->nextbranch;
                   1532:                 }
                   1533:                 if ( cuttail )
                   1534:                     pt->hsh = cuttail;
                   1535:                 else if ( pt == pre )
                   1536:                     cuthead->branches = pt->nextbranch;
                   1537:                 else
                   1538:                     pre->nextbranch = pt->nextbranch;
                   1539:             }
                   1540:        else {
                   1541:             if ( cuttail == nil && !quietflag) {
                   1542:                 VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
                   1543:                c = response = getchar();
                   1544:                while( c != EOF && c != '\n') c = getchar();
                   1545:                 if ( response != 'y' && response != 'Y') {
                   1546:                     diagnose("No revision deleted");
                   1547:                    Delta = delstrt;
                   1548:                    while( Delta) {
                   1549:                        Delta->selector = 'S';
                   1550:                        Delta = Delta->next;
                   1551:                    }
                   1552:                    return;
                   1553:                }
                   1554:            }
                   1555:             Head = cuttail;
                   1556:        }
                   1557:         return;
                   1558: }
                   1559: 

unix.superglobalmegacorp.com

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