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