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