Annotation of 43BSDReno/contrib/rcs/src/rcs.c, revision 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.