rcsmerge.c revision 10
1/*
2 *                       rcsmerge operation
3 */
4/*****************************************************************************
5 *                       join 2 revisions with respect to a third
6 *****************************************************************************
7 */
8
9/* Copyright (C) 1982, 1988, 1989 Walter Tichy
10   Copyright 1990, 1991 by Paul Eggert
11   Distributed under license by the Free Software Foundation, Inc.
12
13This file is part of RCS.
14
15RCS is free software; you can redistribute it and/or modify
16it under the terms of the GNU General Public License as published by
17the Free Software Foundation; either version 2, or (at your option)
18any later version.
19
20RCS is distributed in the hope that it will be useful,
21but WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23GNU General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with RCS; see the file COPYING.  If not, write to
27the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28
29Report problems and direct all questions to:
30
31    rcs-bugs@cs.purdue.edu
32
33*/
34
35
36
37/* $Log: rcsmerge.c,v $
38 * Revision 5.7  1991/11/20  17:58:09  eggert
39 * Don't Iopen(f, "r+"); it's not portable.
40 *
41 * Revision 5.6  1991/08/19  03:13:55  eggert
42 * Add -r$.  Tune.
43 *
44 * Revision 5.5  1991/04/21  11:58:27  eggert
45 * Add -x, RCSINIT, MS-DOS support.
46 *
47 * Revision 5.4  1991/02/25  07:12:43  eggert
48 * Merging a revision to itself is no longer an error.
49 *
50 * Revision 5.3  1990/11/01  05:03:50  eggert
51 * Remove unneeded setid check.
52 *
53 * Revision 5.2  1990/09/04  08:02:28  eggert
54 * Check for I/O error when reading working file.
55 *
56 * Revision 5.1  1990/08/29  07:14:04  eggert
57 * Add -q.  Pass -L options to merge.
58 *
59 * Revision 5.0  1990/08/22  08:13:41  eggert
60 * Propagate merge's exit status.
61 * Remove compile-time limits; use malloc instead.
62 * Make lock and temp files faster and safer.  Ansify and Posixate.  Add -V.
63 * Don't use access().  Tune.
64 *
65 * Revision 4.5  89/05/01  15:13:16  narten
66 * changed copyright header to reflect current distribution rules
67 *
68 * Revision 4.4  88/08/09  19:13:13  eggert
69 * Beware merging into a readonly file.
70 * Beware merging a revision to itself (no change).
71 * Use execv(), not system(); yield exit status like diff(1)'s.
72 *
73 * Revision 4.3  87/10/18  10:38:02  narten
74 * Updating version numbers. Changes relative to version 1.1
75 * actually relative to 4.1
76 *
77 * Revision 1.3  87/09/24  14:00:31  narten
78 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
79 * warnings)
80 *
81 * Revision 1.2  87/03/27  14:22:36  jenkins
82 * Port to suns
83 *
84 * Revision 4.1  83/03/28  11:14:57  wft
85 * Added handling of default branch.
86 *
87 * Revision 3.3  82/12/24  15:29:00  wft
88 * Added call to catchsig().
89 *
90 * Revision 3.2  82/12/10  21:32:02  wft
91 * Replaced getdelta() with gettree(); improved error messages.
92 *
93 * Revision 3.1  82/11/28  19:27:44  wft
94 * Initial revision.
95 *
96 */
97#include "rcsbase.h"
98
99static char const co[] = CO;
100
101mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 eggert Exp $")
102{
103	static char const cmdusage[] =
104		"\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] [-p] [-Vn] file";
105	static char const quietarg[] = "-q";
106
107	register int i;
108	char *a, **newargv;
109	char const *arg[3];
110	char const *rev[2]; /*revision numbers*/
111	char const *expandarg, *versionarg;
112        int tostdout;
113	int status;
114	RILE *workptr;
115	struct buf commarg;
116	struct buf numericrev; /* holds expanded revision number */
117	struct hshentries *gendeltas; /* deltas to be generated */
118        struct hshentry * target;
119
120	bufautobegin(&commarg);
121	bufautobegin(&numericrev);
122	rev[0] = rev[1] = nil;
123	status = 0; /* Keep lint happy.  */
124	tostdout = false;
125	expandarg = versionarg = quietarg; /* i.e. a no-op */
126	suffixes = X_DEFAULT;
127
128	argc = getRCSINIT(argc, argv, &newargv);
129	argv = newargv;
130	while (a = *++argv,  0<--argc && *a++=='-') {
131		switch (*a++) {
132                case 'p':
133                        tostdout=true;
134			goto revno;
135
136		case 'q':
137			quietflag = true;
138		revno:
139			if (!*a)
140				break;
141                        /* falls into -r */
142                case 'r':
143			if (!rev[0])
144				rev[0] = a;
145			else if (!rev[1])
146				rev[1] = a;
147			else
148				faterror("too many revision numbers");
149                        break;
150		case 'x':
151			suffixes = a;
152			break;
153		case 'V':
154			versionarg = *argv;
155			setRCSversion(versionarg);
156			break;
157
158		case 'k':
159			expandarg = *argv;
160			if (0 <= str2expmode(expandarg+2))
161			    break;
162			/* fall into */
163                default:
164			faterror("unknown option: %s%s", *argv, cmdusage);
165                };
166        } /* end of option processing */
167
168	if (argc<1) faterror("no input file%s", cmdusage);
169	if (!rev[0]) faterror("no base revision number given");
170
171        /* now handle all filenames */
172
173	if (0  <  pairfilenames(argc, argv, rcsreadopen, true, false)) {
174
175                if (argc>2 || (argc==2&&argv[1]!=nil))
176                        warn("too many arguments");
177		diagnose("RCS file: %s\n", RCSfilename);
178		if (!(workptr = Iopen(workfilename,
179			FOPEN_R_WORK,
180			(struct stat*)0
181		)))
182			efaterror(workfilename);
183
184                gettree();  /* reads in the delta tree */
185
186                if (Head==nil) faterror("no revisions present");
187
188		if (!*rev[0])
189			rev[0]  =  Dbranch ? Dbranch : Head->num;
190		if (!fexpandsym(rev[0], &numericrev, workptr))
191			goto end;
192		if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end;
193		rev[0] = target->num;
194		if (!rev[1] || !*rev[1])
195			rev[1]  =  Dbranch ? Dbranch : Head->num;
196		if (!fexpandsym(rev[1], &numericrev, workptr))
197			goto end;
198		if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end;
199		rev[1] = target->num;
200
201		if (strcmp(rev[0],rev[1]) == 0) {
202			if (tostdout) {
203				FILE *o;
204#				if text_equals_binary_stdio || text_work_stdio
205				    o = stdout;
206#				else
207				    if (!(o=fdopen(STDOUT_FILENO,FOPEN_W_WORK)))
208					efaterror("stdout");
209#				endif
210				fastcopy(workptr,o);
211				Ofclose(o);
212			}
213			goto end;
214		}
215		Izclose(&workptr);
216
217		for (i=0; i<2; i++) {
218			diagnose("retrieving revision %s\n", rev[i]);
219			bufscpy(&commarg, "-p");
220			bufscat(&commarg, rev[i]);
221			if (run(
222				(char*)0,
223				/* Do not collide with merger.c maketemp().  */
224				arg[i+1] = maketemp(i+3),
225				co, quietarg, commarg.string, expandarg,
226				versionarg, RCSfilename, (char*)0
227			))
228				faterror("co failed");
229		}
230		diagnose("Merging differences between %s and %s into %s%s\n",
231			 rev[0], rev[1], workfilename,
232                         tostdout?"; result to stdout":"");
233
234		arg[0] = rev[0] = workfilename;
235		status = merge(tostdout, rev, arg);
236        }
237
238end:
239	Izclose(&workptr);
240	tempunlink();
241	exitmain(nerror ? DIFF_TROUBLE : status);
242}
243
244#if lint
245#	define exiterr rmergeExit
246#endif
247	exiting void
248exiterr()
249{
250	tempunlink();
251	_exit(DIFF_TROUBLE);
252}
253