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