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

##### Functions

    addnewaccess      X
    branchpoint       X
    breaklock         X
    buildeltatext     X
    buildtree         X
    getaccessor       X
    getassoclst       X
    getdelrev         X
    getrplaccess      X
    getstates         X
    main              X
    rcs_setstate      X
    releaselst        X
    removeaccess      X
    removerevs        X
    rmnewlocklst      X
    scanlogtext       X
    searchcutpt       X
    sendmail          X
    setlock           X
    updateassoc       X
    updatelocks       X

##### Variables

    RCSfilename       X
    accessfile        X
    accessorlst       X
    assoclst          X
    branchdummy       X
    branchflag        X
    branchnum         X
    branchsym         X
    caller            X
    chgheadstate      X
    command           X
    commentflag       X
    commsyml          X
    curaccess         X
    cutfilename       X
    cuthead           X
    cuttail           X
    delaccessflag     X
    delrev            X
    delstrt           X
    diffilename       X
    gendeltas         X
    headstate         X
    initflag          X
    lastassoc         X
    laststate         X
    lockhead          X
    newRCSfilename    X
    newaccessor       X
    newlocklst        X
    numrev            X
    oldumask          X
    rmaccess          X
    rmvaccessor       X
    rmvlocklst        X
    rplaccessor       X
    statelst          X
    strict_selected   X
    strictlock        X
    textfile          X
    textflag          X
    unlockcaller      X
    workfilename      X

##### struct's

    Lockrev           X
    Status            X
    Symrev            X
    delrevpair        X

##### enums's

    stringwork        X

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

#### Defined functions

addnewaccess defined in line 937; used 2 times

- in line 477, 490

branchpoint defined in line 1095; used 4 times

- in line 1166, 1190, 1216, 1282

breaklock defined in line 1028; used 3 times

- in line 128, 1346, 1367

buildeltatext defined in line 1442; used 1 times

- in line 547

buildtree defined in line 1489; used 1 times

- in line 522

getaccessor defined in line 631; used 3 times

- in line 208, 257, 290

getassoclst defined in line 586; used 2 times

- in line 364, 372

getdelrev defined in line 755; used 1 times

- in line 381

getrplaccess defined in line 707; used 1 times

- in line 414

getstates defined in line 667; used 1 times

- in line 389

main defined in line 202; never used
rcs_setstate defined in line 1418; used 2 times

- in line 507-512(2)

releaselst defined in line 859; used 2 times

- in line 275-277(2)

removeaccess defined in line 904; used 3 times

- in line 208, 299, 484

removerevs defined in line 1138; used 1 times

- in line 517

rmnewlocklst defined in line 875; used 2 times

- in line 209, 339

scanlogtext defined in line 808; used 5 times

- in line 549, 1451, 1460, 1470, 1484

searchcutpt defined in line 1074; used 9 times

- in line 1146, 1158, 1164, 1181-1185(2), 1200, 1212, 1257, 1280

sendmail defined in line 962; used 1 times

- in line 1051

setlock defined in line 1395; used 2 times

- in line 1376, 1382

updateassoc defined in line 1292; used 1 times

- in line 495

updatelocks defined in line 1331; used 1 times

- in line 497

#### Defined variables

RCSfilename defined in line 149; used 13 times

- in line 279, 430, 450-454(3), 527, 556-558(2), 568-576(4), 986

accessfile defined in line 158; used 13 times

- in line 220, 272, 279-282(2), 413, 473, 714-722(4), 732-736(2), 749

accessorlst defined in line 153; used 1 times

- in line 718

assoclst defined in line 189; used 3 times

- in line 228, 624, 1302

branchdummy defined in line 194; used 2 times

- in line 465-466(2)

branchflag defined in line 197; used 4 times

- in line 221, 242-243(2), 463

branchnum defined in line 193; used 3 times

- in line 463-465(3)

branchsym defined in line 193; used 2 times

- in line 244, 463

caller defined in line 159; used 10 times

- in line 220, 226, 453, 988, 1344-1346(2), 1367, 1376, 1382-1384(2)

chgheadstate defined in line 197; used 3 times

- in line 221, 500, 684

command defined in line 195; used 6 times

