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