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

Defined functions

addnewaccess defined in line 884; used 2 times
branchpoint defined in line 1042; used 4 times
breaklock defined in line 973; used 3 times
buildeltatext defined in line 1321; used 1 times
buildtree defined in line 1368; used 1 times
getaccessor defined in line 578; used 3 times
getassoclst defined in line 533; used 2 times
getdelrev defined in line 702; used 1 times
getrplaccess defined in line 654; used 1 times
getstates defined in line 614; used 1 times
main defined in line 153; never used
releaselst defined in line 806; used 2 times
removeaccess defined in line 851; used 3 times
removerevs defined in line 1085; used 1 times
rmnewlocklst defined in line 822; used 2 times
scanlogtext defined in line 755; used 5 times
searchcutpt defined in line 1021; used 9 times
sendmail defined in line 909; used 1 times
updateassoc defined in line 1239; used 1 times
updatelock defined in line 1278; used 1 times

Defined variables

RCSfilename defined in line 101; used 12 times
accessfile defined in line 111; used 13 times
accessorlst defined in line 103; used 1 times
assoclst defined in line 142; used 3 times
caller defined in line 112; used 8 times
chgheadstate defined in line 148; used 3 times
command defined in line 146; used 5 times
commentflag defined in line 148; used 4 times
commsyml defined in line 146; used 2 times
curaccess defined in line 114; used 11 times
cutfilename defined in line 102; used 4 times
cuthead defined in line 145; used 13 times
cuttail defined in line 145; used 33 times
delaccessflag defined in line 149; used 3 times
delrev defined in line 144; used 18 times
delstrt defined in line 145; used 8 times
diffilename defined in line 102; used 3 times
filestatus defined in line 107; used 2 times
gendeltas defined in line 115; used 16 times
headoverride defined in line 148; never used
headstate defined in line 147; used 2 times
initflag defined in line 110; used 6 times
lastassoc defined in line 142; used 4 times
laststate defined in line 143; used 4 times
lockhead defined in line 148; used 3 times
newRCSfilename defined in line 102; used 7 times
newaccessor defined in line 113; used 9 times
newlocklst defined in line 141; used 9 times
numrev defined in line 112; used 31 times
oldumask defined in line 108; used 2 times
rcsbaseid defined in line 71; never used
rcsid defined in line 4; never used
rewriteflag defined in line 106; used 4 times
rmaccess defined in line 114; used 4 times
rmvaccessor defined in line 113; used 6 times
rmvlocklst defined in line 141; used 3 times
rplaccessor defined in line 113; used 3 times
statelst defined in line 143; used 3 times
strict_selected defined in line 110; used 4 times
strictlock defined in line 110; used 5 times
textfile defined in line 111; used 4 times
textflag defined in line 110; used 3 times
unlockcaller defined in line 148; used 3 times
workfilename defined in line 101; never used

Defined struct's

Lockrev defined in line 117; used 24 times
Status defined in line 129; used 12 times
Symrev defined in line 122; used 12 times
delrevpair defined in line 135; used 8 times

Defined enum's

stringwork defined in line 150; used 2 times
  • in line 756(2)
Last modified: 1985-10-24
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3516
Valid CSS Valid XHTML 1.0 Strict