- in line 1011-1020(4), 1475-1476(2)

commentflag defined in line 197; used 4 times

- in line 221, 248-249(2), 460

commsyml defined in line 195; used 2 times

- in line 250, 460

curaccess defined in line 161; used 11 times

- in line 230, 259-263(2), 276, 302-304(2), 712, 737, 743-747(3)

cutfilename defined in line 148; used 4 times

- in line 1454-1456(3), 1475

cuthead defined in line 192; used 13 times

- in line 516, 1087-1089(2), 1208, 1253, 1453, 1459, 1473, 1499-1503(4), 1511

cuttail defined in line 192; used 33 times

- in line 516-520(3), 546, 1165-1167(3), 1182-1191(8), 1202-1204(2), 1216-1217(2), 1258, 1276-1283(4), 1449, 1469, 1479-1482(2), 1501, 1508-1509(2), 1516, 1530

delaccessflag defined in line 198; used 3 times

- in line 231, 287, 472

delrev defined in line 191; used 18 times

- in line 376, 517, 542, 763(2), 773, 784, 795-800(2), 1150, 1156, 1179, 1198, 1225, 1250(2), 1273(2)

delstrt defined in line 192; used 8 times

- in line 219, 1170, 1194, 1220, 1286, 1500-1504(2), 1522

diffilename defined in line 148; used 3 times

- in line 1474-1479(3)

gendeltas defined in line 162; used 16 times

- in line 521, 547, 1151, 1158, 1164, 1181-1185(2), 1200, 1212-1214(2), 1235, 1257, 1280, 1362, 1406, 1428

headstate defined in line 196; used 3 times

- in line 505-507(2), 685

initflag defined in line 157; used 6 times

- in line 223, 238, 426, 453, 538, 567

lastassoc defined in line 189; used 4 times

- in line 228, 621-625(3)

laststate defined in line 190; used 4 times

- in line 227, 698-702(3)

lockhead defined in line 197; used 3 times

- in line 222, 312, 1380

newRCSfilename defined in line 148; used 7 times

- in line 527-531(3), 556-564(4)

newaccessor defined in line 160; used 9 times

- in line 230, 258-261(2), 275-276(2), 299-302(3), 487

newlocklst defined in line 188; used 9 times

- in line 229, 321, 882-887(6), 1374

numrev defined in line 159; used 33 times

- in line 1150-1154(4), 1160-1164(2), 1175, 1213-1214(2), 1225-1240(7), 1267, 1318-1321(2), 1361-1364(4), 1404-1409(5), 1426-1432(5)

oldumask defined in line 155; used 2 times

- in line 528, 534

rcsbaseid defined in line 111; never used
rcsid defined in line 5; never used
rmaccess defined in line 161; used 4 times

- in line 230, 278, 292-297(2)

rmvaccessor defined in line 160; used 6 times

- in line 230, 277-278(2), 291-294(2), 482

rmvlocklst defined in line 188; used 3 times

- in line 229, 336, 1359

rplaccessor defined in line 160; used 3 times

- in line 219, 474, 746

statelst defined in line 190; used 3 times

- in line 227, 510, 701

strict_selected defined in line 157; used 4 times

- in line 224, 343, 351, 459

strictlock defined in line 157; used 5 times

- in line 344-356(4), 459

textfile defined in line 158; used 4 times

- in line 220, 395-396(2), 538

textflag defined in line 157; used 3 times

- in line 223, 393, 538

unlockcaller defined in line 197; used 3 times

- in line 222, 327, 1341

workfilename defined in line 149; never used

#### Defined struct's

Lockrev defined in line 164; used 24 times

- in line 166(2), 188(2), 209-210(4), 315(4), 330(4), 875-880(6), 1339(2)

Status defined in line 176; used 12 times

- in line 179(2), 190(2), 211(2), 674(2), 694(4)

Symrev defined in line 169; used 12 times

- in line 173(2), 189(2), 593(2), 608(4), 1297(2)

delrevpair defined in line 182; used 8 times

- in line 191(2), 761-765(6)

#### Defined enum's

stringwork defined in line 199; used 2 times

- in line 809(2)

Last modified: 1994-07-11 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 4404 |