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
branchpoint defined in line 1095; used 4 times
breaklock defined in line 1028; used 3 times
buildeltatext defined in line 1442; used 1 times
buildtree defined in line 1489; used 1 times
getaccessor defined in line 631; used 3 times
getassoclst defined in line 586; used 2 times
getdelrev defined in line 755; used 1 times
getrplaccess defined in line 707; used 1 times
getstates defined in line 667; used 1 times
main defined in line 202; never used
rcs_setstate defined in line 1418; used 2 times
releaselst defined in line 859; used 2 times
removeaccess defined in line 904; used 3 times
removerevs defined in line 1138; used 1 times
rmnewlocklst defined in line 875; used 2 times
scanlogtext defined in line 808; used 5 times
searchcutpt defined in line 1074; used 9 times
sendmail defined in line 962; used 1 times
setlock defined in line 1395; used 2 times
updateassoc defined in line 1292; used 1 times
updatelocks defined in line 1331; used 1 times

Defined variables

RCSfilename defined in line 149; used 13 times
accessfile defined in line 158; used 13 times
accessorlst defined in line 153; used 1 times
assoclst defined in line 189; used 3 times
branchdummy defined in line 194; used 2 times
branchflag defined in line 197; used 4 times
branchnum defined in line 193; used 3 times
branchsym defined in line 193; used 2 times
caller defined in line 159; used 10 times
chgheadstate defined in line 197; used 3 times
command defined in line 195; used 6 times
commentflag defined in line 197; used 4 times
commsyml defined in line 195; used 2 times
curaccess defined in line 161; used 11 times
cutfilename defined in line 148; used 4 times
cuthead defined in line 192; used 13 times
cuttail defined in line 192; used 33 times
delaccessflag defined in line 198; used 3 times
delrev defined in line 191; used 18 times
delstrt defined in line 192; used 8 times
diffilename defined in line 148; used 3 times
gendeltas defined in line 162; used 16 times
headstate defined in line 196; used 3 times
initflag defined in line 157; used 6 times
lastassoc defined in line 189; used 4 times
laststate defined in line 190; used 4 times
lockhead defined in line 197; used 3 times
newRCSfilename defined in line 148; used 7 times
newaccessor defined in line 160; used 9 times
newlocklst defined in line 188; used 9 times
numrev defined in line 159; used 33 times
oldumask defined in line 155; used 2 times
rcsbaseid defined in line 111; never used
rcsid defined in line 5; never used
rmaccess defined in line 161; used 4 times
rmvaccessor defined in line 160; used 6 times
rmvlocklst defined in line 188; used 3 times
rplaccessor defined in line 160; used 3 times
statelst defined in line 190; used 3 times
strict_selected defined in line 157; used 4 times
strictlock defined in line 157; used 5 times
textfile defined in line 158; used 4 times
textflag defined in line 157; used 3 times
unlockcaller defined in line 197; used 3 times
workfilename defined in line 149; never used

Defined struct's

Lockrev defined in line 164; used 24 times
Status defined in line 176; used 12 times
Symrev defined in line 169; used 12 times
delrevpair defined in line 182; used 8 times

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: 7404
Valid CSS Valid XHTML 1.0 Strict