xref: /dragonfly/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c (revision 36a3d1d6)
1 /* Compare RCS revisions.  */
2 
3 /* Copyright 1982, 1988, 1989 Walter Tichy
4    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Distributed under license by the Free Software Foundation, Inc.
6 
7 This file is part of RCS.
8 
9 RCS is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RCS; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 Report problems and direct all questions to:
25 
26     rcs-bugs@cs.purdue.edu
27 
28 */
29 
30 /*
31  * $FreeBSD: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.8 1999/08/27 23:36:55 peter Exp $
32  * $DragonFly: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.2 2003/06/17 04:25:48 dillon Exp $
33  *
34  * Revision 5.19  1995/06/16 06:19:24  eggert
35  * Update FSF address.
36  *
37  * Revision 5.18  1995/06/01 16:23:43  eggert
38  * (main): Pass "--binary" if -kb and if --binary makes a difference.
39  * Don't treat + options specially.
40  *
41  * Revision 5.17  1994/03/17 14:05:48  eggert
42  * Specify subprocess input via file descriptor, not file name.  Remove lint.
43  *
44  * Revision 5.16  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.15  1993/11/03 17:42:27  eggert
48  * Add -z.  Ignore -T.  Pass -Vn to `co'.  Add Name keyword.
49  * Put revision numbers in -c output.  Improve quality of diagnostics.
50  *
51  * Revision 5.14  1992/07/28  16:12:44  eggert
52  * Add -V.  Use co -M for better dates with traditional diff -c.
53  *
54  * Revision 5.13  1992/02/17  23:02:23  eggert
55  * Output more readable context diff headers.
56  * Suppress needless checkout and comparison of identical revisions.
57  *
58  * Revision 5.12  1992/01/24  18:44:19  eggert
59  * Add GNU diff 1.15.2's new options.  lint -> RCS_lint
60  *
61  * Revision 5.11  1992/01/06  02:42:34  eggert
62  * Update usage string.
63  *
64  * Revision 5.10  1991/10/07  17:32:46  eggert
65  * Remove lint.
66  *
67  * Revision 5.9  1991/08/19  03:13:55  eggert
68  * Add RCSINIT, -r$.  Tune.
69  *
70  * Revision 5.8  1991/04/21  11:58:21  eggert
71  * Add -x, RCSINIT, MS-DOS support.
72  *
73  * Revision 5.7  1990/12/13  06:54:07  eggert
74  * GNU diff 1.15 has -u.
75  *
76  * Revision 5.6  1990/11/01  05:03:39  eggert
77  * Remove unneeded setid check.
78  *
79  * Revision 5.5  1990/10/04  06:30:19  eggert
80  * Accumulate exit status across files.
81  *
82  * Revision 5.4  1990/09/27  01:31:43  eggert
83  * Yield 1, not EXIT_FAILURE, when diffs are found.
84  *
85  * Revision 5.3  1990/09/11  02:41:11  eggert
86  * Simplify -kkvl test.
87  *
88  * Revision 5.2  1990/09/04  17:07:19  eggert
89  * Diff's argv was too small by 1.
90  *
91  * Revision 5.1  1990/08/29  07:13:55  eggert
92  * Add -kkvl.
93  *
94  * Revision 5.0  1990/08/22  08:12:46  eggert
95  * Add -k, -V.  Don't use access().  Add setuid support.
96  * Remove compile-time limits; use malloc instead.
97  * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options.
98  * Add GNU diff's flags.  Make lock and temp files faster and safer.
99  * Ansify and Posixate.
100  *
101  * Revision 4.6  89/05/01  15:12:27  narten
102  * changed copyright header to reflect current distribution rules
103  *
104  * Revision 4.5  88/08/09  19:12:41  eggert
105  * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R.
106  *
107  * Revision 4.4  87/12/18  11:37:46  narten
108  * changes Jay Lepreau made in the 4.3 BSD version, to add support for
109  * "-i", "-w", and "-t" flags and to permit flags to be bundled together,
110  * merged in.
111  *
112  * Revision 4.3  87/10/18  10:31:42  narten
113  * Updating version numbers. Changes relative to 1.1 actually
114  * relative to 4.1
115  *
116  * Revision 1.3  87/09/24  13:59:21  narten
117  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
118  * warnings)
119  *
120  * Revision 1.2  87/03/27  14:22:15  jenkins
121  * Port to suns
122  *
123  * Revision 4.1  83/05/03  22:13:19  wft
124  * Added default branch, option -q, exit status like diff.
125  * Added fterror() to replace faterror().
126  *
127  * Revision 3.6  83/01/15  17:52:40  wft
128  * Expanded mainprogram to handle multiple RCS files.
129  *
130  * Revision 3.5  83/01/06  09:33:45  wft
131  * Fixed passing of -c (context) option to diff.
132  *
133  * Revision 3.4  82/12/24  15:28:38  wft
134  * Added call to catchsig().
135  *
136  * Revision 3.3  82/12/10  16:08:17  wft
137  * Corrected checking of return code from diff; improved error msgs.
138  *
139  * Revision 3.2  82/12/04  13:20:09  wft
140  * replaced getdelta() with gettree(). Changed diagnostics.
141  *
142  * Revision 3.1  82/11/28  19:25:04  wft
143  * Initial revision.
144  *
145  */
146 #include "rcsbase.h"
147 
148 #if DIFF_L
149 static char const *setup_label P((struct buf*,char const*,char const[datesize]));
150 #endif
151 static void cleanup P((void));
152 
153 static int exitstatus;
154 static RILE *workptr;
155 static struct stat workstat;
156 
157 mainProg(rcsdiffId, "rcsdiff", "$DragonFly: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.2 2003/06/17 04:25:48 dillon Exp $")
158 {
159     static char const cmdusage[] =
160 	    "\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [diff options] file ...";
161 
162     int  revnums;                 /* counter for revision numbers given */
163     char const *rev1, *rev2;	/* revision numbers from command line */
164     char const *xrev1, *xrev2;	/* expanded revision numbers */
165     char const *expandarg, *lexpandarg, *suffixarg, *versionarg, *zonearg;
166 #if DIFF_L
167     static struct buf labelbuf[2];
168     int file_labels;
169     char const **diff_label1, **diff_label2;
170     char date2[datesize];
171 #endif
172     char const *cov[10 + !DIFF_L];
173     char const **diffv, **diffp, **diffpend;	/* argv for subsidiary diff */
174     char const **pp, *p, *diffvstr;
175     struct buf commarg;
176     struct buf numericrev;	/* expanded revision number */
177     struct hshentries *gendeltas;	/* deltas to be generated */
178     struct hshentry * target;
179     char *a, *dcp, **newargv;
180     int no_diff_means_no_output;
181     register c;
182 
183     exitstatus = DIFF_SUCCESS;
184 
185     bufautobegin(&commarg);
186     bufautobegin(&numericrev);
187     revnums = 0;
188     rev1 = rev2 = xrev2 = 0;
189 #if DIFF_L
190     file_labels = 0;
191 #endif
192     expandarg = suffixarg = versionarg = zonearg = 0;
193     no_diff_means_no_output = true;
194     suffixes = X_DEFAULT;
195 
196     /*
197     * Room for runv extra + args [+ --binary] [+ 2 labels]
198     * + 1 file + 1 trailing null.
199     */
200     diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2);
201     diffp = diffv + 1;
202     *diffp++ = DIFF;
203 
204     argc = getRCSINIT(argc, argv, &newargv);
205     argv = newargv;
206     while (a = *++argv,  0<--argc && *a++=='-') {
207 	dcp = a;
208 	while ((c = *a++)) switch (c) {
209 	    case 'r':
210 		    switch (++revnums) {
211 			case 1: rev1=a; break;
212 			case 2: rev2=a; break;
213 			default: error("too many revision numbers");
214 		    }
215 		    goto option_handled;
216 	    case '-': case 'D':
217 		    no_diff_means_no_output = false;
218 		    /* fall into */
219 	    case 'C': case 'F': case 'I': case 'L': case 'W':
220 #if DIFF_L
221 		    if (c == 'L'  &&  file_labels++ == 2)
222 			faterror("too many -L options");
223 #endif
224 		    *dcp++ = c;
225 		    if (*a)
226 			do *dcp++ = *a++;
227 			while (*a);
228 		    else {
229 			if (!--argc)
230 			    faterror("-%c needs following argument%s",
231 				    c, cmdusage
232 			    );
233 			*diffp++ = *argv++;
234 		    }
235 		    break;
236 	    case 'y':
237 		    no_diff_means_no_output = false;
238 		    /* fall into */
239 	    case 'B': case 'H':
240 	    case '0': case '1': case '2': case '3': case '4':
241 	    case '5': case '6': case '7': case '8': case '9':
242 	    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
243 	    case 'h': case 'i': case 'n': case 'p':
244 	    case 't': case 'u': case 'w':
245 		    *dcp++ = c;
246 		    break;
247 	    case 'q':
248 		    quietflag=true;
249 		    break;
250 	    case 'x':
251 		    suffixarg = *argv;
252 		    suffixes = *argv + 2;
253 		    goto option_handled;
254 	    case 'z':
255 		    zonearg = *argv;
256 		    zone_set(*argv + 2);
257 		    goto option_handled;
258 	    case 'T':
259 		    /* Ignore -T, so that RCSINIT can contain -T.  */
260 		    if (*a)
261 			    goto unknown;
262 		    break;
263 	    case 'V':
264 		    versionarg = *argv;
265 		    setRCSversion(versionarg);
266 		    goto option_handled;
267 	    case 'k':
268 		    expandarg = *argv;
269 		    if (0 <= str2expmode(expandarg+2))
270 			goto option_handled;
271 		    /* fall into */
272 	    default:
273 	    unknown:
274 		    error("unknown option: %s%s", *argv, cmdusage);
275 	    };
276       option_handled:
277 	if (dcp != *argv+1) {
278 	    *dcp = 0;
279 	    *diffp++ = *argv;
280 	}
281     } /* end of option processing */
282 
283     for (pp = diffv+2, c = 0;  pp<diffp;  )
284 	    c += strlen(*pp++) + 1;
285     diffvstr = a = tnalloc(char, c + 1);
286     for (pp = diffv+2;  pp<diffp;  ) {
287 	    p = *pp++;
288 	    *a++ = ' ';
289 	    while ((*a = *p++))
290 		    a++;
291     }
292     *a = 0;
293 
294 #if DIFF_L
295     diff_label1 = diff_label2 = 0;
296     if (file_labels < 2) {
297 	    if (!file_labels)
298 		    diff_label1 = diffp++;
299 	    diff_label2 = diffp++;
300     }
301 #endif
302     diffpend = diffp;
303 
304     cov[1] = CO;
305     cov[2] = "-q";
306 #   if !DIFF_L
307 	cov[3] = "-M";
308 #   endif
309 
310     /* Now handle all pathnames.  */
311     if (nerror)
312 	cleanup();
313     else if (argc < 1)
314 	faterror("no input file%s", cmdusage);
315     else
316 	for (;  0 < argc;  cleanup(), ++argv, --argc) {
317 	    ffree();
318 
319 	    if (pairnames(argc, argv, rcsreadopen, true, false)  <=  0)
320 		    continue;
321 	    diagnose("===================================================================\nRCS file: %s\n",RCSname);
322 	    if (!rev2) {
323 		/* Make sure work file is readable, and get its status.  */
324 		if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) {
325 		    eerror(workname);
326 		    continue;
327 		}
328 	    }
329 
330 
331 	    gettree(); /* reads in the delta tree */
332 
333 	    if (!Head) {
334 		    rcserror("no revisions present");
335 		    continue;
336 	    }
337 	    if (revnums==0  ||  !*rev1)
338 		    rev1  =  Dbranch ? Dbranch : Head->num;
339 
340 	    if (!fexpandsym(rev1, &numericrev, workptr)) continue;
341 	    if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
342 	    xrev1=target->num;
343 #if DIFF_L
344 	    if (diff_label1)
345 		*diff_label1 = setup_label(&labelbuf[0], target->num, target->date);
346 #endif
347 
348 	    lexpandarg = expandarg;
349 	    if (revnums==2) {
350 		    if (!fexpandsym(
351 			    *rev2 ? rev2  : Dbranch ? Dbranch  : Head->num,
352 			    &numericrev,
353 			    workptr
354 		    ))
355 			continue;
356 		    if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
357 		    xrev2=target->num;
358 		    if (no_diff_means_no_output  &&  xrev1 == xrev2)
359 			continue;
360 	    } else if (
361 			target->lockedby
362 		&&	!lexpandarg
363 		&&	Expand == KEYVAL_EXPAND
364 		&&	WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
365 	    )
366 		    lexpandarg = "-kkvl";
367 	    Izclose(&workptr);
368 #if DIFF_L
369 	    if (diff_label2)
370 		if (revnums == 2)
371 		    *diff_label2 = setup_label(&labelbuf[1], target->num, target->date);
372 		else {
373 		    time2date(workstat.st_mtime, date2);
374 		    *diff_label2 = setup_label(&labelbuf[1], (char*)0, date2);
375 		}
376 #endif
377 
378 	    diagnose("retrieving revision %s\n", xrev1);
379 	    bufscpy(&commarg, "-p");
380 	    bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */
381 
382 	    pp = &cov[3 + !DIFF_L];
383 	    *pp++ = commarg.string;
384 	    if (lexpandarg) *pp++ = lexpandarg;
385 	    if (suffixarg) *pp++ = suffixarg;
386 	    if (versionarg) *pp++ = versionarg;
387 	    if (zonearg) *pp++ = zonearg;
388 	    *pp++ = RCSname;
389 	    *pp = 0;
390 
391 	    diffp = diffpend;
392 #	    if OPEN_O_BINARY
393 		    if (Expand == BINARY_EXPAND)
394 			    *diffp++ = "--binary";
395 #	    endif
396 	    diffp[0] = maketemp(0);
397 	    if (runv(-1, diffp[0], cov)) {
398 		    rcserror("co failed");
399 		    continue;
400 	    }
401 	    if (!rev2) {
402 		    diffp[1] = workname;
403 		    if (*workname == '-') {
404 			char *dp = ftnalloc(char, strlen(workname)+3);
405 			diffp[1] = dp;
406 			*dp++ = '.';
407 			*dp++ = SLASH;
408 			VOID strcpy(dp, workname);
409 		    }
410 	    } else {
411 		    diagnose("retrieving revision %s\n",xrev2);
412 		    bufscpy(&commarg, "-p");
413 		    bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */
414 		    cov[3 + !DIFF_L] = commarg.string;
415 		    diffp[1] = maketemp(1);
416 		    if (runv(-1, diffp[1], cov)) {
417 			    rcserror("co failed");
418 			    continue;
419 		    }
420 	    }
421 	    if (!rev2)
422 		    diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname);
423 	    else
424 		    diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2);
425 
426 	    diffp[2] = 0;
427 	    switch (runv(-1, (char*)0, diffv)) {
428 		    case DIFF_SUCCESS:
429 			    break;
430 		    case DIFF_FAILURE:
431 			    if (exitstatus == DIFF_SUCCESS)
432 				    exitstatus = DIFF_FAILURE;
433 			    break;
434 		    default:
435 			    workerror("diff failed");
436 	    }
437 	}
438 
439     tempunlink();
440     exitmain(exitstatus);
441 }
442 
443     static void
444 cleanup()
445 {
446     if (nerror) exitstatus = DIFF_TROUBLE;
447     Izclose(&finptr);
448     Izclose(&workptr);
449 }
450 
451 #if RCS_lint
452 #	define exiterr rdiffExit
453 #endif
454     void
455 exiterr()
456 {
457     tempunlink();
458     _exit(DIFF_TROUBLE);
459 }
460 
461 #if DIFF_L
462 	static char const *
463 setup_label(b, num, date)
464 	struct buf *b;
465 	char const *num;
466 	char const date[datesize];
467 {
468 	char *p;
469 	char datestr[datesize + zonelenmax];
470 	VOID date2str(date, datestr);
471 	bufalloc(b,
472 		strlen(workname)
473 		+ sizeof datestr + 4
474 		+ (num ? strlen(num) : 0)
475 	);
476 	p = b->string;
477 	if (num)
478 		VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num);
479 	else
480 		VOID sprintf(p, "-L%s\t%s", workname, datestr);
481 	return p;
482 }
483 #endif
484