1/* Check out working files from revisions of RCS files. */ 2 3/* Copyright 1982, 1988, 1989 Walter Tichy 4 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 5 Distributed under license by the Free Software Foundation, Inc. 6 7This file is part of RCS. 8 9RCS is free software; you can redistribute it and/or modify 10it under the terms of the GNU General Public License as published by 11the Free Software Foundation; either version 2, or (at your option) 12any later version. 13 14RCS is distributed in the hope that it will be useful, 15but WITHOUT ANY WARRANTY; without even the implied warranty of 16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17GNU General Public License for more details. 18 19You should have received a copy of the GNU General Public License 20along with RCS; see the file COPYING. 21If not, write to the Free Software Foundation, 2259 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 24Report problems and direct all questions to: 25 26 rcs-bugs@cs.purdue.edu 27 28*/ 29 30/* 31 * $Log: co.c,v $ 32 * Revision 1.1 2003/06/11 15:56:09 darkwyrm 33 * Added rcs, gzip, sed, and associated utilities. 34 * 35 * Revision 5.18 1995/06/16 06:19:24 eggert 36 * Update FSF address. 37 * 38 * Revision 5.17 1995/06/01 16:23:43 eggert 39 * (main, preparejoin): Pass argument instead of using `join' static variable. 40 * (main): Add -kb. 41 * 42 * Revision 5.16 1994/03/17 14:05:48 eggert 43 * Move buffer-flushes out of critical sections, since they aren't critical. 44 * Use ORCSerror to clean up after a fatal error. Remove lint. 45 * Specify subprocess input via file descriptor, not file name. 46 * 47 * Revision 5.15 1993/11/09 17:40:15 eggert 48 * -V now prints version on stdout and exits. Don't print usage twice. 49 * 50 * Revision 5.14 1993/11/03 17:42:27 eggert 51 * Add -z. Generate a value for the Name keyword. 52 * Don't arbitrarily limit the number of joins. 53 * Improve quality of diagnostics. 54 * 55 * Revision 5.13 1992/07/28 16:12:44 eggert 56 * Add -V. Check that working and RCS files are distinct. 57 * 58 * Revision 5.12 1992/02/17 23:02:08 eggert 59 * Add -T. 60 * 61 * Revision 5.11 1992/01/24 18:44:19 eggert 62 * Add support for bad_creat0. lint -> RCS_lint 63 * 64 * Revision 5.10 1992/01/06 02:42:34 eggert 65 * Update usage string. 66 * 67 * Revision 5.9 1991/10/07 17:32:46 eggert 68 * -k affects just working file, not RCS file. 69 * 70 * Revision 5.8 1991/08/19 03:13:55 eggert 71 * Warn before removing somebody else's file. 72 * Add -M. Fix co -j bugs. Tune. 73 * 74 * Revision 5.7 1991/04/21 11:58:15 eggert 75 * Ensure that working file is newer than RCS file after co -[lu]. 76 * Add -x, RCSINIT, MS-DOS support. 77 * 78 * Revision 5.6 1990/12/04 05:18:38 eggert 79 * Don't checkaccesslist() unless necessary. 80 * Use -I for prompts and -q for diagnostics. 81 * 82 * Revision 5.5 1990/11/01 05:03:26 eggert 83 * Fix -j. Add -I. 84 * 85 * Revision 5.4 1990/10/04 06:30:11 eggert 86 * Accumulate exit status across files. 87 * 88 * Revision 5.3 1990/09/11 02:41:09 eggert 89 * co -kv yields a readonly working file. 90 * 91 * Revision 5.2 1990/09/04 08:02:13 eggert 92 * Standardize yes-or-no procedure. 93 * 94 * Revision 5.0 1990/08/22 08:10:02 eggert 95 * Permit multiple locks by same user. Add setuid support. 96 * Remove compile-time limits; use malloc instead. 97 * Permit dates past 1999/12/31. Switch to GMT. 98 * Make lock and temp files faster and safer. 99 * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. 100 * 101 * Revision 4.7 89/05/01 15:11:41 narten 102 * changed copyright header to reflect current distribution rules 103 * 104 * Revision 4.6 88/08/09 19:12:15 eggert 105 * Fix "co -d" core dump; rawdate wasn't always initialized. 106 * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint 107 * 108 * Revision 4.5 87/12/18 11:35:40 narten 109 * lint cleanups (from Guy Harris) 110 * 111 * Revision 4.4 87/10/18 10:20:53 narten 112 * Updating version numbers changes relative to 1.1, are actually 113 * relative to 4.2 114 * 115 * Revision 1.3 87/09/24 13:58:30 narten 116 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 117 * warnings) 118 * 119 * Revision 1.2 87/03/27 14:21:38 jenkins 120 * Port to suns 121 * 122 * Revision 4.2 83/12/05 13:39:48 wft 123 * made rewriteflag external. 124 * 125 * Revision 4.1 83/05/10 16:52:55 wft 126 * Added option -u and -f. 127 * Added handling of default branch. 128 * Replaced getpwuid() with getcaller(). 129 * Removed calls to stat(); now done by pairfilenames(). 130 * Changed and renamed rmoldfile() to rmworkfile(). 131 * Replaced catchints() calls with restoreints(), unlink()--link() with rename(); 132 * 133 * Revision 3.7 83/02/15 15:27:07 wft 134 * Added call to fastcopy() to copy remainder of RCS file. 135 * 136 * Revision 3.6 83/01/15 14:37:50 wft 137 * Added ignoring of interrupts while RCS file is renamed; this avoids 138 * deletion of RCS files during the unlink/link window. 139 * 140 * Revision 3.5 82/12/08 21:40:11 wft 141 * changed processing of -d to use DATEFORM; removed actual from 142 * call to preparejoin; re-fixed printing of done at the end. 143 * 144 * Revision 3.4 82/12/04 18:40:00 wft 145 * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. 146 * Fixed printing of "done". 147 * 148 * Revision 3.3 82/11/28 22:23:11 wft 149 * Replaced getlogin() with getpwuid(), flcose() with ffclose(), 150 * %02d with %.2d, mode generation for working file with WORKMODE. 151 * Fixed nil printing. Fixed -j combined with -l and -p, and exit 152 * for non-existing revisions in preparejoin(). 153 * 154 * Revision 3.2 82/10/18 20:47:21 wft 155 * Mode of working file is now maintained even for co -l, but write permission 156 * is removed. 157 * The working file inherits its mode from the RCS file, plus write permission 158 * for the owner. The write permission is not given if locking is strict and 159 * co does not lock. 160 * An existing working file without write permission is deleted automatically. 161 * Otherwise, co asks (empty answer: abort co). 162 * Call to getfullRCSname() added, check for write error added, call 163 * for getlogin() fixed. 164 * 165 * Revision 3.1 82/10/13 16:01:30 wft 166 * fixed type of variables receiving from getc() (char -> int). 167 * removed unused variables. 168 */ 169 170 171 172 173#include "rcsbase.h" 174 175static char *addjoin P((char*)); 176static char const *getancestor P((char const*,char const*)); 177static int buildjoin P((char const*)); 178static int preparejoin P((char*)); 179static int rmlock P((struct hshentry const*)); 180static int rmworkfile P((void)); 181static void cleanup P((void)); 182 183static char const quietarg[] = "-q"; 184 185static char const *expandarg, *suffixarg, *versionarg, *zonearg; 186static char const **joinlist; /* revisions to be joined */ 187static int joinlength; 188static FILE *neworkptr; 189static int exitstatus; 190static int forceflag; 191static int lastjoin; /* index of last element in joinlist */ 192static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */ 193static int mtimeflag; 194static struct hshentries *gendeltas; /* deltas to be generated */ 195static struct hshentry *targetdelta; /* final delta to be generated */ 196static struct stat workstat; 197 198mainProg(coId, "co", "$Id: co.c 3476 2003-06-11 15:56:10Z darkwyrm $") 199{ 200 static char const cmdusage[] = 201 "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ..."; 202 203 char *a, *joinflag, **newargv; 204 char const *author, *date, *rev, *state; 205 char const *joinname, *newdate, *neworkname; 206 int changelock; /* 1 if a lock has been changed, -1 if error */ 207 int expmode, r, tostdout, workstatstat; 208 int Ttimeflag; 209 struct buf numericrev; /* expanded revision number */ 210 char finaldate[datesize]; 211# if OPEN_O_BINARY 212 int stdout_mode = 0; 213# endif 214 215 setrid(); 216 author = date = rev = state = 0; 217 joinflag = 0; 218 bufautobegin(&numericrev); 219 expmode = -1; 220 suffixes = X_DEFAULT; 221 tostdout = false; 222 Ttimeflag = false; 223 224 argc = getRCSINIT(argc, argv, &newargv); 225 argv = newargv; 226 while (a = *++argv, 0<--argc && *a++=='-') { 227 switch (*a++) { 228 229 case 'r': 230 revno: 231 if (*a) { 232 if (rev) warn("redefinition of revision number"); 233 rev = a; 234 } 235 break; 236 237 case 'f': 238 forceflag=true; 239 goto revno; 240 241 case 'l': 242 if (lockflag < 0) { 243 warn("-u overridden by -l."); 244 } 245 lockflag = 1; 246 goto revno; 247 248 case 'u': 249 if (0 < lockflag) { 250 warn("-l overridden by -u."); 251 } 252 lockflag = -1; 253 goto revno; 254 255 case 'p': 256 tostdout = true; 257 goto revno; 258 259 case 'I': 260 interactiveflag = true; 261 goto revno; 262 263 case 'q': 264 quietflag=true; 265 goto revno; 266 267 case 'd': 268 if (date) 269 redefined('d'); 270 str2date(a, finaldate); 271 date=finaldate; 272 break; 273 274 case 'j': 275 if (*a) { 276 if (joinflag) redefined('j'); 277 joinflag = a; 278 } 279 break; 280 281 case 'M': 282 mtimeflag = true; 283 goto revno; 284 285 case 's': 286 if (*a) { 287 if (state) redefined('s'); 288 state = a; 289 } 290 break; 291 292 case 'T': 293 if (*a) 294 goto unknown; 295 Ttimeflag = true; 296 break; 297 298 case 'w': 299 if (author) redefined('w'); 300 if (*a) 301 author = a; 302 else 303 author = getcaller(); 304 break; 305 306 case 'x': 307 suffixarg = *argv; 308 suffixes = a; 309 break; 310 311 case 'V': 312 versionarg = *argv; 313 setRCSversion(versionarg); 314 break; 315 316 case 'z': 317 zonearg = *argv; 318 zone_set(a); 319 break; 320 321 case 'k': /* set keyword expand mode */ 322 expandarg = *argv; 323 if (0 <= expmode) redefined('k'); 324 if (0 <= (expmode = str2expmode(a))) 325 break; 326 /* fall into */ 327 default: 328 unknown: 329 error("unknown option: %s%s", *argv, cmdusage); 330 331 }; 332 } /* end of option processing */ 333 334 /* Now handle all pathnames. */ 335 if (nerror) cleanup(); 336 else if (argc < 1) faterror("no input file%s", cmdusage); 337 else for (; 0 < argc; cleanup(), ++argv, --argc) { 338 ffree(); 339 340 if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) 341 continue; 342 343 /* 344 * RCSname contains the name of the RCS file, and finptr 345 * points at it. workname contains the name of the working file. 346 * Also, RCSstat has been set. 347 */ 348 diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); 349 350 workstatstat = -1; 351 if (tostdout) { 352# if OPEN_O_BINARY 353 int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0; 354 if (stdout_mode != newmode) { 355 stdout_mode = newmode; 356 oflush(); 357 VOID setmode(STDOUT_FILENO, newmode); 358 } 359# endif 360 neworkname = 0; 361 neworkptr = workstdout = stdout; 362 } else { 363 workstatstat = stat(workname, &workstat); 364 if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) { 365 rcserror("RCS file is the same as working file %s.", 366 workname 367 ); 368 continue; 369 } 370 neworkname = makedirtemp(1); 371 if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) { 372 if (errno == EACCES) 373 workerror("permission denied on parent directory"); 374 else 375 eerror(neworkname); 376 continue; 377 } 378 } 379 380 gettree(); /* reads in the delta tree */ 381 382 if (!Head) { 383 /* no revisions; create empty file */ 384 diagnose("no revisions present; generating empty revision 0.0\n"); 385 if (lockflag) 386 warn( 387 "no revisions, so nothing can be %slocked", 388 lockflag < 0 ? "un" : "" 389 ); 390 Ozclose(&fcopy); 391 if (workstatstat == 0) 392 if (!rmworkfile()) continue; 393 changelock = 0; 394 newdate = 0; 395 } else { 396 int locks = lockflag ? findlock(false, &targetdelta) : 0; 397 if (rev) { 398 /* expand symbolic revision number */ 399 if (!expandsym(rev, &numericrev)) 400 continue; 401 } else { 402 switch (locks) { 403 default: 404 continue; 405 case 0: 406 bufscpy(&numericrev, Dbranch?Dbranch:""); 407 break; 408 case 1: 409 bufscpy(&numericrev, targetdelta->num); 410 break; 411 } 412 } 413 /* get numbers of deltas to be generated */ 414 if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) 415 continue; 416 /* check reservations */ 417 changelock = 418 lockflag < 0 ? 419 rmlock(targetdelta) 420 : lockflag == 0 ? 421 0 422 : 423 addlock(targetdelta, true); 424 425 if ( 426 changelock < 0 427 || (changelock && !checkaccesslist()) 428 || dorewrite(lockflag, changelock) != 0 429 ) 430 continue; 431 432 if (0 <= expmode) 433 Expand = expmode; 434 if (0 < lockflag && Expand == VAL_EXPAND) { 435 rcserror("cannot combine -kv and -l"); 436 continue; 437 } 438 439 if (joinflag && !preparejoin(joinflag)) 440 continue; 441 442 diagnose("revision %s%s\n",targetdelta->num, 443 0<lockflag ? " (locked)" : 444 lockflag<0 ? " (unlocked)" : ""); 445 446 /* Prepare to remove old working file if necessary. */ 447 if (workstatstat == 0) 448 if (!rmworkfile()) continue; 449 450 /* skip description */ 451 getdesc(false); /* don't echo*/ 452 453 locker_expansion = 0 < lockflag; 454 targetdelta->name = namedrev(rev, targetdelta); 455 joinname = buildrevision( 456 gendeltas, targetdelta, 457 joinflag&&tostdout ? (FILE*)0 : neworkptr, 458 Expand < MIN_UNEXPAND 459 ); 460# if !large_memory 461 if (fcopy == neworkptr) 462 fcopy = 0; /* Don't close it twice. */ 463# endif 464 if_advise_access(changelock && gendeltas->first!=targetdelta, 465 finptr, MADV_SEQUENTIAL 466 ); 467 468 if (donerewrite(changelock, 469 Ttimeflag ? RCSstat.st_mtime : (time_t)-1 470 ) != 0) 471 continue; 472 473 if (changelock) { 474 locks += lockflag; 475 if (1 < locks) 476 rcswarn("You now have %d locks.", locks); 477 } 478 479 newdate = targetdelta->date; 480 if (joinflag) { 481 newdate = 0; 482 if (!joinname) { 483 aflush(neworkptr); 484 joinname = neworkname; 485 } 486 if (Expand == BINARY_EXPAND) 487 workerror("merging binary files"); 488 if (!buildjoin(joinname)) 489 continue; 490 } 491 } 492 if (!tostdout) { 493 mode_t m = WORKMODE(RCSstat.st_mode, 494 ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks)) 495 ); 496 time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1; 497 aflush(neworkptr); 498 ignoreints(); 499 r = chnamemod(&neworkptr, neworkname, workname, 1, m, t); 500 keepdirtemp(neworkname); 501 restoreints(); 502 if (r != 0) { 503 eerror(workname); 504 error("see %s", neworkname); 505 continue; 506 } 507 diagnose("done\n"); 508 } 509 } 510 511 tempunlink(); 512 Ofclose(workstdout); 513 exitmain(exitstatus); 514 515} /* end of main (co) */ 516 517 static void 518cleanup() 519{ 520 if (nerror) exitstatus = EXIT_FAILURE; 521 Izclose(&finptr); 522 ORCSclose(); 523# if !large_memory 524 if (fcopy!=workstdout) Ozclose(&fcopy); 525# endif 526 if (neworkptr!=workstdout) Ozclose(&neworkptr); 527 dirtempunlink(); 528} 529 530#if RCS_lint 531# define exiterr coExit 532#endif 533 void 534exiterr() 535{ 536 ORCSerror(); 537 dirtempunlink(); 538 tempunlink(); 539 _exit(EXIT_FAILURE); 540} 541 542 543/***************************************************************** 544 * The following routines are auxiliary routines 545 *****************************************************************/ 546 547 static int 548rmworkfile() 549/* 550 * Prepare to remove workname, if it exists, and if 551 * it is read-only. 552 * Otherwise (file writable): 553 * if !quietmode asks the user whether to really delete it (default: fail); 554 * otherwise failure. 555 * Returns true if permission is gotten. 556 */ 557{ 558 if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { 559 /* File is writable */ 560 if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", 561 workname, 562 myself(workstat.st_uid) ? "" : ", and you do not own it" 563 )) { 564 error(!quietflag && ttystdin() 565 ? "checkout aborted" 566 : "writable %s exists; checkout aborted", workname); 567 return false; 568 } 569 } 570 /* Actual unlink is done later by caller. */ 571 return true; 572} 573 574 575 static int 576rmlock(delta) 577 struct hshentry const *delta; 578/* Function: removes the lock held by caller on delta. 579 * Returns -1 if someone else holds the lock, 580 * 0 if there is no lock on delta, 581 * and 1 if a lock was found and removed. 582 */ 583{ register struct rcslock * next, * trail; 584 char const *num; 585 struct rcslock dummy; 586 int whomatch, nummatch; 587 588 num=delta->num; 589 dummy.nextlock=next=Locks; 590 trail = &dummy; 591 while (next) { 592 whomatch = strcmp(getcaller(), next->login); 593 nummatch=strcmp(num,next->delta->num); 594 if ((whomatch==0) && (nummatch==0)) break; 595 /*found a lock on delta by caller*/ 596 if ((whomatch!=0)&&(nummatch==0)) { 597 rcserror("revision %s locked by %s; use co -r or rcs -u", 598 num, next->login 599 ); 600 return -1; 601 } 602 trail=next; 603 next=next->nextlock; 604 } 605 if (next) { 606 /*found one; delete it */ 607 trail->nextlock=next->nextlock; 608 Locks=dummy.nextlock; 609 next->delta->lockedby = 0; 610 return 1; /*success*/ 611 } else return 0; /*no lock on delta*/ 612} 613 614 615 616 617/***************************************************************** 618 * The rest of the routines are for handling joins 619 *****************************************************************/ 620 621 622 static char * 623addjoin(joinrev) 624 char *joinrev; 625/* Add joinrev's number to joinlist, yielding address of char past joinrev, 626 * or 0 if no such revision exists. 627 */ 628{ 629 register char *j; 630 register struct hshentry *d; 631 char terminator; 632 struct buf numrev; 633 struct hshentries *joindeltas; 634 635 j = joinrev; 636 for (;;) { 637 switch (*j++) { 638 default: 639 continue; 640 case 0: 641 case ' ': case '\t': case '\n': 642 case ':': case ',': case ';': 643 break; 644 } 645 break; 646 } 647 terminator = *--j; 648 *j = 0; 649 bufautobegin(&numrev); 650 d = 0; 651 if (expandsym(joinrev, &numrev)) 652 d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas); 653 bufautoend(&numrev); 654 *j = terminator; 655 if (d) { 656 joinlist[++lastjoin] = d->num; 657 return j; 658 } 659 return 0; 660} 661 662 static int 663preparejoin(j) 664 register char *j; 665/* Parse join list J and place pointers to the 666 * revision numbers into joinlist. 667 */ 668{ 669 lastjoin= -1; 670 for (;;) { 671 while ((*j==' ')||(*j=='\t')||(*j==',')) j++; 672 if (*j=='\0') break; 673 if (lastjoin>=joinlength-2) { 674 joinlist = 675 (joinlength *= 2) == 0 676 ? tnalloc(char const *, joinlength = 16) 677 : trealloc(char const *, joinlist, joinlength); 678 } 679 if (!(j = addjoin(j))) return false; 680 while ((*j==' ') || (*j=='\t')) j++; 681 if (*j == ':') { 682 j++; 683 while((*j==' ') || (*j=='\t')) j++; 684 if (*j!='\0') { 685 if (!(j = addjoin(j))) return false; 686 } else { 687 rcsfaterror("join pair incomplete"); 688 } 689 } else { 690 if (lastjoin==0) { /* first pair */ 691 /* common ancestor missing */ 692 joinlist[1]=joinlist[0]; 693 lastjoin=1; 694 /*derive common ancestor*/ 695 if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) 696 return false; 697 } else { 698 rcsfaterror("join pair incomplete"); 699 } 700 } 701 } 702 if (lastjoin < 1) 703 rcsfaterror("empty join"); 704 return true; 705} 706 707 708 709 static char const * 710getancestor(r1, r2) 711 char const *r1, *r2; 712/* Yield the common ancestor of r1 and r2 if successful, 0 otherwise. 713 * Work reliably only if r1 and r2 are not branch numbers. 714 */ 715{ 716 static struct buf t1, t2; 717 718 int l1, l2, l3; 719 char const *r; 720 721 l1 = countnumflds(r1); 722 l2 = countnumflds(r2); 723 if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) { 724 /* not on main trunk or identical */ 725 l3 = 0; 726 while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0) 727 l3 += 2; 728 /* This will terminate since r1 and r2 are not the same; see above. */ 729 if (l3==0) { 730 /* no common prefix; common ancestor on main trunk */ 731 VOID partialno(&t1, r1, l1>2 ? 2 : l1); 732 VOID partialno(&t2, r2, l2>2 ? 2 : l2); 733 r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; 734 if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) 735 return r; 736 } else if (cmpnumfld(r1, r2, l3+1)!=0) 737 return partialno(&t1,r1,l3); 738 } 739 rcserror("common ancestor of %s and %s undefined", r1, r2); 740 return 0; 741} 742 743 744 745 static int 746buildjoin(initialfile) 747 char const *initialfile; 748/* Function: merge pairs of elements in joinlist into initialfile 749 * If workstdout is set, copy result to stdout. 750 * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). 751 */ 752{ 753 struct buf commarg; 754 struct buf subs; 755 char const *rev2, *rev3; 756 int i; 757 char const *cov[10], *mergev[11]; 758 char const **p; 759 760 bufautobegin(&commarg); 761 bufautobegin(&subs); 762 rev2 = maketemp(0); 763 rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ 764 765 cov[1] = CO; 766 /* cov[2] setup below */ 767 p = &cov[3]; 768 if (expandarg) *p++ = expandarg; 769 if (suffixarg) *p++ = suffixarg; 770 if (versionarg) *p++ = versionarg; 771 if (zonearg) *p++ = zonearg; 772 *p++ = quietarg; 773 *p++ = RCSname; 774 *p = 0; 775 776 mergev[1] = MERGE; 777 mergev[2] = mergev[4] = "-L"; 778 /* rest of mergev setup below */ 779 780 i=0; 781 while (i<lastjoin) { 782 /*prepare marker for merge*/ 783 if (i==0) 784 bufscpy(&subs, targetdelta->num); 785 else { 786 bufscat(&subs, ","); 787 bufscat(&subs, joinlist[i-2]); 788 bufscat(&subs, ":"); 789 bufscat(&subs, joinlist[i-1]); 790 } 791 diagnose("revision %s\n",joinlist[i]); 792 bufscpy(&commarg, "-p"); 793 bufscat(&commarg, joinlist[i]); 794 cov[2] = commarg.string; 795 if (runv(-1, rev2, cov)) 796 goto badmerge; 797 diagnose("revision %s\n",joinlist[i+1]); 798 bufscpy(&commarg, "-p"); 799 bufscat(&commarg, joinlist[i+1]); 800 cov[2] = commarg.string; 801 if (runv(-1, rev3, cov)) 802 goto badmerge; 803 diagnose("merging...\n"); 804 mergev[3] = subs.string; 805 mergev[5] = joinlist[i+1]; 806 p = &mergev[6]; 807 if (quietflag) *p++ = quietarg; 808 if (lastjoin<=i+2 && workstdout) *p++ = "-p"; 809 *p++ = initialfile; 810 *p++ = rev2; 811 *p++ = rev3; 812 *p = 0; 813 switch (runv(-1, (char*)0, mergev)) { 814 case DIFF_FAILURE: case DIFF_SUCCESS: 815 break; 816 default: 817 goto badmerge; 818 } 819 i=i+2; 820 } 821 bufautoend(&commarg); 822 bufautoend(&subs); 823 return true; 824 825 badmerge: 826 nerror++; 827 bufautoend(&commarg); 828 bufautoend(&subs); 829 return false; 830} 831