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