co.c revision 21673
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 * 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 171static char *addjoin P((char*)); 172static char const *getancestor P((char const*,char const*)); 173static int buildjoin P((char const*)); 174static int preparejoin P((char*)); 175static int rmlock P((struct hshentry const*)); 176static int rmworkfile P((void)); 177static void cleanup P((void)); 178 179static char const quietarg[] = "-q"; 180 181static char const *expandarg, *suffixarg, *versionarg, *zonearg, *incexcarg; 182static char const **joinlist; /* revisions to be joined */ 183static int joinlength; 184static FILE *neworkptr; 185static int exitstatus; 186static int forceflag; 187static int lastjoin; /* index of last element in joinlist */ 188static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */ 189static int mtimeflag; 190static struct hshentries *gendeltas; /* deltas to be generated */ 191static struct hshentry *targetdelta; /* final delta to be generated */ 192static struct stat workstat; 193 194mainProg(coId, "co", "$FreeBSD: head/gnu/usr.bin/rcs/co/co.c 21673 1997-01-14 07:20:47Z jkh $") 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 inclusions/exclusions */ 318 incexcarg = *argv; 319 setIncExc(incexcarg); 320 break; 321 322 case 'k': /* set keyword expand mode */ 323 expandarg = *argv; 324 if (0 <= expmode) redefined('k'); 325 if (0 <= (expmode = str2expmode(a))) 326 break; 327 /* fall into */ 328 default: 329 unknown: 330 error("unknown option: %s%s", *argv, cmdusage); 331 332 }; 333 } /* end of option processing */ 334 335 /* Now handle all pathnames. */ 336 if (nerror) cleanup(); 337 else if (argc < 1) faterror("no input file%s", cmdusage); 338 else for (; 0 < argc; cleanup(), ++argv, --argc) { 339 ffree(); 340 341 if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) 342 continue; 343 344 /* 345 * RCSname contains the name of the RCS file, and finptr 346 * points at it. workname contains the name of the working file. 347 * Also, RCSstat has been set. 348 */ 349 diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); 350 351 workstatstat = -1; 352 if (tostdout) { 353# if OPEN_O_BINARY 354 int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0; 355 if (stdout_mode != newmode) { 356 stdout_mode = newmode; 357 oflush(); 358 VOID setmode(STDOUT_FILENO, newmode); 359 } 360# endif 361 neworkname = 0; 362 neworkptr = workstdout = stdout; 363 } else { 364 workstatstat = stat(workname, &workstat); 365 if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) { 366 rcserror("RCS file is the same as working file %s.", 367 workname 368 ); 369 continue; 370 } 371 neworkname = makedirtemp(1); 372 if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) { 373 if (errno == EACCES) 374 workerror("permission denied on parent directory"); 375 else 376 eerror(neworkname); 377 continue; 378 } 379 } 380 381 gettree(); /* reads in the delta tree */ 382 383 if (!Head) { 384 /* no revisions; create empty file */ 385 diagnose("no revisions present; generating empty revision 0.0\n"); 386 if (lockflag) 387 warn( 388 "no revisions, so nothing can be %slocked", 389 lockflag < 0 ? "un" : "" 390 ); 391 Ozclose(&fcopy); 392 if (workstatstat == 0) 393 if (!rmworkfile()) continue; 394 changelock = 0; 395 newdate = 0; 396 } else { 397 int locks = lockflag ? findlock(false, &targetdelta) : 0; 398 if (rev) { 399 /* expand symbolic revision number */ 400 if (!expandsym(rev, &numericrev)) 401 continue; 402 } else { 403 switch (locks) { 404 default: 405 continue; 406 case 0: 407 bufscpy(&numericrev, Dbranch?Dbranch:""); 408 break; 409 case 1: 410 bufscpy(&numericrev, targetdelta->num); 411 break; 412 } 413 } 414 /* get numbers of deltas to be generated */ 415 if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) 416 continue; 417 /* check reservations */ 418 changelock = 419 lockflag < 0 ? 420 rmlock(targetdelta) 421 : lockflag == 0 ? 422 0 423 : 424 addlock(targetdelta, true); 425 426 if ( 427 changelock < 0 428 || (changelock && !checkaccesslist()) 429 || dorewrite(lockflag, changelock) != 0 430 ) 431 continue; 432 433 if (0 <= expmode) 434 Expand = expmode; 435 if (0 < lockflag && Expand == VAL_EXPAND) { 436 rcserror("cannot combine -kv and -l"); 437 continue; 438 } 439 440 if (joinflag && !preparejoin(joinflag)) 441 continue; 442 443 diagnose("revision %s%s\n",targetdelta->num, 444 0<lockflag ? " (locked)" : 445 lockflag<0 ? " (unlocked)" : ""); 446 447 /* Prepare to remove old working file if necessary. */ 448 if (workstatstat == 0) 449 if (!rmworkfile()) continue; 450 451 /* skip description */ 452 getdesc(false); /* don't echo*/ 453 454 locker_expansion = 0 < lockflag; 455 targetdelta->name = namedrev(rev, targetdelta); 456 joinname = buildrevision( 457 gendeltas, targetdelta, 458 joinflag&&tostdout ? (FILE*)0 : neworkptr, 459 Expand < MIN_UNEXPAND 460 ); 461# if !large_memory 462 if (fcopy == neworkptr) 463 fcopy = 0; /* Don't close it twice. */ 464# endif 465 if_advise_access(changelock && gendeltas->first!=targetdelta, 466 finptr, MADV_SEQUENTIAL 467 ); 468 469 if (donerewrite(changelock, 470 Ttimeflag ? RCSstat.st_mtime : (time_t)-1 471 ) != 0) 472 continue; 473 474 if (changelock) { 475 locks += lockflag; 476 if (1 < locks) 477 rcswarn("You now have %d locks.", locks); 478 } 479 480 newdate = targetdelta->date; 481 if (joinflag) { 482 newdate = 0; 483 if (!joinname) { 484 aflush(neworkptr); 485 joinname = neworkname; 486 } 487 if (Expand == BINARY_EXPAND) 488 workerror("merging binary files"); 489 if (!buildjoin(joinname)) 490 continue; 491 } 492 } 493 if (!tostdout) { 494 mode_t m = WORKMODE(RCSstat.st_mode, 495 ! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks)) 496 ); 497 time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1; 498 aflush(neworkptr); 499 ignoreints(); 500 r = chnamemod(&neworkptr, neworkname, workname, 1, m, t); 501 keepdirtemp(neworkname); 502 restoreints(); 503 if (r != 0) { 504 eerror(workname); 505 error("see %s", neworkname); 506 continue; 507 } 508 diagnose("done\n"); 509 } 510 } 511 512 tempunlink(); 513 Ofclose(workstdout); 514 exitmain(exitstatus); 515 516} /* end of main (co) */ 517 518 static void 519cleanup() 520{ 521 if (nerror) exitstatus = EXIT_FAILURE; 522 Izclose(&finptr); 523 ORCSclose(); 524# if !large_memory 525 if (fcopy!=workstdout) Ozclose(&fcopy); 526# endif 527 if (neworkptr!=workstdout) Ozclose(&neworkptr); 528 dirtempunlink(); 529} 530 531#if RCS_lint 532# define exiterr coExit 533#endif 534 void 535exiterr() 536{ 537 ORCSerror(); 538 dirtempunlink(); 539 tempunlink(); 540 _exit(EXIT_FAILURE); 541} 542 543 544/***************************************************************** 545 * The following routines are auxiliary routines 546 *****************************************************************/ 547 548 static int 549rmworkfile() 550/* 551 * Prepare to remove workname, if it exists, and if 552 * it is read-only. 553 * Otherwise (file writable): 554 * if !quietmode asks the user whether to really delete it (default: fail); 555 * otherwise failure. 556 * Returns true if permission is gotten. 557 */ 558{ 559 if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { 560 /* File is writable */ 561 if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", 562 workname, 563 myself(workstat.st_uid) ? "" : ", and you do not own it" 564 )) { 565 error(!quietflag && ttystdin() 566 ? "checkout aborted" 567 : "writable %s exists; checkout aborted", workname); 568 return false; 569 } 570 } 571 /* Actual unlink is done later by caller. */ 572 return true; 573} 574 575 576 static int 577rmlock(delta) 578 struct hshentry const *delta; 579/* Function: removes the lock held by caller on delta. 580 * Returns -1 if someone else holds the lock, 581 * 0 if there is no lock on delta, 582 * and 1 if a lock was found and removed. 583 */ 584{ register struct rcslock * next, * trail; 585 char const *num; 586 struct rcslock dummy; 587 int whomatch, nummatch; 588 589 num=delta->num; 590 dummy.nextlock=next=Locks; 591 trail = &dummy; 592 while (next) { 593 whomatch = strcmp(getcaller(), next->login); 594 nummatch=strcmp(num,next->delta->num); 595 if ((whomatch==0) && (nummatch==0)) break; 596 /*found a lock on delta by caller*/ 597 if ((whomatch!=0)&&(nummatch==0)) { 598 rcserror("revision %s locked by %s; use co -r or rcs -u", 599 num, next->login 600 ); 601 return -1; 602 } 603 trail=next; 604 next=next->nextlock; 605 } 606 if (next) { 607 /*found one; delete it */ 608 trail->nextlock=next->nextlock; 609 Locks=dummy.nextlock; 610 next->delta->lockedby = 0; 611 return 1; /*success*/ 612 } else return 0; /*no lock on delta*/ 613} 614 615 616 617 618/***************************************************************** 619 * The rest of the routines are for handling joins 620 *****************************************************************/ 621 622 623 static char * 624addjoin(joinrev) 625 char *joinrev; 626/* Add joinrev's number to joinlist, yielding address of char past joinrev, 627 * or 0 if no such revision exists. 628 */ 629{ 630 register char *j; 631 register struct hshentry *d; 632 char terminator; 633 struct buf numrev; 634 struct hshentries *joindeltas; 635 636 j = joinrev; 637 for (;;) { 638 switch (*j++) { 639 default: 640 continue; 641 case 0: 642 case ' ': case '\t': case '\n': 643 case ':': case ',': case ';': 644 break; 645 } 646 break; 647 } 648 terminator = *--j; 649 *j = 0; 650 bufautobegin(&numrev); 651 d = 0; 652 if (expandsym(joinrev, &numrev)) 653 d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas); 654 bufautoend(&numrev); 655 *j = terminator; 656 if (d) { 657 joinlist[++lastjoin] = d->num; 658 return j; 659 } 660 return 0; 661} 662 663 static int 664preparejoin(j) 665 register char *j; 666/* Parse join list J and place pointers to the 667 * revision numbers into joinlist. 668 */ 669{ 670 lastjoin= -1; 671 for (;;) { 672 while ((*j==' ')||(*j=='\t')||(*j==',')) j++; 673 if (*j=='\0') break; 674 if (lastjoin>=joinlength-2) { 675 joinlist = 676 (joinlength *= 2) == 0 677 ? tnalloc(char const *, joinlength = 16) 678 : trealloc(char const *, joinlist, joinlength); 679 } 680 if (!(j = addjoin(j))) return false; 681 while ((*j==' ') || (*j=='\t')) j++; 682 if (*j == ':') { 683 j++; 684 while((*j==' ') || (*j=='\t')) j++; 685 if (*j!='\0') { 686 if (!(j = addjoin(j))) return false; 687 } else { 688 rcsfaterror("join pair incomplete"); 689 } 690 } else { 691 if (lastjoin==0) { /* first pair */ 692 /* common ancestor missing */ 693 joinlist[1]=joinlist[0]; 694 lastjoin=1; 695 /*derive common ancestor*/ 696 if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) 697 return false; 698 } else { 699 rcsfaterror("join pair incomplete"); 700 } 701 } 702 } 703 if (lastjoin < 1) 704 rcsfaterror("empty join"); 705 return true; 706} 707 708 709 710 static char const * 711getancestor(r1, r2) 712 char const *r1, *r2; 713/* Yield the common ancestor of r1 and r2 if successful, 0 otherwise. 714 * Work reliably only if r1 and r2 are not branch numbers. 715 */ 716{ 717 static struct buf t1, t2; 718 719 int l1, l2, l3; 720 char const *r; 721 722 l1 = countnumflds(r1); 723 l2 = countnumflds(r2); 724 if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) { 725 /* not on main trunk or identical */ 726 l3 = 0; 727 while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0) 728 l3 += 2; 729 /* This will terminate since r1 and r2 are not the same; see above. */ 730 if (l3==0) { 731 /* no common prefix; common ancestor on main trunk */ 732 VOID partialno(&t1, r1, l1>2 ? 2 : l1); 733 VOID partialno(&t2, r2, l2>2 ? 2 : l2); 734 r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; 735 if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) 736 return r; 737 } else if (cmpnumfld(r1, r2, l3+1)!=0) 738 return partialno(&t1,r1,l3); 739 } 740 rcserror("common ancestor of %s and %s undefined", r1, r2); 741 return 0; 742} 743 744 745 746 static int 747buildjoin(initialfile) 748 char const *initialfile; 749/* Function: merge pairs of elements in joinlist into initialfile 750 * If workstdout is set, copy result to stdout. 751 * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). 752 */ 753{ 754 struct buf commarg; 755 struct buf subs; 756 char const *rev2, *rev3; 757 int i; 758 char const *cov[10], *mergev[11]; 759 char const **p; 760 761 bufautobegin(&commarg); 762 bufautobegin(&subs); 763 rev2 = maketemp(0); 764 rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ 765 766 cov[1] = CO; 767 /* cov[2] setup below */ 768 p = &cov[3]; 769 if (expandarg) *p++ = expandarg; 770 if (suffixarg) *p++ = suffixarg; 771 if (versionarg) *p++ = versionarg; 772 if (zonearg) *p++ = zonearg; 773 *p++ = quietarg; 774 *p++ = RCSname; 775 *p = 0; 776 777 mergev[1] = MERGE; 778 mergev[2] = mergev[4] = "-L"; 779 /* rest of mergev setup below */ 780 781 i=0; 782 while (i<lastjoin) { 783 /*prepare marker for merge*/ 784 if (i==0) 785 bufscpy(&subs, targetdelta->num); 786 else { 787 bufscat(&subs, ","); 788 bufscat(&subs, joinlist[i-2]); 789 bufscat(&subs, ":"); 790 bufscat(&subs, joinlist[i-1]); 791 } 792 diagnose("revision %s\n",joinlist[i]); 793 bufscpy(&commarg, "-p"); 794 bufscat(&commarg, joinlist[i]); 795 cov[2] = commarg.string; 796 if (runv(-1, rev2, cov)) 797 goto badmerge; 798 diagnose("revision %s\n",joinlist[i+1]); 799 bufscpy(&commarg, "-p"); 800 bufscat(&commarg, joinlist[i+1]); 801 cov[2] = commarg.string; 802 if (runv(-1, rev3, cov)) 803 goto badmerge; 804 diagnose("merging...\n"); 805 mergev[3] = subs.string; 806 mergev[5] = joinlist[i+1]; 807 p = &mergev[6]; 808 if (quietflag) *p++ = quietarg; 809 if (lastjoin<=i+2 && workstdout) *p++ = "-p"; 810 *p++ = initialfile; 811 *p++ = rev2; 812 *p++ = rev3; 813 *p = 0; 814 switch (runv(-1, (char*)0, mergev)) { 815 case DIFF_FAILURE: case DIFF_SUCCESS: 816 break; 817 default: 818 goto badmerge; 819 } 820 i=i+2; 821 } 822 bufautoend(&commarg); 823 bufautoend(&subs); 824 return true; 825 826 badmerge: 827 nerror++; 828 bufautoend(&commarg); 829 bufautoend(&subs); 830 return false; 831} 832