Annotation of 43BSDTahoe/new/rcs/src/rcs.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

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