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