1 /* $NetBSD: co.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */
2
3 /* Check out working files from revisions of RCS files. */
4
5 /* Copyright 1982, 1988, 1989 Walter Tichy
6 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
7 Distributed under license by the Free Software Foundation, Inc.
8
9 This file is part of RCS.
10
11 RCS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
14 any later version.
15
16 RCS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with RCS; see the file COPYING.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 Report problems and direct all questions to:
27
28 rcs-bugs@cs.purdue.edu
29
30 */
31
32 /*
33 * Log: co.c,v
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", "Id: co.c,v 5.18 1995/06/16 06:19:24 eggert 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
cleanup()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
exiterr()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
rmworkfile()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
rmlock(delta)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 *
addjoin(joinrev)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
preparejoin(j)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 *
getancestor(r1,r2)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
buildjoin(initialfile)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