xref: /dflybsd-src/gnu/usr.bin/rcs/co/co.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* Check out working files from revisions of RCS files.  */
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
486d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
586d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
686d7f5d3SJohn Marino 
786d7f5d3SJohn Marino This file is part of RCS.
886d7f5d3SJohn Marino 
986d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
1086d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
1186d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
1286d7f5d3SJohn Marino any later version.
1386d7f5d3SJohn Marino 
1486d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
1586d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
1686d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1786d7f5d3SJohn Marino GNU General Public License for more details.
1886d7f5d3SJohn Marino 
1986d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
2086d7f5d3SJohn Marino along with RCS; see the file COPYING.
2186d7f5d3SJohn Marino If not, write to the Free Software Foundation,
2286d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2386d7f5d3SJohn Marino 
2486d7f5d3SJohn Marino Report problems and direct all questions to:
2586d7f5d3SJohn Marino 
2686d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
2786d7f5d3SJohn Marino 
2886d7f5d3SJohn Marino */
2986d7f5d3SJohn Marino 
3086d7f5d3SJohn Marino /*
3186d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/co/co.c,v 1.10 1999/08/27 23:36:40 peter Exp $
3286d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/co/co.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
3386d7f5d3SJohn Marino  *
3486d7f5d3SJohn Marino  * Revision 5.18  1995/06/16 06:19:24  eggert
3586d7f5d3SJohn Marino  * Update FSF address.
3686d7f5d3SJohn Marino  *
3786d7f5d3SJohn Marino  * Revision 5.17  1995/06/01 16:23:43  eggert
3886d7f5d3SJohn Marino  * (main, preparejoin): Pass argument instead of using `join' static variable.
3986d7f5d3SJohn Marino  * (main): Add -kb.
4086d7f5d3SJohn Marino  *
4186d7f5d3SJohn Marino  * Revision 5.16  1994/03/17 14:05:48  eggert
4286d7f5d3SJohn Marino  * Move buffer-flushes out of critical sections, since they aren't critical.
4386d7f5d3SJohn Marino  * Use ORCSerror to clean up after a fatal error.  Remove lint.
4486d7f5d3SJohn Marino  * Specify subprocess input via file descriptor, not file name.
4586d7f5d3SJohn Marino  *
4686d7f5d3SJohn Marino  * Revision 5.15  1993/11/09 17:40:15  eggert
4786d7f5d3SJohn Marino  * -V now prints version on stdout and exits.  Don't print usage twice.
4886d7f5d3SJohn Marino  *
4986d7f5d3SJohn Marino  * Revision 5.14  1993/11/03 17:42:27  eggert
5086d7f5d3SJohn Marino  * Add -z.  Generate a value for the Name keyword.
5186d7f5d3SJohn Marino  * Don't arbitrarily limit the number of joins.
5286d7f5d3SJohn Marino  * Improve quality of diagnostics.
5386d7f5d3SJohn Marino  *
5486d7f5d3SJohn Marino  * Revision 5.13  1992/07/28  16:12:44  eggert
5586d7f5d3SJohn Marino  * Add -V.  Check that working and RCS files are distinct.
5686d7f5d3SJohn Marino  *
5786d7f5d3SJohn Marino  * Revision 5.12  1992/02/17  23:02:08  eggert
5886d7f5d3SJohn Marino  * Add -T.
5986d7f5d3SJohn Marino  *
6086d7f5d3SJohn Marino  * Revision 5.11  1992/01/24  18:44:19  eggert
6186d7f5d3SJohn Marino  * Add support for bad_creat0.  lint -> RCS_lint
6286d7f5d3SJohn Marino  *
6386d7f5d3SJohn Marino  * Revision 5.10  1992/01/06  02:42:34  eggert
6486d7f5d3SJohn Marino  * Update usage string.
6586d7f5d3SJohn Marino  *
6686d7f5d3SJohn Marino  * Revision 5.9  1991/10/07  17:32:46  eggert
6786d7f5d3SJohn Marino  * -k affects just working file, not RCS file.
6886d7f5d3SJohn Marino  *
6986d7f5d3SJohn Marino  * Revision 5.8  1991/08/19  03:13:55  eggert
7086d7f5d3SJohn Marino  * Warn before removing somebody else's file.
7186d7f5d3SJohn Marino  * Add -M.  Fix co -j bugs.  Tune.
7286d7f5d3SJohn Marino  *
7386d7f5d3SJohn Marino  * Revision 5.7  1991/04/21  11:58:15  eggert
7486d7f5d3SJohn Marino  * Ensure that working file is newer than RCS file after co -[lu].
7586d7f5d3SJohn Marino  * Add -x, RCSINIT, MS-DOS support.
7686d7f5d3SJohn Marino  *
7786d7f5d3SJohn Marino  * Revision 5.6  1990/12/04  05:18:38  eggert
7886d7f5d3SJohn Marino  * Don't checkaccesslist() unless necessary.
7986d7f5d3SJohn Marino  * Use -I for prompts and -q for diagnostics.
8086d7f5d3SJohn Marino  *
8186d7f5d3SJohn Marino  * Revision 5.5  1990/11/01  05:03:26  eggert
8286d7f5d3SJohn Marino  * Fix -j.  Add -I.
8386d7f5d3SJohn Marino  *
8486d7f5d3SJohn Marino  * Revision 5.4  1990/10/04  06:30:11  eggert
8586d7f5d3SJohn Marino  * Accumulate exit status across files.
8686d7f5d3SJohn Marino  *
8786d7f5d3SJohn Marino  * Revision 5.3  1990/09/11  02:41:09  eggert
8886d7f5d3SJohn Marino  * co -kv yields a readonly working file.
8986d7f5d3SJohn Marino  *
9086d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:13  eggert
9186d7f5d3SJohn Marino  * Standardize yes-or-no procedure.
9286d7f5d3SJohn Marino  *
9386d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:10:02  eggert
9486d7f5d3SJohn Marino  * Permit multiple locks by same user.  Add setuid support.
9586d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.
9686d7f5d3SJohn Marino  * Permit dates past 1999/12/31.  Switch to GMT.
9786d7f5d3SJohn Marino  * Make lock and temp files faster and safer.
9886d7f5d3SJohn Marino  * Ansify and Posixate.  Add -k, -V.  Remove snooping.  Tune.
9986d7f5d3SJohn Marino  *
10086d7f5d3SJohn Marino  * Revision 4.7  89/05/01  15:11:41  narten
10186d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
10286d7f5d3SJohn Marino  *
10386d7f5d3SJohn Marino  * Revision 4.6  88/08/09  19:12:15  eggert
10486d7f5d3SJohn Marino  * Fix "co -d" core dump; rawdate wasn't always initialized.
10586d7f5d3SJohn Marino  * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
10686d7f5d3SJohn Marino  *
10786d7f5d3SJohn Marino  * Revision 4.5  87/12/18  11:35:40  narten
10886d7f5d3SJohn Marino  * lint cleanups (from Guy Harris)
10986d7f5d3SJohn Marino  *
11086d7f5d3SJohn Marino  * Revision 4.4  87/10/18  10:20:53  narten
11186d7f5d3SJohn Marino  * Updating version numbers changes relative to 1.1, are actually
11286d7f5d3SJohn Marino  * relative to 4.2
11386d7f5d3SJohn Marino  *
11486d7f5d3SJohn Marino  * Revision 1.3  87/09/24  13:58:30  narten
11586d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
11686d7f5d3SJohn Marino  * warnings)
11786d7f5d3SJohn Marino  *
11886d7f5d3SJohn Marino  * Revision 1.2  87/03/27  14:21:38  jenkins
11986d7f5d3SJohn Marino  * Port to suns
12086d7f5d3SJohn Marino  *
12186d7f5d3SJohn Marino  * Revision 4.2  83/12/05  13:39:48  wft
12286d7f5d3SJohn Marino  * made rewriteflag external.
12386d7f5d3SJohn Marino  *
12486d7f5d3SJohn Marino  * Revision 4.1  83/05/10  16:52:55  wft
12586d7f5d3SJohn Marino  * Added option -u and -f.
12686d7f5d3SJohn Marino  * Added handling of default branch.
12786d7f5d3SJohn Marino  * Replaced getpwuid() with getcaller().
12886d7f5d3SJohn Marino  * Removed calls to stat(); now done by pairfilenames().
12986d7f5d3SJohn Marino  * Changed and renamed rmoldfile() to rmworkfile().
13086d7f5d3SJohn Marino  * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
13186d7f5d3SJohn Marino  *
13286d7f5d3SJohn Marino  * Revision 3.7  83/02/15  15:27:07  wft
13386d7f5d3SJohn Marino  * Added call to fastcopy() to copy remainder of RCS file.
13486d7f5d3SJohn Marino  *
13586d7f5d3SJohn Marino  * Revision 3.6  83/01/15  14:37:50  wft
13686d7f5d3SJohn Marino  * Added ignoring of interrupts while RCS file is renamed; this avoids
13786d7f5d3SJohn Marino  * deletion of RCS files during the unlink/link window.
13886d7f5d3SJohn Marino  *
13986d7f5d3SJohn Marino  * Revision 3.5  82/12/08  21:40:11  wft
14086d7f5d3SJohn Marino  * changed processing of -d to use DATEFORM; removed actual from
14186d7f5d3SJohn Marino  * call to preparejoin; re-fixed printing of done at the end.
14286d7f5d3SJohn Marino  *
14386d7f5d3SJohn Marino  * Revision 3.4  82/12/04  18:40:00  wft
14486d7f5d3SJohn Marino  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
14586d7f5d3SJohn Marino  * Fixed printing of "done".
14686d7f5d3SJohn Marino  *
14786d7f5d3SJohn Marino  * Revision 3.3  82/11/28  22:23:11  wft
14886d7f5d3SJohn Marino  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
14986d7f5d3SJohn Marino  * %02d with %.2d, mode generation for working file with WORKMODE.
15086d7f5d3SJohn Marino  * Fixed nil printing. Fixed -j combined with -l and -p, and exit
15186d7f5d3SJohn Marino  * for non-existing revisions in preparejoin().
15286d7f5d3SJohn Marino  *
15386d7f5d3SJohn Marino  * Revision 3.2  82/10/18  20:47:21  wft
15486d7f5d3SJohn Marino  * Mode of working file is now maintained even for co -l, but write permission
15586d7f5d3SJohn Marino  * is removed.
15686d7f5d3SJohn Marino  * The working file inherits its mode from the RCS file, plus write permission
15786d7f5d3SJohn Marino  * for the owner. The write permission is not given if locking is strict and
15886d7f5d3SJohn Marino  * co does not lock.
15986d7f5d3SJohn Marino  * An existing working file without write permission is deleted automatically.
16086d7f5d3SJohn Marino  * Otherwise, co asks (empty answer: abort co).
16186d7f5d3SJohn Marino  * Call to getfullRCSname() added, check for write error added, call
16286d7f5d3SJohn Marino  * for getlogin() fixed.
16386d7f5d3SJohn Marino  *
16486d7f5d3SJohn Marino  * Revision 3.1  82/10/13  16:01:30  wft
16586d7f5d3SJohn Marino  * fixed type of variables receiving from getc() (char -> int).
16686d7f5d3SJohn Marino  * removed unused variables.
16786d7f5d3SJohn Marino  */
16886d7f5d3SJohn Marino 
16986d7f5d3SJohn Marino 
17086d7f5d3SJohn Marino 
17186d7f5d3SJohn Marino 
17286d7f5d3SJohn Marino #include "rcsbase.h"
17386d7f5d3SJohn Marino 
17486d7f5d3SJohn Marino static char *addjoin P((char*));
17586d7f5d3SJohn Marino static char const *getancestor P((char const*,char const*));
17686d7f5d3SJohn Marino static int buildjoin P((char const*));
17786d7f5d3SJohn Marino static int preparejoin P((char*));
17886d7f5d3SJohn Marino static int rmlock P((struct hshentry const*));
17986d7f5d3SJohn Marino static int rmworkfile P((void));
18086d7f5d3SJohn Marino static void cleanup P((void));
18186d7f5d3SJohn Marino 
18286d7f5d3SJohn Marino static char const quietarg[] = "-q";
18386d7f5d3SJohn Marino 
18486d7f5d3SJohn Marino static char const *expandarg, *suffixarg, *versionarg, *zonearg;
18586d7f5d3SJohn Marino static char const **joinlist; /* revisions to be joined */
18686d7f5d3SJohn Marino static int joinlength;
18786d7f5d3SJohn Marino static FILE *neworkptr;
18886d7f5d3SJohn Marino static int exitstatus;
18986d7f5d3SJohn Marino static int forceflag;
19086d7f5d3SJohn Marino static int lastjoin;			/* index of last element in joinlist  */
19186d7f5d3SJohn Marino static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
19286d7f5d3SJohn Marino static int mtimeflag;
19386d7f5d3SJohn Marino static struct hshentries *gendeltas;	/* deltas to be generated	*/
19486d7f5d3SJohn Marino static struct hshentry *targetdelta;	/* final delta to be generated	*/
19586d7f5d3SJohn Marino static struct stat workstat;
19686d7f5d3SJohn Marino 
19786d7f5d3SJohn Marino mainProg(coId, "co", "$DragonFly: src/gnu/usr.bin/rcs/co/co.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
19886d7f5d3SJohn Marino {
19986d7f5d3SJohn Marino 	static char const cmdusage[] =
20086d7f5d3SJohn Marino 		"\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ...";
20186d7f5d3SJohn Marino 
20286d7f5d3SJohn Marino 	char *a, *joinflag, **newargv;
20386d7f5d3SJohn Marino 	char const *author, *date, *rev, *state;
20486d7f5d3SJohn Marino 	char const *joinname, *newdate, *neworkname;
20586d7f5d3SJohn Marino 	int changelock;  /* 1 if a lock has been changed, -1 if error */
20686d7f5d3SJohn Marino 	int expmode, r, tostdout, workstatstat;
20786d7f5d3SJohn Marino 	int Ttimeflag;
20886d7f5d3SJohn Marino 	struct buf numericrev;	/* expanded revision number	*/
20986d7f5d3SJohn Marino 	char finaldate[datesize];
21086d7f5d3SJohn Marino #	if OPEN_O_BINARY
21186d7f5d3SJohn Marino 		int stdout_mode = 0;
21286d7f5d3SJohn Marino #	endif
21386d7f5d3SJohn Marino 
21486d7f5d3SJohn Marino 	setrid();
21586d7f5d3SJohn Marino 	author = date = rev = state = 0;
21686d7f5d3SJohn Marino 	joinflag = 0;
21786d7f5d3SJohn Marino 	bufautobegin(&numericrev);
21886d7f5d3SJohn Marino 	expmode = -1;
21986d7f5d3SJohn Marino 	suffixes = X_DEFAULT;
22086d7f5d3SJohn Marino 	tostdout = false;
22186d7f5d3SJohn Marino 	Ttimeflag = false;
22286d7f5d3SJohn Marino 
22386d7f5d3SJohn Marino 	argc = getRCSINIT(argc, argv, &newargv);
22486d7f5d3SJohn Marino 	argv = newargv;
22586d7f5d3SJohn Marino 	while (a = *++argv,  0<--argc && *a++=='-') {
22686d7f5d3SJohn Marino 		switch (*a++) {
22786d7f5d3SJohn Marino 
22886d7f5d3SJohn Marino                 case 'r':
22986d7f5d3SJohn Marino 		revno:
23086d7f5d3SJohn Marino 			if (*a) {
23186d7f5d3SJohn Marino 				if (rev) warn("redefinition of revision number");
23286d7f5d3SJohn Marino 				rev = a;
23386d7f5d3SJohn Marino                         }
23486d7f5d3SJohn Marino                         break;
23586d7f5d3SJohn Marino 
23686d7f5d3SJohn Marino 		case 'f':
23786d7f5d3SJohn Marino 			forceflag=true;
23886d7f5d3SJohn Marino 			goto revno;
23986d7f5d3SJohn Marino 
24086d7f5d3SJohn Marino                 case 'l':
24186d7f5d3SJohn Marino 			if (lockflag < 0) {
24286d7f5d3SJohn Marino 				warn("-u overridden by -l.");
24386d7f5d3SJohn Marino                         }
24486d7f5d3SJohn Marino 			lockflag = 1;
24586d7f5d3SJohn Marino                         goto revno;
24686d7f5d3SJohn Marino 
24786d7f5d3SJohn Marino                 case 'u':
24886d7f5d3SJohn Marino 			if (0 < lockflag) {
24986d7f5d3SJohn Marino 				warn("-l overridden by -u.");
25086d7f5d3SJohn Marino                         }
25186d7f5d3SJohn Marino 			lockflag = -1;
25286d7f5d3SJohn Marino                         goto revno;
25386d7f5d3SJohn Marino 
25486d7f5d3SJohn Marino                 case 'p':
25586d7f5d3SJohn Marino 			tostdout = true;
25686d7f5d3SJohn Marino                         goto revno;
25786d7f5d3SJohn Marino 
25886d7f5d3SJohn Marino 		case 'I':
25986d7f5d3SJohn Marino 			interactiveflag = true;
26086d7f5d3SJohn Marino 			goto revno;
26186d7f5d3SJohn Marino 
26286d7f5d3SJohn Marino                 case 'q':
26386d7f5d3SJohn Marino                         quietflag=true;
26486d7f5d3SJohn Marino                         goto revno;
26586d7f5d3SJohn Marino 
26686d7f5d3SJohn Marino                 case 'd':
26786d7f5d3SJohn Marino 			if (date)
26886d7f5d3SJohn Marino 				redefined('d');
26986d7f5d3SJohn Marino 			str2date(a, finaldate);
27086d7f5d3SJohn Marino                         date=finaldate;
27186d7f5d3SJohn Marino                         break;
27286d7f5d3SJohn Marino 
27386d7f5d3SJohn Marino                 case 'j':
27486d7f5d3SJohn Marino 			if (*a) {
27586d7f5d3SJohn Marino 				if (joinflag) redefined('j');
27686d7f5d3SJohn Marino 				joinflag = a;
27786d7f5d3SJohn Marino                         }
27886d7f5d3SJohn Marino                         break;
27986d7f5d3SJohn Marino 
28086d7f5d3SJohn Marino 		case 'M':
28186d7f5d3SJohn Marino 			mtimeflag = true;
28286d7f5d3SJohn Marino 			goto revno;
28386d7f5d3SJohn Marino 
28486d7f5d3SJohn Marino                 case 's':
28586d7f5d3SJohn Marino 			if (*a) {
28686d7f5d3SJohn Marino 				if (state) redefined('s');
28786d7f5d3SJohn Marino 				state = a;
28886d7f5d3SJohn Marino                         }
28986d7f5d3SJohn Marino                         break;
29086d7f5d3SJohn Marino 
29186d7f5d3SJohn Marino 		case 'T':
29286d7f5d3SJohn Marino 			if (*a)
29386d7f5d3SJohn Marino 				goto unknown;
29486d7f5d3SJohn Marino 			Ttimeflag = true;
29586d7f5d3SJohn Marino 			break;
29686d7f5d3SJohn Marino 
29786d7f5d3SJohn Marino                 case 'w':
29886d7f5d3SJohn Marino 			if (author) redefined('w');
29986d7f5d3SJohn Marino 			if (*a)
30086d7f5d3SJohn Marino 				author = a;
30186d7f5d3SJohn Marino 			else
30286d7f5d3SJohn Marino 				author = getcaller();
30386d7f5d3SJohn Marino                         break;
30486d7f5d3SJohn Marino 
30586d7f5d3SJohn Marino 		case 'x':
30686d7f5d3SJohn Marino 			suffixarg = *argv;
30786d7f5d3SJohn Marino 			suffixes = a;
30886d7f5d3SJohn Marino 			break;
30986d7f5d3SJohn Marino 
31086d7f5d3SJohn Marino 		case 'V':
31186d7f5d3SJohn Marino 			versionarg = *argv;
31286d7f5d3SJohn Marino 			setRCSversion(versionarg);
31386d7f5d3SJohn Marino 			break;
31486d7f5d3SJohn Marino 
31586d7f5d3SJohn Marino 		case 'z':
31686d7f5d3SJohn Marino 			zonearg = *argv;
31786d7f5d3SJohn Marino 			zone_set(a);
31886d7f5d3SJohn Marino 			break;
31986d7f5d3SJohn Marino 
32086d7f5d3SJohn Marino 		case 'k':    /*  set keyword expand mode  */
32186d7f5d3SJohn Marino 			expandarg = *argv;
32286d7f5d3SJohn Marino 			if (0 <= expmode) redefined('k');
32386d7f5d3SJohn Marino 			if (0 <= (expmode = str2expmode(a)))
32486d7f5d3SJohn Marino 			    break;
32586d7f5d3SJohn Marino 			/* fall into */
32686d7f5d3SJohn Marino                 default:
32786d7f5d3SJohn Marino 		unknown:
32886d7f5d3SJohn Marino 			error("unknown option: %s%s", *argv, cmdusage);
32986d7f5d3SJohn Marino 
33086d7f5d3SJohn Marino                 };
33186d7f5d3SJohn Marino         } /* end of option processing */
33286d7f5d3SJohn Marino 
33386d7f5d3SJohn Marino 	/* Now handle all pathnames.  */
33486d7f5d3SJohn Marino 	if (nerror) cleanup();
33586d7f5d3SJohn Marino 	else if (argc < 1) faterror("no input file%s", cmdusage);
33686d7f5d3SJohn Marino 	else for (;  0 < argc;  cleanup(), ++argv, --argc) {
33786d7f5d3SJohn Marino 	ffree();
33886d7f5d3SJohn Marino 
33986d7f5d3SJohn Marino 	if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false)  <=  0)
34086d7f5d3SJohn Marino 		continue;
34186d7f5d3SJohn Marino 
34286d7f5d3SJohn Marino 	/*
34386d7f5d3SJohn Marino 	 * RCSname contains the name of the RCS file, and finptr
34486d7f5d3SJohn Marino 	 * points at it.  workname contains the name of the working file.
34586d7f5d3SJohn Marino 	 * Also, RCSstat has been set.
34686d7f5d3SJohn Marino          */
34786d7f5d3SJohn Marino 	diagnose("%s  -->  %s\n", RCSname, tostdout?"standard output":workname);
34886d7f5d3SJohn Marino 
34986d7f5d3SJohn Marino 	workstatstat = -1;
35086d7f5d3SJohn Marino 	if (tostdout) {
35186d7f5d3SJohn Marino #		if OPEN_O_BINARY
35286d7f5d3SJohn Marino 		    int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0;
35386d7f5d3SJohn Marino 		    if (stdout_mode != newmode) {
35486d7f5d3SJohn Marino 			stdout_mode = newmode;
35586d7f5d3SJohn Marino 			oflush();
35686d7f5d3SJohn Marino 			VOID setmode(STDOUT_FILENO, newmode);
35786d7f5d3SJohn Marino 		    }
35886d7f5d3SJohn Marino #		endif
35986d7f5d3SJohn Marino 		neworkname = 0;
36086d7f5d3SJohn Marino 		neworkptr = workstdout = stdout;
36186d7f5d3SJohn Marino 	} else {
36286d7f5d3SJohn Marino 		workstatstat = stat(workname, &workstat);
36386d7f5d3SJohn Marino 		if (workstatstat == 0  &&  same_file(RCSstat, workstat, 0)) {
36486d7f5d3SJohn Marino 			rcserror("RCS file is the same as working file %s.",
36586d7f5d3SJohn Marino 				workname
36686d7f5d3SJohn Marino 			);
36786d7f5d3SJohn Marino 			continue;
36886d7f5d3SJohn Marino 		}
36986d7f5d3SJohn Marino 		neworkname = makedirtemp(1);
37086d7f5d3SJohn Marino 		if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) {
37186d7f5d3SJohn Marino 			if (errno == EACCES)
37286d7f5d3SJohn Marino 			    workerror("permission denied on parent directory");
37386d7f5d3SJohn Marino 			else
37486d7f5d3SJohn Marino 			    eerror(neworkname);
37586d7f5d3SJohn Marino 			continue;
37686d7f5d3SJohn Marino 		}
37786d7f5d3SJohn Marino 	}
37886d7f5d3SJohn Marino 
37986d7f5d3SJohn Marino         gettree();  /* reads in the delta tree */
38086d7f5d3SJohn Marino 
38186d7f5d3SJohn Marino 	if (!Head) {
38286d7f5d3SJohn Marino                 /* no revisions; create empty file */
38386d7f5d3SJohn Marino 		diagnose("no revisions present; generating empty revision 0.0\n");
38486d7f5d3SJohn Marino 		if (lockflag)
38586d7f5d3SJohn Marino 			warn(
38686d7f5d3SJohn Marino 				"no revisions, so nothing can be %slocked",
38786d7f5d3SJohn Marino 				lockflag < 0 ? "un" : ""
38886d7f5d3SJohn Marino 			);
38986d7f5d3SJohn Marino 		Ozclose(&fcopy);
39086d7f5d3SJohn Marino 		if (workstatstat == 0)
39186d7f5d3SJohn Marino 			if (!rmworkfile()) continue;
39286d7f5d3SJohn Marino 		changelock = 0;
39386d7f5d3SJohn Marino 		newdate = 0;
39486d7f5d3SJohn Marino         } else {
39586d7f5d3SJohn Marino 		int locks = lockflag ? findlock(false, &targetdelta) : 0;
39686d7f5d3SJohn Marino 		if (rev) {
39786d7f5d3SJohn Marino                         /* expand symbolic revision number */
39886d7f5d3SJohn Marino 			if (!expandsym(rev, &numericrev))
39986d7f5d3SJohn Marino                                 continue;
40086d7f5d3SJohn Marino 		} else {
40186d7f5d3SJohn Marino 			switch (locks) {
40286d7f5d3SJohn Marino 			    default:
40386d7f5d3SJohn Marino 				continue;
40486d7f5d3SJohn Marino 			    case 0:
40586d7f5d3SJohn Marino 				bufscpy(&numericrev, Dbranch?Dbranch:"");
40686d7f5d3SJohn Marino 				break;
40786d7f5d3SJohn Marino 			    case 1:
40886d7f5d3SJohn Marino 				bufscpy(&numericrev, targetdelta->num);
40986d7f5d3SJohn Marino 				break;
41086d7f5d3SJohn Marino 			}
41186d7f5d3SJohn Marino 		}
41286d7f5d3SJohn Marino                 /* get numbers of deltas to be generated */
41386d7f5d3SJohn Marino 		if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
41486d7f5d3SJohn Marino                         continue;
41586d7f5d3SJohn Marino                 /* check reservations */
41686d7f5d3SJohn Marino 		changelock =
41786d7f5d3SJohn Marino 			lockflag < 0 ?
41886d7f5d3SJohn Marino 				rmlock(targetdelta)
41986d7f5d3SJohn Marino 			: lockflag == 0 ?
42086d7f5d3SJohn Marino 				0
42186d7f5d3SJohn Marino 			:
42286d7f5d3SJohn Marino 				addlock(targetdelta, true);
42386d7f5d3SJohn Marino 
42486d7f5d3SJohn Marino 		if (
42586d7f5d3SJohn Marino 			changelock < 0
42686d7f5d3SJohn Marino 			|| (changelock && !checkaccesslist())
42786d7f5d3SJohn Marino 			|| dorewrite(lockflag, changelock) != 0
42886d7f5d3SJohn Marino 		)
42986d7f5d3SJohn Marino 			continue;
43086d7f5d3SJohn Marino 
43186d7f5d3SJohn Marino 		if (0 <= expmode)
43286d7f5d3SJohn Marino 			Expand = expmode;
43386d7f5d3SJohn Marino 		if (0 < lockflag  &&  Expand == VAL_EXPAND) {
43486d7f5d3SJohn Marino 			rcserror("cannot combine -kv and -l");
43586d7f5d3SJohn Marino 			continue;
43686d7f5d3SJohn Marino 		}
43786d7f5d3SJohn Marino 
43886d7f5d3SJohn Marino 		if (joinflag && !preparejoin(joinflag))
43986d7f5d3SJohn Marino 			continue;
44086d7f5d3SJohn Marino 
44186d7f5d3SJohn Marino 		diagnose("revision %s%s\n",targetdelta->num,
44286d7f5d3SJohn Marino 			 0<lockflag ? " (locked)" :
44386d7f5d3SJohn Marino 			 lockflag<0 ? " (unlocked)" : "");
44486d7f5d3SJohn Marino 
44586d7f5d3SJohn Marino 		/* Prepare to remove old working file if necessary.  */
44686d7f5d3SJohn Marino 		if (workstatstat == 0)
44786d7f5d3SJohn Marino                         if (!rmworkfile()) continue;
44886d7f5d3SJohn Marino 
44986d7f5d3SJohn Marino                 /* skip description */
45086d7f5d3SJohn Marino                 getdesc(false); /* don't echo*/
45186d7f5d3SJohn Marino 
45286d7f5d3SJohn Marino 		locker_expansion = 0 < lockflag;
45386d7f5d3SJohn Marino 		targetdelta->name = namedrev(rev, targetdelta);
45486d7f5d3SJohn Marino 		joinname = buildrevision(
45586d7f5d3SJohn Marino 			gendeltas, targetdelta,
45686d7f5d3SJohn Marino 			joinflag&&tostdout ? (FILE*)0 : neworkptr,
45786d7f5d3SJohn Marino 			Expand < MIN_UNEXPAND
45886d7f5d3SJohn Marino 		);
45986d7f5d3SJohn Marino #		if !large_memory
46086d7f5d3SJohn Marino 			if (fcopy == neworkptr)
46186d7f5d3SJohn Marino 				fcopy = 0;  /* Don't close it twice.  */
46286d7f5d3SJohn Marino #		endif
46386d7f5d3SJohn Marino 		if_advise_access(changelock && gendeltas->first!=targetdelta,
46486d7f5d3SJohn Marino 			finptr, MADV_SEQUENTIAL
46586d7f5d3SJohn Marino 		);
46686d7f5d3SJohn Marino 
46786d7f5d3SJohn Marino 		if (donerewrite(changelock,
46886d7f5d3SJohn Marino 			Ttimeflag ? RCSstat.st_mtime : (time_t)-1
46986d7f5d3SJohn Marino 		) != 0)
47086d7f5d3SJohn Marino 			continue;
47186d7f5d3SJohn Marino 
47286d7f5d3SJohn Marino 		if (changelock) {
47386d7f5d3SJohn Marino 			locks += lockflag;
47486d7f5d3SJohn Marino 			if (1 < locks)
47586d7f5d3SJohn Marino 				rcswarn("You now have %d locks.", locks);
47686d7f5d3SJohn Marino 		}
47786d7f5d3SJohn Marino 
47886d7f5d3SJohn Marino 		newdate = targetdelta->date;
47986d7f5d3SJohn Marino 		if (joinflag) {
48086d7f5d3SJohn Marino 			newdate = 0;
48186d7f5d3SJohn Marino 			if (!joinname) {
48286d7f5d3SJohn Marino 				aflush(neworkptr);
48386d7f5d3SJohn Marino 				joinname = neworkname;
48486d7f5d3SJohn Marino 			}
48586d7f5d3SJohn Marino 			if (Expand == BINARY_EXPAND)
48686d7f5d3SJohn Marino 				workerror("merging binary files");
48786d7f5d3SJohn Marino 			if (!buildjoin(joinname))
48886d7f5d3SJohn Marino 				continue;
48986d7f5d3SJohn Marino 		}
49086d7f5d3SJohn Marino         }
49186d7f5d3SJohn Marino 	if (!tostdout) {
49286d7f5d3SJohn Marino 	    mode_t m = WORKMODE(RCSstat.st_mode,
49386d7f5d3SJohn Marino 		!  (Expand==VAL_EXPAND  ||  (lockflag<=0 && StrictLocks))
49486d7f5d3SJohn Marino 	    );
49586d7f5d3SJohn Marino 	    time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1;
49686d7f5d3SJohn Marino 	    aflush(neworkptr);
49786d7f5d3SJohn Marino 	    ignoreints();
49886d7f5d3SJohn Marino 	    r = chnamemod(&neworkptr, neworkname, workname, 1, m, t);
49986d7f5d3SJohn Marino 	    keepdirtemp(neworkname);
50086d7f5d3SJohn Marino 	    restoreints();
50186d7f5d3SJohn Marino 	    if (r != 0) {
50286d7f5d3SJohn Marino 		eerror(workname);
50386d7f5d3SJohn Marino 		error("see %s", neworkname);
50486d7f5d3SJohn Marino 		continue;
50586d7f5d3SJohn Marino 	    }
50686d7f5d3SJohn Marino 	    diagnose("done\n");
50786d7f5d3SJohn Marino 	}
50886d7f5d3SJohn Marino 	}
50986d7f5d3SJohn Marino 
51086d7f5d3SJohn Marino 	tempunlink();
51186d7f5d3SJohn Marino 	Ofclose(workstdout);
51286d7f5d3SJohn Marino 	exitmain(exitstatus);
51386d7f5d3SJohn Marino 
51486d7f5d3SJohn Marino }       /* end of main (co) */
51586d7f5d3SJohn Marino 
51686d7f5d3SJohn Marino 	static void
cleanup()51786d7f5d3SJohn Marino cleanup()
51886d7f5d3SJohn Marino {
51986d7f5d3SJohn Marino 	if (nerror) exitstatus = EXIT_FAILURE;
52086d7f5d3SJohn Marino 	Izclose(&finptr);
52186d7f5d3SJohn Marino 	ORCSclose();
52286d7f5d3SJohn Marino #	if !large_memory
52386d7f5d3SJohn Marino 		if (fcopy!=workstdout) Ozclose(&fcopy);
52486d7f5d3SJohn Marino #	endif
52586d7f5d3SJohn Marino 	if (neworkptr!=workstdout) Ozclose(&neworkptr);
52686d7f5d3SJohn Marino 	dirtempunlink();
52786d7f5d3SJohn Marino }
52886d7f5d3SJohn Marino 
52986d7f5d3SJohn Marino #if RCS_lint
53086d7f5d3SJohn Marino #	define exiterr coExit
53186d7f5d3SJohn Marino #endif
53286d7f5d3SJohn Marino 	void
exiterr()53386d7f5d3SJohn Marino exiterr()
53486d7f5d3SJohn Marino {
53586d7f5d3SJohn Marino 	ORCSerror();
53686d7f5d3SJohn Marino 	dirtempunlink();
53786d7f5d3SJohn Marino 	tempunlink();
53886d7f5d3SJohn Marino 	_exit(EXIT_FAILURE);
53986d7f5d3SJohn Marino }
54086d7f5d3SJohn Marino 
54186d7f5d3SJohn Marino 
54286d7f5d3SJohn Marino /*****************************************************************
54386d7f5d3SJohn Marino  * The following routines are auxiliary routines
54486d7f5d3SJohn Marino  *****************************************************************/
54586d7f5d3SJohn Marino 
54686d7f5d3SJohn Marino 	static int
rmworkfile()54786d7f5d3SJohn Marino rmworkfile()
54886d7f5d3SJohn Marino /*
54986d7f5d3SJohn Marino  * Prepare to remove workname, if it exists, and if
55086d7f5d3SJohn Marino  * it is read-only.
55186d7f5d3SJohn Marino  * Otherwise (file writable):
55286d7f5d3SJohn Marino  *   if !quietmode asks the user whether to really delete it (default: fail);
55386d7f5d3SJohn Marino  *   otherwise failure.
55486d7f5d3SJohn Marino  * Returns true if permission is gotten.
55586d7f5d3SJohn Marino  */
55686d7f5d3SJohn Marino {
55786d7f5d3SJohn Marino 	if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
55886d7f5d3SJohn Marino 	    /* File is writable */
55986d7f5d3SJohn Marino 	    if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ",
56086d7f5d3SJohn Marino 			workname,
56186d7f5d3SJohn Marino 			myself(workstat.st_uid) ? "" : ", and you do not own it"
56286d7f5d3SJohn Marino 	    )) {
56386d7f5d3SJohn Marino 		error(!quietflag && ttystdin()
56486d7f5d3SJohn Marino 			? "checkout aborted"
56586d7f5d3SJohn Marino 			: "writable %s exists; checkout aborted", workname);
56686d7f5d3SJohn Marino 		return false;
56786d7f5d3SJohn Marino             }
56886d7f5d3SJohn Marino         }
56986d7f5d3SJohn Marino 	/* Actual unlink is done later by caller. */
57086d7f5d3SJohn Marino 	return true;
57186d7f5d3SJohn Marino }
57286d7f5d3SJohn Marino 
57386d7f5d3SJohn Marino 
57486d7f5d3SJohn Marino 	static int
rmlock(delta)57586d7f5d3SJohn Marino rmlock(delta)
57686d7f5d3SJohn Marino 	struct hshentry const *delta;
57786d7f5d3SJohn Marino /* Function: removes the lock held by caller on delta.
57886d7f5d3SJohn Marino  * Returns -1 if someone else holds the lock,
57986d7f5d3SJohn Marino  * 0 if there is no lock on delta,
58086d7f5d3SJohn Marino  * and 1 if a lock was found and removed.
58186d7f5d3SJohn Marino  */
58286d7f5d3SJohn Marino {       register struct rcslock * next, * trail;
58386d7f5d3SJohn Marino 	char const *num;
58486d7f5d3SJohn Marino 	struct rcslock dummy;
58586d7f5d3SJohn Marino         int whomatch, nummatch;
58686d7f5d3SJohn Marino 
58786d7f5d3SJohn Marino         num=delta->num;
58886d7f5d3SJohn Marino         dummy.nextlock=next=Locks;
58986d7f5d3SJohn Marino         trail = &dummy;
59086d7f5d3SJohn Marino 	while (next) {
59186d7f5d3SJohn Marino 		whomatch = strcmp(getcaller(), next->login);
59286d7f5d3SJohn Marino                 nummatch=strcmp(num,next->delta->num);
59386d7f5d3SJohn Marino                 if ((whomatch==0) && (nummatch==0)) break;
59486d7f5d3SJohn Marino 			/*found a lock on delta by caller*/
59586d7f5d3SJohn Marino                 if ((whomatch!=0)&&(nummatch==0)) {
59686d7f5d3SJohn Marino                     rcserror("revision %s locked by %s; use co -r or rcs -u",
59786d7f5d3SJohn Marino 			num, next->login
59886d7f5d3SJohn Marino 		    );
59986d7f5d3SJohn Marino                     return -1;
60086d7f5d3SJohn Marino                 }
60186d7f5d3SJohn Marino                 trail=next;
60286d7f5d3SJohn Marino                 next=next->nextlock;
60386d7f5d3SJohn Marino         }
60486d7f5d3SJohn Marino 	if (next) {
60586d7f5d3SJohn Marino                 /*found one; delete it */
60686d7f5d3SJohn Marino                 trail->nextlock=next->nextlock;
60786d7f5d3SJohn Marino                 Locks=dummy.nextlock;
60886d7f5d3SJohn Marino 		next->delta->lockedby = 0;
60986d7f5d3SJohn Marino                 return 1; /*success*/
61086d7f5d3SJohn Marino         } else  return 0; /*no lock on delta*/
61186d7f5d3SJohn Marino }
61286d7f5d3SJohn Marino 
61386d7f5d3SJohn Marino 
61486d7f5d3SJohn Marino 
61586d7f5d3SJohn Marino 
61686d7f5d3SJohn Marino /*****************************************************************
61786d7f5d3SJohn Marino  * The rest of the routines are for handling joins
61886d7f5d3SJohn Marino  *****************************************************************/
61986d7f5d3SJohn Marino 
62086d7f5d3SJohn Marino 
62186d7f5d3SJohn Marino 	static char *
addjoin(joinrev)62286d7f5d3SJohn Marino addjoin(joinrev)
62386d7f5d3SJohn Marino 	char *joinrev;
62486d7f5d3SJohn Marino /* Add joinrev's number to joinlist, yielding address of char past joinrev,
62586d7f5d3SJohn Marino  * or 0 if no such revision exists.
62686d7f5d3SJohn Marino  */
62786d7f5d3SJohn Marino {
62886d7f5d3SJohn Marino 	register char *j;
62986d7f5d3SJohn Marino 	register struct hshentry *d;
63086d7f5d3SJohn Marino 	char terminator;
63186d7f5d3SJohn Marino 	struct buf numrev;
63286d7f5d3SJohn Marino 	struct hshentries *joindeltas;
63386d7f5d3SJohn Marino 
63486d7f5d3SJohn Marino 	j = joinrev;
63586d7f5d3SJohn Marino 	for (;;) {
63686d7f5d3SJohn Marino 	    switch (*j++) {
63786d7f5d3SJohn Marino 		default:
63886d7f5d3SJohn Marino 		    continue;
63986d7f5d3SJohn Marino 		case 0:
64086d7f5d3SJohn Marino 		case ' ': case '\t': case '\n':
64186d7f5d3SJohn Marino 		case ':': case ',': case ';':
64286d7f5d3SJohn Marino 		    break;
64386d7f5d3SJohn Marino 	    }
64486d7f5d3SJohn Marino 	    break;
64586d7f5d3SJohn Marino 	}
64686d7f5d3SJohn Marino 	terminator = *--j;
64786d7f5d3SJohn Marino 	*j = 0;
64886d7f5d3SJohn Marino 	bufautobegin(&numrev);
64986d7f5d3SJohn Marino 	d = 0;
65086d7f5d3SJohn Marino 	if (expandsym(joinrev, &numrev))
65186d7f5d3SJohn Marino 	    d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas);
65286d7f5d3SJohn Marino 	bufautoend(&numrev);
65386d7f5d3SJohn Marino 	*j = terminator;
65486d7f5d3SJohn Marino 	if (d) {
65586d7f5d3SJohn Marino 		joinlist[++lastjoin] = d->num;
65686d7f5d3SJohn Marino 		return j;
65786d7f5d3SJohn Marino 	}
65886d7f5d3SJohn Marino 	return 0;
65986d7f5d3SJohn Marino }
66086d7f5d3SJohn Marino 
66186d7f5d3SJohn Marino 	static int
preparejoin(j)66286d7f5d3SJohn Marino preparejoin(j)
66386d7f5d3SJohn Marino 	register char *j;
66486d7f5d3SJohn Marino /* Parse join list J and place pointers to the
66586d7f5d3SJohn Marino  * revision numbers into joinlist.
66686d7f5d3SJohn Marino  */
66786d7f5d3SJohn Marino {
66886d7f5d3SJohn Marino         lastjoin= -1;
66986d7f5d3SJohn Marino         for (;;) {
67086d7f5d3SJohn Marino                 while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
67186d7f5d3SJohn Marino                 if (*j=='\0') break;
67286d7f5d3SJohn Marino                 if (lastjoin>=joinlength-2) {
67386d7f5d3SJohn Marino 		    joinlist =
67486d7f5d3SJohn Marino 			(joinlength *= 2) == 0
67586d7f5d3SJohn Marino 			? tnalloc(char const *, joinlength = 16)
67686d7f5d3SJohn Marino 			: trealloc(char const *, joinlist, joinlength);
67786d7f5d3SJohn Marino                 }
67886d7f5d3SJohn Marino 		if (!(j = addjoin(j))) return false;
67986d7f5d3SJohn Marino                 while ((*j==' ') || (*j=='\t')) j++;
68086d7f5d3SJohn Marino                 if (*j == ':') {
68186d7f5d3SJohn Marino                         j++;
68286d7f5d3SJohn Marino                         while((*j==' ') || (*j=='\t')) j++;
68386d7f5d3SJohn Marino                         if (*j!='\0') {
68486d7f5d3SJohn Marino 				if (!(j = addjoin(j))) return false;
68586d7f5d3SJohn Marino                         } else {
68686d7f5d3SJohn Marino 				rcsfaterror("join pair incomplete");
68786d7f5d3SJohn Marino                         }
68886d7f5d3SJohn Marino                 } else {
68986d7f5d3SJohn Marino                         if (lastjoin==0) { /* first pair */
69086d7f5d3SJohn Marino                                 /* common ancestor missing */
69186d7f5d3SJohn Marino                                 joinlist[1]=joinlist[0];
69286d7f5d3SJohn Marino                                 lastjoin=1;
69386d7f5d3SJohn Marino                                 /*derive common ancestor*/
69486d7f5d3SJohn Marino 				if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
69586d7f5d3SJohn Marino                                        return false;
69686d7f5d3SJohn Marino                         } else {
69786d7f5d3SJohn Marino 				rcsfaterror("join pair incomplete");
69886d7f5d3SJohn Marino                         }
69986d7f5d3SJohn Marino                 }
70086d7f5d3SJohn Marino         }
70186d7f5d3SJohn Marino 	if (lastjoin < 1)
70286d7f5d3SJohn Marino 		rcsfaterror("empty join");
70386d7f5d3SJohn Marino 	return true;
70486d7f5d3SJohn Marino }
70586d7f5d3SJohn Marino 
70686d7f5d3SJohn Marino 
70786d7f5d3SJohn Marino 
70886d7f5d3SJohn Marino 	static char const *
getancestor(r1,r2)70986d7f5d3SJohn Marino getancestor(r1, r2)
71086d7f5d3SJohn Marino 	char const *r1, *r2;
71186d7f5d3SJohn Marino /* Yield the common ancestor of r1 and r2 if successful, 0 otherwise.
71286d7f5d3SJohn Marino  * Work reliably only if r1 and r2 are not branch numbers.
71386d7f5d3SJohn Marino  */
71486d7f5d3SJohn Marino {
71586d7f5d3SJohn Marino 	static struct buf t1, t2;
71686d7f5d3SJohn Marino 
71786d7f5d3SJohn Marino 	int l1, l2, l3;
71886d7f5d3SJohn Marino 	char const *r;
71986d7f5d3SJohn Marino 
72086d7f5d3SJohn Marino 	l1 = countnumflds(r1);
72186d7f5d3SJohn Marino 	l2 = countnumflds(r2);
72286d7f5d3SJohn Marino 	if ((2<l1 || 2<l2)  &&  cmpnum(r1,r2)!=0) {
72386d7f5d3SJohn Marino 	    /* not on main trunk or identical */
72486d7f5d3SJohn Marino 	    l3 = 0;
72586d7f5d3SJohn Marino 	    while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
72686d7f5d3SJohn Marino 		l3 += 2;
72786d7f5d3SJohn Marino 	    /* This will terminate since r1 and r2 are not the same; see above. */
72886d7f5d3SJohn Marino 	    if (l3==0) {
72986d7f5d3SJohn Marino 		/* no common prefix; common ancestor on main trunk */
73086d7f5d3SJohn Marino 		VOID partialno(&t1, r1, l1>2 ? 2 : l1);
73186d7f5d3SJohn Marino 		VOID partialno(&t2, r2, l2>2 ? 2 : l2);
73286d7f5d3SJohn Marino 		r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
73386d7f5d3SJohn Marino 		if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
73486d7f5d3SJohn Marino 			return r;
73586d7f5d3SJohn Marino 	    } else if (cmpnumfld(r1, r2, l3+1)!=0)
73686d7f5d3SJohn Marino 			return partialno(&t1,r1,l3);
73786d7f5d3SJohn Marino 	}
73886d7f5d3SJohn Marino 	rcserror("common ancestor of %s and %s undefined", r1, r2);
73986d7f5d3SJohn Marino 	return 0;
74086d7f5d3SJohn Marino }
74186d7f5d3SJohn Marino 
74286d7f5d3SJohn Marino 
74386d7f5d3SJohn Marino 
74486d7f5d3SJohn Marino 	static int
buildjoin(initialfile)74586d7f5d3SJohn Marino buildjoin(initialfile)
74686d7f5d3SJohn Marino 	char const *initialfile;
74786d7f5d3SJohn Marino /* Function: merge pairs of elements in joinlist into initialfile
74886d7f5d3SJohn Marino  * If workstdout is set, copy result to stdout.
74986d7f5d3SJohn Marino  * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink().
75086d7f5d3SJohn Marino  */
75186d7f5d3SJohn Marino {
75286d7f5d3SJohn Marino 	struct buf commarg;
75386d7f5d3SJohn Marino 	struct buf subs;
75486d7f5d3SJohn Marino 	char const *rev2, *rev3;
75586d7f5d3SJohn Marino         int i;
75686d7f5d3SJohn Marino 	char const *cov[10], *mergev[11];
75786d7f5d3SJohn Marino 	char const **p;
75886d7f5d3SJohn Marino 
75986d7f5d3SJohn Marino 	bufautobegin(&commarg);
76086d7f5d3SJohn Marino 	bufautobegin(&subs);
76186d7f5d3SJohn Marino 	rev2 = maketemp(0);
76286d7f5d3SJohn Marino 	rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
76386d7f5d3SJohn Marino 
76486d7f5d3SJohn Marino 	cov[1] = CO;
76586d7f5d3SJohn Marino 	/* cov[2] setup below */
76686d7f5d3SJohn Marino 	p = &cov[3];
76786d7f5d3SJohn Marino 	if (expandarg) *p++ = expandarg;
76886d7f5d3SJohn Marino 	if (suffixarg) *p++ = suffixarg;
76986d7f5d3SJohn Marino 	if (versionarg) *p++ = versionarg;
77086d7f5d3SJohn Marino 	if (zonearg) *p++ = zonearg;
77186d7f5d3SJohn Marino 	*p++ = quietarg;
77286d7f5d3SJohn Marino 	*p++ = RCSname;
77386d7f5d3SJohn Marino 	*p = 0;
77486d7f5d3SJohn Marino 
77586d7f5d3SJohn Marino 	mergev[1] = MERGE;
77686d7f5d3SJohn Marino 	mergev[2] = mergev[4] = "-L";
77786d7f5d3SJohn Marino 	/* rest of mergev setup below */
77886d7f5d3SJohn Marino 
77986d7f5d3SJohn Marino         i=0;
78086d7f5d3SJohn Marino         while (i<lastjoin) {
78186d7f5d3SJohn Marino                 /*prepare marker for merge*/
78286d7f5d3SJohn Marino                 if (i==0)
78386d7f5d3SJohn Marino 			bufscpy(&subs, targetdelta->num);
78486d7f5d3SJohn Marino 		else {
78586d7f5d3SJohn Marino 			bufscat(&subs, ",");
78686d7f5d3SJohn Marino 			bufscat(&subs, joinlist[i-2]);
78786d7f5d3SJohn Marino 			bufscat(&subs, ":");
78886d7f5d3SJohn Marino 			bufscat(&subs, joinlist[i-1]);
78986d7f5d3SJohn Marino 		}
79086d7f5d3SJohn Marino 		diagnose("revision %s\n",joinlist[i]);
79186d7f5d3SJohn Marino 		bufscpy(&commarg, "-p");
79286d7f5d3SJohn Marino 		bufscat(&commarg, joinlist[i]);
79386d7f5d3SJohn Marino 		cov[2] = commarg.string;
79486d7f5d3SJohn Marino 		if (runv(-1, rev2, cov))
79586d7f5d3SJohn Marino 			goto badmerge;
79686d7f5d3SJohn Marino 		diagnose("revision %s\n",joinlist[i+1]);
79786d7f5d3SJohn Marino 		bufscpy(&commarg, "-p");
79886d7f5d3SJohn Marino 		bufscat(&commarg, joinlist[i+1]);
79986d7f5d3SJohn Marino 		cov[2] = commarg.string;
80086d7f5d3SJohn Marino 		if (runv(-1, rev3, cov))
80186d7f5d3SJohn Marino 			goto badmerge;
80286d7f5d3SJohn Marino 		diagnose("merging...\n");
80386d7f5d3SJohn Marino 		mergev[3] = subs.string;
80486d7f5d3SJohn Marino 		mergev[5] = joinlist[i+1];
80586d7f5d3SJohn Marino 		p = &mergev[6];
80686d7f5d3SJohn Marino 		if (quietflag) *p++ = quietarg;
80786d7f5d3SJohn Marino 		if (lastjoin<=i+2 && workstdout) *p++ = "-p";
80886d7f5d3SJohn Marino 		*p++ = initialfile;
80986d7f5d3SJohn Marino 		*p++ = rev2;
81086d7f5d3SJohn Marino 		*p++ = rev3;
81186d7f5d3SJohn Marino 		*p = 0;
81286d7f5d3SJohn Marino 		switch (runv(-1, (char*)0, mergev)) {
81386d7f5d3SJohn Marino 		    case DIFF_FAILURE: case DIFF_SUCCESS:
81486d7f5d3SJohn Marino 			break;
81586d7f5d3SJohn Marino 		    default:
81686d7f5d3SJohn Marino 			goto badmerge;
81786d7f5d3SJohn Marino 		}
81886d7f5d3SJohn Marino                 i=i+2;
81986d7f5d3SJohn Marino         }
82086d7f5d3SJohn Marino 	bufautoend(&commarg);
82186d7f5d3SJohn Marino 	bufautoend(&subs);
82286d7f5d3SJohn Marino         return true;
82386d7f5d3SJohn Marino 
82486d7f5d3SJohn Marino     badmerge:
82586d7f5d3SJohn Marino 	nerror++;
82686d7f5d3SJohn Marino 	bufautoend(&commarg);
82786d7f5d3SJohn Marino 	bufautoend(&subs);
82886d7f5d3SJohn Marino 	return false;
82986d7f5d3SJohn Marino }
830