150476Speter/* Change RCS file attributes. */ 247857Sbrian 347857Sbrian/* Copyright 1982, 1988, 1989 Walter Tichy 447857Sbrian Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 547857Sbrian Distributed under license by the Free Software Foundation, Inc. 647857Sbrian 747857SbrianThis file is part of RCS. 847857Sbrian 947857SbrianRCS is free software; you can redistribute it and/or modify 1047857Sbrianit under the terms of the GNU General Public License as published by 1147857Sbrianthe Free Software Foundation; either version 2, or (at your option) 1247857Sbrianany later version. 1347857Sbrian 1447857SbrianRCS is distributed in the hope that it will be useful, 1547857Sbrianbut WITHOUT ANY WARRANTY; without even the implied warranty of 1647857SbrianMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1747857SbrianGNU General Public License for more details. 1847857Sbrian 1947857SbrianYou should have received a copy of the GNU General Public License 2047857Sbrianalong with RCS; see the file COPYING. 2147857SbrianIf not, write to the Free Software Foundation, 2247857Sbrian59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2347857Sbrian 2447857SbrianReport problems and direct all questions to: 2547857Sbrian 2647857Sbrian rcs-bugs@cs.purdue.edu 2747857Sbrian 2847857Sbrian*/ 2947857Sbrian 3047857Sbrian/* 3147857Sbrian * Revision 5.21 1995/06/16 06:19:24 eggert 3247857Sbrian * Update FSF address. 3347857Sbrian * 3447857Sbrian * Revision 5.20 1995/06/01 16:23:43 eggert 3547857Sbrian * (main): Warn if no options were given. Punctuate messages properly. 3647857Sbrian * 3747857Sbrian * (sendmail): Rewind mailmess before flushing it. 3847857Sbrian * Output another warning if mail should work but fails. 3947857Sbrian * 4047857Sbrian * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference. 4147857Sbrian * 4247857Sbrian * Revision 5.19 1994/03/17 14:05:48 eggert 4347857Sbrian * Use ORCSerror to clean up after a fatal error. Remove lint. 4447857Sbrian * Specify subprocess input via file descriptor, not file name. Remove lint. 4547857Sbrian * Flush stderr after prompt. 4647857Sbrian * 4747857Sbrian * Revision 5.18 1993/11/09 17:40:15 eggert 4847857Sbrian * -V now prints version on stdout and exits. Don't print usage twice. 4947857Sbrian * 5047857Sbrian * Revision 5.17 1993/11/03 17:42:27 eggert 5147857Sbrian * Add -z. Don't lose track of -m or -t when there are no other changes. 5247857Sbrian * Don't discard ignored phrases. Improve quality of diagnostics. 5347857Sbrian * 5447857Sbrian * Revision 5.16 1992/07/28 16:12:44 eggert 5547857Sbrian * rcs -l now asks whether you want to break the lock. 5647857Sbrian * Add -V. Set RCS file's mode and time at right moment. 5747857Sbrian * 5847857Sbrian * Revision 5.15 1992/02/17 23:02:20 eggert 5947857Sbrian * Add -T. 6047857Sbrian * 6147857Sbrian * Revision 5.14 1992/01/27 16:42:53 eggert 6247857Sbrian * Add -M. Avoid invoking umask(); it's one less thing to configure. 6347857Sbrian * Add support for bad_creat0. lint -> RCS_lint 6447857Sbrian * 6547857Sbrian * Revision 5.13 1992/01/06 02:42:34 eggert 6647857Sbrian * Avoid changing RCS file in common cases where no change can occur. 6747857Sbrian * 6847857Sbrian * Revision 5.12 1991/11/20 17:58:08 eggert 6947857Sbrian * Don't read the delta tree from a nonexistent RCS file. 7047857Sbrian * 7147857Sbrian * Revision 5.11 1991/10/07 17:32:46 eggert 7247857Sbrian * Remove lint. 7347857Sbrian * 7447857Sbrian * Revision 5.10 1991/08/19 23:17:54 eggert 7547857Sbrian * Add -m, -r$, piece tables. Revision separator is `:', not `-'. Tune. 7647857Sbrian * 7747857Sbrian * Revision 5.9 1991/04/21 11:58:18 eggert 7847857Sbrian * Add -x, RCSINIT, MS-DOS support. 7947857Sbrian * 8047857Sbrian * Revision 5.8 1991/02/25 07:12:38 eggert 8147857Sbrian * strsave -> str_save (DG/UX name clash) 8247857Sbrian * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability 8347857Sbrian * 8447857Sbrian * Revision 5.7 1990/12/18 17:19:21 eggert 8547857Sbrian * Fix bug with multiple -n and -N options. 8647857Sbrian * 8747857Sbrian * Revision 5.6 1990/12/04 05:18:40 eggert 8847857Sbrian * Use -I for prompts and -q for diagnostics. 8947857Sbrian * 9047857Sbrian * Revision 5.5 1990/11/11 00:06:35 eggert 9147857Sbrian * Fix `rcs -e' core dump. 9247857Sbrian * 9347857Sbrian * Revision 5.4 1990/11/01 05:03:33 eggert 9447857Sbrian * Add -I and new -t behavior. Permit arbitrary data in logs. 9547857Sbrian * 9647857Sbrian * Revision 5.3 1990/10/04 06:30:16 eggert 9747857Sbrian * Accumulate exit status across files. 9847857Sbrian * 9947857Sbrian * Revision 5.2 1990/09/04 08:02:17 eggert 10047857Sbrian * Standardize yes-or-no procedure. 10147857Sbrian * 10247857Sbrian * Revision 5.1 1990/08/29 07:13:51 eggert 10347857Sbrian * Remove unused setuid support. Clean old log messages too. 10447857Sbrian * 10547857Sbrian * Revision 5.0 1990/08/22 08:12:42 eggert 10647857Sbrian * Don't lose names when applying -a option to multiple files. 107244040Seadler * Remove compile-time limits; use malloc instead. Add setuid support. 10847857Sbrian * Permit dates past 1999/12/31. Make lock and temp files faster and safer. 10947857Sbrian * Ansify and Posixate. Add -V. Fix umask bug. Make linting easier. Tune. 11047857Sbrian * Yield proper exit status. Check diff's output. 11147857Sbrian * 11247857Sbrian * Revision 4.11 89/05/01 15:12:06 narten 113138815Sbrian * changed copyright header to reflect current distribution rules 11447857Sbrian * 11547857Sbrian * Revision 4.10 88/11/08 16:01:54 narten 11647857Sbrian * didn't install previous patch correctly 11747857Sbrian * 118113346Skeramida * Revision 4.9 88/11/08 13:56:01 narten 119113346Skeramida * removed include <sysexits.h> (not needed) 12047857Sbrian * minor fix for -A option 12147857Sbrian * 12247857Sbrian * Revision 4.8 88/08/09 19:12:27 eggert 12347857Sbrian * Don't access freed storage. 124113346Skeramida * Use execv(), not system(); yield proper exit status; remove lint. 125113346Skeramida * 12647857Sbrian * Revision 4.7 87/12/18 11:37:17 narten 12747857Sbrian * lint cleanups (Guy Harris) 12847857Sbrian * 12947857Sbrian * Revision 4.6 87/10/18 10:28:48 narten 130113346Skeramida * Updating verison numbers. Changes relative to 1.1 are actually 131113346Skeramida * relative to 4.3 13247857Sbrian * 13347857Sbrian * Revision 1.4 87/09/24 13:58:52 narten 13447857Sbrian * Sources now pass through lint (if you ignore printf/sprintf/fprintf 13547857Sbrian * warnings) 13647857Sbrian * 13747857Sbrian * Revision 1.3 87/03/27 14:21:55 jenkins 13847857Sbrian * Port to suns 13947857Sbrian * 14047857Sbrian * Revision 1.2 85/12/17 13:59:09 albitz 14147857Sbrian * Changed setstate to rcs_setstate because of conflict with random.o. 14247857Sbrian * 14347857Sbrian * Revision 4.3 83/12/15 12:27:33 wft 14447857Sbrian * rcs -u now breaks most recent lock if it can't find a lock by the caller. 14547857Sbrian * 14664783Sbrian * Revision 4.2 83/12/05 10:18:20 wft 14751049Sbrian * Added conditional compilation for sending mail. 148113346Skeramida * Alternatives: V4_2BSD, V6, USG, and other. 149113346Skeramida * 15047857Sbrian * Revision 4.1 83/05/10 16:43:02 wft 15147857Sbrian * Simplified breaklock(); added calls to findlock() and getcaller(). 15247857Sbrian * Added option -b (default branch). Updated -s and -w for -b. 15347857Sbrian * Removed calls to stat(); now done by pairfilenames(). 15447857Sbrian * Replaced most catchints() calls with restoreints(). 15547857Sbrian * Removed check for exit status of delivermail(). 15647857Sbrian * Directed all interactive output to stderr. 15747857Sbrian * 15847857Sbrian * Revision 3.9.1.1 83/12/02 22:08:51 wft 15947857Sbrian * Added conditional compilation for 4.2 sendmail and 4.1 delivermail. 16047857Sbrian * 16147857Sbrian * Revision 3.9 83/02/15 15:38:39 wft 16247857Sbrian * Added call to fastcopy() to copy remainder of RCS file. 16347857Sbrian * 16447857Sbrian * Revision 3.8 83/01/18 17:37:51 wft 16547857Sbrian * Changed sendmail(): now uses delivermail, and asks whether to break the lock. 16647857Sbrian * 16747857Sbrian * Revision 3.7 83/01/15 18:04:25 wft 16847857Sbrian * Removed putree(); replaced with puttree() in rcssyn.c. 16947857Sbrian * Combined putdellog() and scanlogtext(); deleted putdellog(). 17047857Sbrian * Cleaned up diagnostics and error messages. Fixed problem with 17147857Sbrian * mutilated files in case of deletions in 2 files in a single command. 17247857Sbrian * Changed marking of selector from 'D' to DELETE. 17347857Sbrian * 17447857Sbrian * Revision 3.6 83/01/14 15:37:31 wft 17547857Sbrian * Added ignoring of interrupts while new RCS file is renamed; 17647857Sbrian * Avoids deletion of RCS files by interrupts. 17747857Sbrian * 17847857Sbrian * Revision 3.5 82/12/10 21:11:39 wft 17947857Sbrian * Removed unused variables, fixed checking of return code from diff, 18047857Sbrian * introduced variant COMPAT2 for skipping Suffix on -A files. 18147857Sbrian * 18247857Sbrian * Revision 3.4 82/12/04 13:18:20 wft 18347857Sbrian * Replaced getdelta() with gettree(), changed breaklock to update 18447857Sbrian * field lockedby, added some diagnostics. 18551049Sbrian * 18647857Sbrian * Revision 3.3 82/12/03 17:08:04 wft 18747857Sbrian * Replaced getlogin() with getpwuid(), flcose() with ffclose(), 18847857Sbrian * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x). 18947857Sbrian * fixed -u for missing revno. Disambiguated structure members. 19047857Sbrian * 19164783Sbrian * Revision 3.2 82/10/18 21:05:07 wft 19247857Sbrian * rcs -i now generates a file mode given by the umask minus write permission; 19347857Sbrian * otherwise, rcs keeps the mode, but removes write permission. 19447857Sbrian * I added a check for write error, fixed call to getlogin(), replaced 195 * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed 196 * conflicting, long identifiers. 197 * 198 * Revision 3.1 82/10/13 16:11:07 wft 199 * fixed type of variables receiving from getc() (char -> int). 200 */ 201 202 203#include "rcsbase.h" 204 205struct Lockrev { 206 char const *revno; 207 struct Lockrev * nextrev; 208}; 209 210struct Symrev { 211 char const *revno; 212 char const *ssymbol; 213 int override; 214 struct Symrev * nextsym; 215}; 216 217struct Message { 218 char const *revno; 219 struct cbuf message; 220 struct Message *nextmessage; 221}; 222 223struct Status { 224 char const *revno; 225 char const *status; 226 struct Status * nextstatus; 227}; 228 229enum changeaccess {append, erase}; 230struct chaccess { 231 char const *login; 232 enum changeaccess command; 233 struct chaccess *nextchaccess; 234}; 235 236struct delrevpair { 237 char const *strt; 238 char const *end; 239 int code; 240}; 241 242static int branchpoint P((struct hshentry*,struct hshentry*)); 243static int breaklock P((struct hshentry const*)); 244static int buildeltatext P((struct hshentries const*)); 245static int doaccess P((void)); 246static int doassoc P((void)); 247static int dolocks P((void)); 248static int domessages P((void)); 249static int rcs_setstate P((char const*,char const*)); 250static int removerevs P((void)); 251static int sendmail P((char const*,char const*)); 252static int setlock P((char const*)); 253static struct Lockrev **rmnewlocklst P((char const*)); 254static struct hshentry *searchcutpt P((char const*,int,struct hshentries*)); 255static void buildtree P((void)); 256static void cleanup P((void)); 257static void getaccessor P((char*,enum changeaccess)); 258static void getassoclst P((int,char*)); 259static void getchaccess P((char const*,enum changeaccess)); 260static void getdelrev P((char*)); 261static void getmessage P((char*)); 262static void getstates P((char*)); 263static void scanlogtext P((struct hshentry*,int)); 264 265static struct buf numrev; 266static char const *headstate; 267static int chgheadstate, exitstatus, lockhead, unlockcaller; 268static int suppress_mail; 269static struct Lockrev *newlocklst, *rmvlocklst; 270static struct Message *messagelst, **nextmessage; 271static struct Status *statelst, **nextstate; 272static struct Symrev *assoclst, **nextassoc; 273static struct chaccess *chaccess, **nextchaccess; 274static struct delrevpair delrev; 275static struct hshentry *cuthead, *cuttail, *delstrt; 276static struct hshentries *gendeltas; 277 278mainProg(rcsId, "rcs", "$FreeBSD$") 279{ 280 static char const cmdusage[] = 281 "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iILqTU} -ksubst -mrev:msg -{nN}name[:[rev]] -orange -sstate[:rev] -t[text] -Vn -xsuff -zzone file ..."; 282 283 char *a, **newargv, *textfile; 284 char const *branchsym, *commsyml; 285 int branchflag, changed, expmode, initflag; 286 int strictlock, strict_selected, textflag; 287 int keepRCStime, Ttimeflag; 288 size_t commsymlen; 289 struct buf branchnum; 290 struct Lockrev *lockpt; 291 struct Lockrev **curlock, **rmvlock; 292 struct Status * curstate; 293 294 nosetid(); 295 296 nextassoc = &assoclst; 297 nextchaccess = &chaccess; 298 nextmessage = &messagelst; 299 nextstate = &statelst; 300 branchsym = commsyml = textfile = 0; 301 branchflag = strictlock = false; 302 bufautobegin(&branchnum); 303 commsymlen = 0; 304 curlock = &newlocklst; 305 rmvlock = &rmvlocklst; 306 expmode = -1; 307 suffixes = X_DEFAULT; 308 initflag= textflag = false; 309 strict_selected = 0; 310 Ttimeflag = false; 311 312 /* preprocessing command options */ 313 if (1 < argc && argv[1][0] != '-') 314 warn("No options were given; this usage is obsolescent."); 315 316 argc = getRCSINIT(argc, argv, &newargv); 317 argv = newargv; 318 while (a = *++argv, 0<--argc && *a++=='-') { 319 switch (*a++) { 320 321 case 'i': /* initial version */ 322 initflag = true; 323 break; 324 325 case 'b': /* change default branch */ 326 if (branchflag) redefined('b'); 327 branchflag= true; 328 branchsym = a; 329 break; 330 331 case 'c': /* change comment symbol */ 332 if (commsyml) redefined('c'); 333 commsyml = a; 334 commsymlen = strlen(a); 335 break; 336 337 case 'a': /* add new accessor */ 338 getaccessor(*argv+1, append); 339 break; 340 341 case 'A': /* append access list according to accessfile */ 342 if (!*a) { 343 error("missing pathname after -A"); 344 break; 345 } 346 *argv = a; 347 if (0 < pairnames(1,argv,rcsreadopen,true,false)) { 348 while (AccessList) { 349 getchaccess(str_save(AccessList->login),append); 350 AccessList = AccessList->nextaccess; 351 } 352 Izclose(&finptr); 353 } 354 break; 355 356 case 'e': /* remove accessors */ 357 getaccessor(*argv+1, erase); 358 break; 359 360 case 'l': /* lock a revision if it is unlocked */ 361 if (!*a) { 362 /* Lock head or default branch. */ 363 lockhead = true; 364 break; 365 } 366 *curlock = lockpt = talloc(struct Lockrev); 367 lockpt->revno = a; 368 lockpt->nextrev = 0; 369 curlock = &lockpt->nextrev; 370 break; 371 372 case 'u': /* release lock of a locked revision */ 373 if (!*a) { 374 unlockcaller=true; 375 break; 376 } 377 *rmvlock = lockpt = talloc(struct Lockrev); 378 lockpt->revno = a; 379 lockpt->nextrev = 0; 380 rmvlock = &lockpt->nextrev; 381 curlock = rmnewlocklst(lockpt->revno); 382 break; 383 384 case 'L': /* set strict locking */ 385 if (strict_selected) { 386 if (!strictlock) /* Already selected -U? */ 387 warn("-U overridden by -L"); 388 } 389 strictlock = true; 390 strict_selected = true; 391 break; 392 393 case 'U': /* release strict locking */ 394 if (strict_selected) { 395 if (strictlock) /* Already selected -L? */ 396 warn("-L overridden by -U"); 397 } 398 strict_selected = true; 399 break; 400 401 case 'n': /* add new association: error, if name exists */ 402 if (!*a) { 403 error("missing symbolic name after -n"); 404 break; 405 } 406 getassoclst(false, (*argv)+1); 407 break; 408 409 case 'N': /* add or change association */ 410 if (!*a) { 411 error("missing symbolic name after -N"); 412 break; 413 } 414 getassoclst(true, (*argv)+1); 415 break; 416 417 case 'm': /* change log message */ 418 getmessage(a); 419 break; 420 421 case 'M': /* do not send mail */ 422 suppress_mail = true; 423 break; 424 425 case 'o': /* delete revisions */ 426 if (delrev.strt) redefined('o'); 427 if (!*a) { 428 error("missing revision range after -o"); 429 break; 430 } 431 getdelrev( (*argv)+1 ); 432 break; 433 434 case 's': /* change state attribute of a revision */ 435 if (!*a) { 436 error("state missing after -s"); 437 break; 438 } 439 getstates( (*argv)+1); 440 break; 441 442 case 't': /* change descriptive text */ 443 textflag=true; 444 if (*a) { 445 if (textfile) redefined('t'); 446 textfile = a; 447 } 448 break; 449 450 case 'T': /* do not update last-mod time for minor changes */ 451 if (*a) 452 goto unknown; 453 Ttimeflag = true; 454 break; 455 456 case 'I': 457 interactiveflag = true; 458 break; 459 460 case 'q': 461 quietflag = true; 462 break; 463 464 case 'x': 465 suffixes = a; 466 break; 467 468 case 'V': 469 setRCSversion(*argv); 470 break; 471 472 case 'z': 473 zone_set(a); 474 break; 475 476 case 'k': /* set keyword expand mode */ 477 if (0 <= expmode) redefined('k'); 478 if (0 <= (expmode = str2expmode(a))) 479 break; 480 /* fall into */ 481 default: 482 unknown: 483 error("unknown option: %s%s", *argv, cmdusage); 484 }; 485 } /* end processing of options */ 486 487 /* Now handle all pathnames. */ 488 if (nerror) cleanup(); 489 else if (argc < 1) faterror("no input file%s", cmdusage); 490 else for (; 0 < argc; cleanup(), ++argv, --argc) { 491 492 ffree(); 493 494 if ( initflag ) { 495 switch (pairnames(argc, argv, rcswriteopen, false, false)) { 496 case -1: break; /* not exist; ok */ 497 case 0: continue; /* error */ 498 case 1: rcserror("already exists"); 499 continue; 500 } 501 } 502 else { 503 switch (pairnames(argc, argv, rcswriteopen, true, false)) { 504 case -1: continue; /* not exist */ 505 case 0: continue; /* errors */ 506 case 1: break; /* file exists; ok*/ 507 } 508 } 509 510 511 /* 512 * RCSname contains the name of the RCS file, and 513 * workname contains the name of the working file. 514 * if !initflag, finptr contains the file descriptor for the 515 * RCS file. The admin node is initialized. 516 */ 517 518 diagnose("RCS file: %s\n", RCSname); 519 520 changed = initflag | textflag; 521 keepRCStime = Ttimeflag; 522 if (!initflag) { 523 if (!checkaccesslist()) continue; 524 gettree(); /* Read the delta tree. */ 525 } 526 527 /* update admin. node */ 528 if (strict_selected) { 529 changed |= StrictLocks ^ strictlock; 530 StrictLocks = strictlock; 531 } 532 if ( 533 commsyml && 534 ( 535 commsymlen != Comment.size || 536 memcmp(commsyml, Comment.string, commsymlen) != 0 537 ) 538 ) { 539 Comment.string = commsyml; 540 Comment.size = strlen(commsyml); 541 changed = true; 542 } 543 if (0 <= expmode && Expand != expmode) { 544 Expand = expmode; 545 changed = true; 546 } 547 548 /* update default branch */ 549 if (branchflag && expandsym(branchsym, &branchnum)) { 550 if (countnumflds(branchnum.string)) { 551 if (cmpnum(Dbranch, branchnum.string) != 0) { 552 Dbranch = branchnum.string; 553 changed = true; 554 } 555 } else 556 if (Dbranch) { 557 Dbranch = 0; 558 changed = true; 559 } 560 } 561 562 changed |= doaccess(); /* Update access list. */ 563 564 changed |= doassoc(); /* Update association list. */ 565 566 changed |= dolocks(); /* Update locks. */ 567 568 changed |= domessages(); /* Update log messages. */ 569 570 /* update state attribution */ 571 if (chgheadstate) { 572 /* change state of default branch or head */ 573 if (!Dbranch) { 574 if (!Head) 575 rcswarn("can't change states in an empty tree"); 576 else if (strcmp(Head->state, headstate) != 0) { 577 Head->state = headstate; 578 changed = true; 579 } 580 } else 581 changed |= rcs_setstate(Dbranch,headstate); 582 } 583 for (curstate = statelst; curstate; curstate = curstate->nextstatus) 584 changed |= rcs_setstate(curstate->revno,curstate->status); 585 586 cuthead = cuttail = 0; 587 if (delrev.strt && removerevs()) { 588 /* rebuild delta tree if some deltas are deleted */ 589 if ( cuttail ) 590 VOID genrevs( 591 cuttail->num, (char *)0, (char *)0, (char *)0, 592 &gendeltas 593 ); 594 buildtree(); 595 changed = true; 596 keepRCStime = false; 597 } 598 599 if (nerror) 600 continue; 601 602 putadmin(); 603 if ( Head ) 604 puttree(Head, frewrite); 605 putdesc(textflag,textfile); 606 607 if ( Head) { 608 if (delrev.strt || messagelst) { 609 if (!cuttail || buildeltatext(gendeltas)) { 610 advise_access(finptr, MADV_SEQUENTIAL); 611 scanlogtext((struct hshentry *)0, false); 612 /* copy rest of delta text nodes that are not deleted */ 613 changed = true; 614 } 615 } 616 } 617 618 if (initflag) { 619 /* Adjust things for donerewrite's sake. */ 620 if (stat(workname, &RCSstat) != 0) { 621# if bad_creat0 622 mode_t m = umask(0); 623 (void) umask(m); 624 RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m; 625# else 626 changed = -1; 627# endif 628 } 629 RCSstat.st_nlink = 0; 630 keepRCStime = false; 631 } 632 if (donerewrite(changed, 633 keepRCStime ? RCSstat.st_mtime : (time_t)-1 634 ) != 0) 635 break; 636 637 diagnose("done\n"); 638 } 639 640 tempunlink(); 641 exitmain(exitstatus); 642} /* end of main (rcs) */ 643 644 static void 645cleanup() 646{ 647 if (nerror) exitstatus = EXIT_FAILURE; 648 Izclose(&finptr); 649 Ozclose(&fcopy); 650 ORCSclose(); 651 dirtempunlink(); 652} 653 654 void 655exiterr() 656{ 657 ORCSerror(); 658 dirtempunlink(); 659 tempunlink(); 660 _exit(EXIT_FAILURE); 661} 662 663 664 static void 665getassoclst(flag, sp) 666int flag; 667char * sp; 668/* Function: associate a symbolic name to a revision or branch, */ 669/* and store in assoclst */ 670 671{ 672 struct Symrev * pt; 673 char const *temp; 674 int c; 675 676 while ((c = *++sp) == ' ' || c == '\t' || c =='\n') 677 continue; 678 temp = sp; 679 sp = checksym(sp, ':'); /* check for invalid symbolic name */ 680 c = *sp; *sp = '\0'; 681 while( c == ' ' || c == '\t' || c == '\n') c = *++sp; 682 683 if ( c != ':' && c != '\0') { 684 error("invalid string %s after option -n or -N",sp); 685 return; 686 } 687 688 pt = talloc(struct Symrev); 689 pt->ssymbol = temp; 690 pt->override = flag; 691 if (c == '\0') /* delete symbol */ 692 pt->revno = 0; 693 else { 694 while ((c = *++sp) == ' ' || c == '\n' || c == '\t') 695 continue; 696 pt->revno = sp; 697 } 698 pt->nextsym = 0; 699 *nextassoc = pt; 700 nextassoc = &pt->nextsym; 701} 702 703 704 static void 705getchaccess(login, command) 706 char const *login; 707 enum changeaccess command; 708{ 709 register struct chaccess *pt; 710 711 pt = talloc(struct chaccess); 712 pt->login = login; 713 pt->command = command; 714 pt->nextchaccess = 0; 715 *nextchaccess = pt; 716 nextchaccess = &pt->nextchaccess; 717} 718 719 720 721 static void 722getaccessor(opt, command) 723 char *opt; 724 enum changeaccess command; 725/* Function: get the accessor list of options -e and -a, */ 726/* and store in chaccess */ 727 728 729{ 730 register c; 731 register char *sp; 732 733 sp = opt; 734 while ((c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') 735 continue; 736 if ( c == '\0') { 737 if (command == erase && sp-opt == 1) { 738 getchaccess((char*)0, command); 739 return; 740 } 741 error("missing login name after option -a or -e"); 742 return; 743 } 744 745 while( c != '\0') { 746 getchaccess(sp, command); 747 sp = checkid(sp,','); 748 c = *sp; *sp = '\0'; 749 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp); 750 } 751} 752 753 754 static void 755getmessage(option) 756 char *option; 757{ 758 struct Message *pt; 759 struct cbuf cb; 760 char *m; 761 762 if (!(m = strchr(option, ':'))) { 763 error("-m option lacks revision number"); 764 return; 765 } 766 *m++ = 0; 767 cb = cleanlogmsg(m, strlen(m)); 768 if (!cb.size) { 769 error("-m option lacks log message"); 770 return; 771 } 772 pt = talloc(struct Message); 773 pt->revno = option; 774 pt->message = cb; 775 pt->nextmessage = 0; 776 *nextmessage = pt; 777 nextmessage = &pt->nextmessage; 778} 779 780 781 static void 782getstates(sp) 783char *sp; 784/* Function: get one state attribute and the corresponding */ 785/* revision and store in statelst */ 786 787{ 788 char const *temp; 789 struct Status *pt; 790 register c; 791 792 while ((c = *++sp) ==' ' || c == '\t' || c == '\n') 793 continue; 794 temp = sp; 795 sp = checkid(sp,':'); /* check for invalid state attribute */ 796 c = *sp; *sp = '\0'; 797 while( c == ' ' || c == '\t' || c == '\n' ) c = *++sp; 798 799 if ( c == '\0' ) { /* change state of def. branch or Head */ 800 chgheadstate = true; 801 headstate = temp; 802 return; 803 } 804 else if ( c != ':' ) { 805 error("missing ':' after state in option -s"); 806 return; 807 } 808 809 while ((c = *++sp) == ' ' || c == '\t' || c == '\n') 810 continue; 811 pt = talloc(struct Status); 812 pt->status = temp; 813 pt->revno = sp; 814 pt->nextstatus = 0; 815 *nextstate = pt; 816 nextstate = &pt->nextstatus; 817} 818 819 820 821 static void 822getdelrev(sp) 823char *sp; 824/* Function: get revision range or branch to be deleted, */ 825/* and place in delrev */ 826{ 827 int c; 828 struct delrevpair *pt; 829 int separator; 830 831 pt = &delrev; 832 while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') 833 continue; 834 835 /* Support old ambiguous '-' syntax; this will go away. */ 836 if (strchr(sp,':')) 837 separator = ':'; 838 else { 839 if (strchr(sp,'-') && VERSION(5) <= RCSversion) 840 warn("`-' is obsolete in `-o%s'; use `:' instead", sp); 841 separator = '-'; 842 } 843 844 if (c == separator) { /* -o:rev */ 845 while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') 846 continue; 847 pt->strt = sp; pt->code = 1; 848 while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp); 849 *sp = '\0'; 850 pt->end = 0; 851 return; 852 } 853 else { 854 pt->strt = sp; 855 while( c != ' ' && c != '\n' && c != '\t' && c != '\0' 856 && c != separator ) c = *++sp; 857 *sp = '\0'; 858 while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp; 859 if ( c == '\0' ) { /* -o rev or branch */ 860 pt->code = 0; 861 pt->end = 0; 862 return; 863 } 864 if (c != separator) { 865 error("invalid range %s %s after -o", pt->strt, sp); 866 } 867 while ((c = *++sp) == ' ' || c == '\n' || c == '\t') 868 continue; 869 if (!c) { /* -orev: */ 870 pt->code = 2; 871 pt->end = 0; 872 return; 873 } 874 } 875 /* -orev1:rev2 */ 876 pt->end = sp; pt->code = 3; 877 while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp; 878 *sp = '\0'; 879} 880 881 882 883 884 static void 885scanlogtext(delta,edit) 886 struct hshentry *delta; 887 int edit; 888/* Function: Scans delta text nodes up to and including the one given 889 * by delta, or up to last one present, if !delta. 890 * For the one given by delta (if delta), the log message is saved into 891 * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied. 892 * Assumes the initial lexeme must be read in first. 893 * Does not advance nexttok after it is finished, except if !delta. 894 */ 895{ 896 struct hshentry const *nextdelta; 897 struct cbuf cb; 898 899 for (;;) { 900 foutptr = 0; 901 if (eoflex()) { 902 if(delta) 903 rcsfaterror("can't find delta for revision %s", 904 delta->num 905 ); 906 return; /* no more delta text nodes */ 907 } 908 nextlex(); 909 if (!(nextdelta=getnum())) 910 fatserror("delta number corrupted"); 911 if (nextdelta->selector) { 912 foutptr = frewrite; 913 aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog); 914 } 915 getkeystring(Klog); 916 if (nextdelta == cuttail) { 917 cb = savestring(&curlogbuf); 918 if (!delta->log.string) 919 delta->log = cleanlogmsg(curlogbuf.string, cb.size); 920 nextlex(); 921 delta->igtext = getphrases(Ktext); 922 } else { 923 if (nextdelta->log.string && nextdelta->selector) { 924 foutptr = 0; 925 readstring(); 926 foutptr = frewrite; 927 putstring(foutptr, false, nextdelta->log, true); 928 afputc(nextc, foutptr); 929 } else 930 readstring(); 931 ignorephrases(Ktext); 932 } 933 getkeystring(Ktext); 934 935 if (delta==nextdelta) 936 break; 937 readstring(); /* skip over it */ 938 939 } 940 /* got the one we're looking for */ 941 if (edit) 942 editstring((struct hshentry*)0); 943 else 944 enterstring(); 945} 946 947 948 949 static struct Lockrev ** 950rmnewlocklst(which) 951 char const *which; 952/* Remove lock to revision WHICH from newlocklst. */ 953{ 954 struct Lockrev *pt, **pre; 955 956 pre = &newlocklst; 957 while ((pt = *pre)) 958 if (strcmp(pt->revno, which) != 0) 959 pre = &pt->nextrev; 960 else { 961 *pre = pt->nextrev; 962 tfree(pt); 963 } 964 return pre; 965} 966 967 968 969 static int 970doaccess() 971{ 972 register struct chaccess *ch; 973 register struct access **p, *t; 974 register int changed = false; 975 976 for (ch = chaccess; ch; ch = ch->nextchaccess) { 977 switch (ch->command) { 978 case erase: 979 if (!ch->login) { 980 if (AccessList) { 981 AccessList = 0; 982 changed = true; 983 } 984 } else 985 for (p = &AccessList; (t = *p); p = &t->nextaccess) 986 if (strcmp(ch->login, t->login) == 0) { 987 *p = t->nextaccess; 988 changed = true; 989 break; 990 } 991 break; 992 case append: 993 for (p = &AccessList; ; p = &t->nextaccess) 994 if (!(t = *p)) { 995 *p = t = ftalloc(struct access); 996 t->login = ch->login; 997 t->nextaccess = 0; 998 changed = true; 999 break; 1000 } else if (strcmp(ch->login, t->login) == 0) 1001 break; 1002 break; 1003 } 1004 } 1005 return changed; 1006} 1007 1008 1009 static int 1010sendmail(Delta, who) 1011 char const *Delta, *who; 1012/* Function: mail to who, informing him that his lock on delta was 1013 * broken by caller. Ask first whether to go ahead. Return false on 1014 * error or if user decides not to break the lock. 1015 */ 1016{ 1017#ifdef SENDMAIL 1018 char const *messagefile; 1019 int old1, old2, c, status; 1020 FILE * mailmess; 1021#endif 1022 1023 1024 aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who); 1025 if (suppress_mail) 1026 return true; 1027 if (!yesorno(false, "Do you want to break the lock? [ny](n): ")) 1028 return false; 1029 1030 /* go ahead with breaking */ 1031#ifdef SENDMAIL 1032 messagefile = maketemp(0); 1033 if (!(mailmess = fopenSafer(messagefile, "w+"))) { 1034 efaterror(messagefile); 1035 } 1036 1037 aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n", 1038 basefilename(RCSname), Delta, getfullRCSname(), getcaller() 1039 ); 1040 aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr); 1041 eflush(); 1042 1043 old1 = '\n'; old2 = ' '; 1044 for (; ;) { 1045 c = getcstdin(); 1046 if (feof(stdin)) { 1047 aprintf(mailmess, "%c\n", old1); 1048 break; 1049 } 1050 else if ( c == '\n' && old1 == '.' && old2 == '\n') 1051 break; 1052 else { 1053 afputc(old1, mailmess); 1054 old2 = old1; old1 = c; 1055 if (c == '\n') { 1056 aputs(">> ", stderr); 1057 eflush(); 1058 } 1059 } 1060 } 1061 Orewind(mailmess); 1062 aflush(mailmess); 1063 status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0); 1064 Ozclose(&mailmess); 1065 if (status == 0) 1066 return true; 1067 warn("Mail failed."); 1068#endif 1069 warn("Mail notification of broken locks is not available."); 1070 warn("Please tell `%s' why you broke the lock.", who); 1071 return(true); 1072} 1073 1074 1075 1076 static int 1077breaklock(delta) 1078 struct hshentry const *delta; 1079/* function: Finds the lock held by caller on delta, 1080 * and removes it. 1081 * Sends mail if a lock different from the caller's is broken. 1082 * Prints an error message if there is no such lock or error. 1083 */ 1084{ 1085 register struct rcslock *next, **trail; 1086 char const *num; 1087 1088 num=delta->num; 1089 for (trail = &Locks; (next = *trail); trail = &next->nextlock) 1090 if (strcmp(num, next->delta->num) == 0) { 1091 if ( 1092 strcmp(getcaller(),next->login) != 0 1093 && !sendmail(num, next->login) 1094 ) { 1095 rcserror("revision %s still locked by %s", 1096 num, next->login 1097 ); 1098 return false; 1099 } 1100 diagnose("%s unlocked\n", next->delta->num); 1101 *trail = next->nextlock; 1102 next->delta->lockedby = 0; 1103 return true; 1104 } 1105 rcserror("no lock set on revision %s", num); 1106 return false; 1107} 1108 1109 1110 1111 static struct hshentry * 1112searchcutpt(object, length, store) 1113 char const *object; 1114 int length; 1115 struct hshentries *store; 1116/* Function: Search store and return entry with number being object. */ 1117/* cuttail = 0, if the entry is Head; otherwise, cuttail */ 1118/* is the entry point to the one with number being object */ 1119 1120{ 1121 cuthead = 0; 1122 while (compartial(store->first->num, object, length)) { 1123 cuthead = store->first; 1124 store = store->rest; 1125 } 1126 return store->first; 1127} 1128 1129 1130 1131 static int 1132branchpoint(strt, tail) 1133struct hshentry *strt, *tail; 1134/* Function: check whether the deltas between strt and tail */ 1135/* are locked or branch point, return 1 if any is */ 1136/* locked or branch point; otherwise, return 0 and */ 1137/* mark deleted */ 1138 1139{ 1140 struct hshentry *pt; 1141 struct rcslock const *lockpt; 1142 1143 for (pt = strt; pt != tail; pt = pt->next) { 1144 if ( pt->branches ){ /* a branch point */ 1145 rcserror("can't remove branch point %s", pt->num); 1146 return true; 1147 } 1148 for (lockpt = Locks; lockpt; lockpt = lockpt->nextlock) 1149 if (lockpt->delta == pt) { 1150 rcserror("can't remove locked revision %s", pt->num); 1151 return true; 1152 } 1153 pt->selector = false; 1154 diagnose("deleting revision %s\n",pt->num); 1155 } 1156 return false; 1157} 1158 1159 1160 1161 static int 1162removerevs() 1163/* Function: get the revision range to be removed, and place the */ 1164/* first revision removed in delstrt, the revision before */ 1165/* delstrt in cuthead (0, if delstrt is head), and the */ 1166/* revision after the last removed revision in cuttail (0 */ 1167/* if the last is a leaf */ 1168 1169{ 1170 struct hshentry *target, *target2, *temp; 1171 int length; 1172 int cmp; 1173 1174 if (!expandsym(delrev.strt, &numrev)) return 0; 1175 target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); 1176 if ( ! target ) return 0; 1177 cmp = cmpnum(target->num, numrev.string); 1178 length = countnumflds(numrev.string); 1179 1180 if (delrev.code == 0) { /* -o rev or -o branch */ 1181 if (length & 1) 1182 temp=searchcutpt(target->num,length+1,gendeltas); 1183 else if (cmp) { 1184 rcserror("Revision %s doesn't exist.", numrev.string); 1185 return 0; 1186 } 1187 else 1188 temp = searchcutpt(numrev.string, length, gendeltas); 1189 cuttail = target->next; 1190 if ( branchpoint(temp, cuttail) ) { 1191 cuttail = 0; 1192 return 0; 1193 } 1194 delstrt = temp; /* first revision to be removed */ 1195 return 1; 1196 } 1197 1198 if (length & 1) { /* invalid branch after -o */ 1199 rcserror("invalid branch range %s after -o", numrev.string); 1200 return 0; 1201 } 1202 1203 if (delrev.code == 1) { /* -o -rev */ 1204 if ( length > 2 ) { 1205 temp = searchcutpt( target->num, length-1, gendeltas); 1206 cuttail = target->next; 1207 } 1208 else { 1209 temp = searchcutpt(target->num, length, gendeltas); 1210 cuttail = target; 1211 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) ) 1212 cuttail = cuttail->next; 1213 } 1214 if ( branchpoint(temp, cuttail) ){ 1215 cuttail = 0; 1216 return 0; 1217 } 1218 delstrt = temp; 1219 return 1; 1220 } 1221 1222 if (delrev.code == 2) { /* -o rev- */ 1223 if ( length == 2 ) { 1224 temp = searchcutpt(target->num, 1,gendeltas); 1225 if (cmp) 1226 cuttail = target; 1227 else 1228 cuttail = target->next; 1229 } 1230 else { 1231 if (cmp) { 1232 cuthead = target; 1233 if ( !(temp = target->next) ) return 0; 1234 } 1235 else 1236 temp = searchcutpt(target->num, length, gendeltas); 1237 getbranchno(temp->num, &numrev); /* get branch number */ 1238 VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas); 1239 } 1240 if ( branchpoint( temp, cuttail ) ) { 1241 cuttail = 0; 1242 return 0; 1243 } 1244 delstrt = temp; 1245 return 1; 1246 } 1247 1248 /* -o rev1-rev2 */ 1249 if (!expandsym(delrev.end, &numrev)) return 0; 1250 if ( 1251 length != countnumflds(numrev.string) 1252 || (length>2 && compartial(numrev.string, target->num, length-1)) 1253 ) { 1254 rcserror("invalid revision range %s-%s", 1255 target->num, numrev.string 1256 ); 1257 return 0; 1258 } 1259 1260 target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); 1261 if ( ! target2 ) return 0; 1262 1263 if ( length > 2) { /* delete revisions on branches */ 1264 if ( cmpnum(target->num, target2->num) > 0) { 1265 cmp = cmpnum(target2->num, numrev.string); 1266 temp = target; 1267 target = target2; 1268 target2 = temp; 1269 } 1270 if (cmp) { 1271 if ( ! cmpnum(target->num, target2->num) ) { 1272 rcserror("Revisions %s-%s don't exist.", 1273 delrev.strt, delrev.end 1274 ); 1275 return 0; 1276 } 1277 cuthead = target; 1278 temp = target->next; 1279 } 1280 else 1281 temp = searchcutpt(target->num, length, gendeltas); 1282 cuttail = target2->next; 1283 } 1284 else { /* delete revisions on trunk */ 1285 if ( cmpnum( target->num, target2->num) < 0 ) { 1286 temp = target; 1287 target = target2; 1288 target2 = temp; 1289 } 1290 else 1291 cmp = cmpnum(target2->num, numrev.string); 1292 if (cmp) { 1293 if ( ! cmpnum(target->num, target2->num) ) { 1294 rcserror("Revisions %s-%s don't exist.", 1295 delrev.strt, delrev.end 1296 ); 1297 return 0; 1298 } 1299 cuttail = target2; 1300 } 1301 else 1302 cuttail = target2->next; 1303 temp = searchcutpt(target->num, length, gendeltas); 1304 } 1305 if ( branchpoint(temp, cuttail) ) { 1306 cuttail = 0; 1307 return 0; 1308 } 1309 delstrt = temp; 1310 return 1; 1311} 1312 1313 1314 1315 static int 1316doassoc() 1317/* Add or delete (if !revno) association that is stored in assoclst. */ 1318{ 1319 char const *p; 1320 int changed = false; 1321 struct Symrev const *curassoc; 1322 struct assoc **pre, *pt; 1323 1324 /* add new associations */ 1325 for (curassoc = assoclst; curassoc; curassoc = curassoc->nextsym) { 1326 char const *ssymbol = curassoc->ssymbol; 1327 1328 if (!curassoc->revno) { /* delete symbol */ 1329 for (pre = &Symbols; ; pre = &pt->nextassoc) 1330 if (!(pt = *pre)) { 1331 rcswarn("can't delete nonexisting symbol %s", ssymbol); 1332 break; 1333 } else if (strcmp(pt->symbol, ssymbol) == 0) { 1334 *pre = pt->nextassoc; 1335 changed = true; 1336 break; 1337 } 1338 } 1339 else { 1340 if (curassoc->revno[0]) { 1341 p = 0; 1342 if (expandsym(curassoc->revno, &numrev)) 1343 p = fstr_save(numrev.string); 1344 } else if (!(p = tiprev())) 1345 rcserror("no latest revision to associate with symbol %s", 1346 ssymbol 1347 ); 1348 if (p) 1349 changed |= addsymbol(p, ssymbol, curassoc->override); 1350 } 1351 } 1352 return changed; 1353} 1354 1355 1356 1357 static int 1358dolocks() 1359/* Function: remove lock for caller or first lock if unlockcaller is set; 1360 * remove locks which are stored in rmvlocklst, 1361 * add new locks which are stored in newlocklst, 1362 * add lock for Dbranch or Head if lockhead is set. 1363 */ 1364{ 1365 struct Lockrev const *lockpt; 1366 struct hshentry *target; 1367 int changed = false; 1368 1369 if (unlockcaller) { /* find lock for caller */ 1370 if ( Head ) { 1371 if (Locks) { 1372 switch (findlock(true, &target)) { 1373 case 0: 1374 /* remove most recent lock */ 1375 changed |= breaklock(Locks->delta); 1376 break; 1377 case 1: 1378 diagnose("%s unlocked\n",target->num); 1379 changed = true; 1380 break; 1381 } 1382 } else { 1383 rcswarn("No locks are set."); 1384 } 1385 } else { 1386 rcswarn("can't unlock an empty tree"); 1387 } 1388 } 1389 1390 /* remove locks which are stored in rmvlocklst */ 1391 for (lockpt = rmvlocklst; lockpt; lockpt = lockpt->nextrev) 1392 if (expandsym(lockpt->revno, &numrev)) { 1393 target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas); 1394 if ( target ) 1395 if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) 1396 rcserror("can't unlock nonexisting revision %s", 1397 lockpt->revno 1398 ); 1399 else 1400 changed |= breaklock(target); 1401 /* breaklock does its own diagnose */ 1402 } 1403 1404 /* add new locks which stored in newlocklst */ 1405 for (lockpt = newlocklst; lockpt; lockpt = lockpt->nextrev) 1406 changed |= setlock(lockpt->revno); 1407 1408 if (lockhead) /* lock default branch or head */ 1409 if (Dbranch) 1410 changed |= setlock(Dbranch); 1411 else if (Head) 1412 changed |= setlock(Head->num); 1413 else 1414 rcswarn("can't lock an empty tree"); 1415 return changed; 1416} 1417 1418 1419 1420 static int 1421setlock(rev) 1422 char const *rev; 1423/* Function: Given a revision or branch number, finds the corresponding 1424 * delta and locks it for caller. 1425 */ 1426{ 1427 struct hshentry *target; 1428 int r; 1429 1430 if (expandsym(rev, &numrev)) { 1431 target = genrevs(numrev.string, (char*)0, (char*)0, 1432 (char*)0, &gendeltas); 1433 if ( target ) 1434 if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) 1435 rcserror("can't lock nonexisting revision %s", 1436 numrev.string 1437 ); 1438 else { 1439 if ((r = addlock(target, false)) < 0 && breaklock(target)) 1440 r = addlock(target, true); 1441 if (0 <= r) { 1442 if (r) 1443 diagnose("%s locked\n", target->num); 1444 return r; 1445 } 1446 } 1447 } 1448 return 0; 1449} 1450 1451 1452 static int 1453domessages() 1454{ 1455 struct hshentry *target; 1456 struct Message *p; 1457 int changed = false; 1458 1459 for (p = messagelst; p; p = p->nextmessage) 1460 if ( 1461 expandsym(p->revno, &numrev) && 1462 (target = genrevs( 1463 numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas 1464 )) 1465 ) { 1466 /* 1467 * We can't check the old log -- it's much later in the file. 1468 * We pessimistically assume that it changed. 1469 */ 1470 target->log = p->message; 1471 changed = true; 1472 } 1473 return changed; 1474} 1475 1476 1477 static int 1478rcs_setstate(rev,status) 1479 char const *rev, *status; 1480/* Function: Given a revision or branch number, finds the corresponding delta 1481 * and sets its state to status. 1482 */ 1483{ 1484 struct hshentry *target; 1485 1486 if (expandsym(rev, &numrev)) { 1487 target = genrevs(numrev.string, (char*)0, (char*)0, 1488 (char*)0, &gendeltas); 1489 if ( target ) 1490 if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) 1491 rcserror("can't set state of nonexisting revision %s", 1492 numrev.string 1493 ); 1494 else if (strcmp(target->state, status) != 0) { 1495 target->state = status; 1496 return true; 1497 } 1498 } 1499 return false; 1500} 1501 1502 1503 1504 1505 1506 static int 1507buildeltatext(deltas) 1508 struct hshentries const *deltas; 1509/* Function: put the delta text on frewrite and make necessary */ 1510/* change to delta text */ 1511{ 1512 register FILE *fcut; /* temporary file to rebuild delta tree */ 1513 char const *cutname; 1514 1515 fcut = 0; 1516 cuttail->selector = false; 1517 scanlogtext(deltas->first, false); 1518 if ( cuthead ) { 1519 cutname = maketemp(3); 1520 if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) { 1521 efaterror(cutname); 1522 } 1523 1524 while (deltas->first != cuthead) { 1525 deltas = deltas->rest; 1526 scanlogtext(deltas->first, true); 1527 } 1528 1529 snapshotedit(fcut); 1530 Orewind(fcut); 1531 aflush(fcut); 1532 } 1533 1534 while (deltas->first != cuttail) 1535 scanlogtext((deltas = deltas->rest)->first, true); 1536 finishedit((struct hshentry*)0, (FILE*)0, true); 1537 Ozclose(&fcopy); 1538 1539 if (fcut) { 1540 char const *diffname = maketemp(0); 1541 char const *diffv[6 + !!OPEN_O_BINARY]; 1542 char const **diffp = diffv; 1543 *++diffp = DIFF; 1544 *++diffp = DIFFFLAGS; 1545# if OPEN_O_BINARY 1546 if (Expand == BINARY_EXPAND) 1547 *++diffp == "--binary"; 1548# endif 1549 *++diffp = "-"; 1550 *++diffp = resultname; 1551 *++diffp = 0; 1552 switch (runv(fileno(fcut), diffname, diffv)) { 1553 case DIFF_FAILURE: case DIFF_SUCCESS: break; 1554 default: rcsfaterror("diff failed"); 1555 } 1556 Ofclose(fcut); 1557 return putdtext(cuttail,diffname,frewrite,true); 1558 } else 1559 return putdtext(cuttail,resultname,frewrite,false); 1560} 1561 1562 1563 1564 static void 1565buildtree() 1566/* Function: actually removes revisions whose selector field */ 1567/* is false, and rebuilds the linkage of deltas. */ 1568/* asks for reconfirmation if deleting last revision*/ 1569{ 1570 struct hshentry * Delta; 1571 struct branchhead *pt, *pre; 1572 1573 if ( cuthead ) 1574 if ( cuthead->next == delstrt ) 1575 cuthead->next = cuttail; 1576 else { 1577 pre = pt = cuthead->branches; 1578 while( pt && pt->hsh != delstrt ) { 1579 pre = pt; 1580 pt = pt->nextbranch; 1581 } 1582 if ( cuttail ) 1583 pt->hsh = cuttail; 1584 else if ( pt == pre ) 1585 cuthead->branches = pt->nextbranch; 1586 else 1587 pre->nextbranch = pt->nextbranch; 1588 } 1589 else { 1590 if (!cuttail && !quietflag) { 1591 if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) { 1592 rcserror("No revision deleted"); 1593 Delta = delstrt; 1594 while( Delta) { 1595 Delta->selector = true; 1596 Delta = Delta->next; 1597 } 1598 return; 1599 } 1600 } 1601 Head = cuttail; 1602 } 1603 return; 1604} 1605 1606#if RCS_lint 1607/* This lets us lint everything all at once. */ 1608 1609char const cmdid[] = ""; 1610 1611#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();} 1612 1613 int 1614main(argc, argv) 1615 int argc; 1616 char **argv; 1617{ 1618 go(ciId, ciExit); 1619 go(coId, coExit); 1620 go(identId, identExit); 1621 go(mergeId, mergeExit); 1622 go(rcsId, exiterr); 1623 go(rcscleanId, rcscleanExit); 1624 go(rcsdiffId, rdiffExit); 1625 go(rcsmergeId, rmergeExit); 1626 go(rlogId, rlogExit); 1627 return 0; 1628} 1629#endif 1630