xref: /openbsd-src/usr.bin/cvs/diff.c (revision 37fdff3fa60fdd1baf6bc6082eee5bd7ac3b858c)
1*37fdff3fStobias /*	$OpenBSD: diff.c,v 1.126 2008/02/04 15:07:33 tobias Exp $	*/
208f90673Sjfb /*
33ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
408f90673Sjfb  *
53ad3fb45Sjoris  * Permission to use, copy, modify, and distribute this software for any
63ad3fb45Sjoris  * purpose with or without fee is hereby granted, provided that the above
73ad3fb45Sjoris  * copyright notice and this permission notice appear in all copies.
808f90673Sjfb  *
93ad3fb45Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103ad3fb45Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113ad3fb45Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123ad3fb45Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133ad3fb45Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143ad3fb45Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153ad3fb45Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1608f90673Sjfb  */
1708f90673Sjfb 
181f8531bdSotto #include <sys/stat.h>
19fd660bf2Stobias #include <sys/time.h>
201f8531bdSotto 
211f8531bdSotto #include <errno.h>
22fd660bf2Stobias #include <stdlib.h>
231f8531bdSotto #include <string.h>
241f8531bdSotto #include <unistd.h>
2508f90673Sjfb 
2608f90673Sjfb #include "cvs.h"
27af5bb824Sniallo #include "diff.h"
289fac60a5Sjoris #include "remote.h"
2908f90673Sjfb 
303ad3fb45Sjoris void	cvs_diff_local(struct cvs_file *);
31ec6ed1abSjoris 
324230e8b3Sjoris static int	 Nflag = 0;
33fd660bf2Stobias static int	 force_head = 0;
34*37fdff3fStobias static char	*koptstr;
354230e8b3Sjoris static char	*rev1 = NULL;
364230e8b3Sjoris static char	*rev2 = NULL;
3767caf486Sjoris 
38e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
39f331ff59Stobias 	CVS_OP_DIFF, CVS_USE_WDIR, "diff",
40e4276007Sjfb 	{ "di", "dif" },
41e4276007Sjfb 	"Show differences between revisions",
42bcf22459Stobias 	"[-cilNnpRu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
43c9150269Sxsa 	"[-k mode] [file ...]",
44*37fdff3fStobias 	"cfD:ik:lNnpr:Ru",
45fd660bf2Stobias 	NULL,
46fd660bf2Stobias 	cvs_diff
47fd660bf2Stobias };
48fd660bf2Stobias 
49fd660bf2Stobias struct cvs_cmd cvs_cmd_rdiff = {
50fd660bf2Stobias 	CVS_OP_RDIFF, 0, "rdiff",
51fd660bf2Stobias 	{ "patch", "pa" },
52fd660bf2Stobias 	"Show differences between revisions",
53fd660bf2Stobias 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev\n"
54fd660bf2Stobias 	"[-D date2 | -r rev2] [-k mode] module ...",
55*37fdff3fStobias 	"cfD:k:lr:RuV:",
5616cfc147Sjoris 	NULL,
573ad3fb45Sjoris 	cvs_diff
58e4276007Sjfb };
59e4276007Sjfb 
603ad3fb45Sjoris int
613ad3fb45Sjoris cvs_diff(int argc, char **argv)
6208f90673Sjfb {
6316cfc147Sjoris 	int ch;
643ad3fb45Sjoris 	char *arg = ".";
651890abdaSjoris 	int flags;
663ad3fb45Sjoris 	struct cvs_recursion cr;
6708f90673Sjfb 
681890abdaSjoris 	flags = CR_RECURSE_DIRS;
69dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
70dc6a6879Sjfb 
713ad3fb45Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_diff.cmd_opts)) != -1) {
7208f90673Sjfb 		switch (ch) {
7308f90673Sjfb 		case 'c':
74f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
75f9b67873Sniallo 			diff_format = D_CONTEXT;
7608f90673Sjfb 			break;
77fd660bf2Stobias 		case 'f':
78fd660bf2Stobias 			force_head = 1;
79fd660bf2Stobias 			break;
80fd660bf2Stobias 		case 'i':
81fd660bf2Stobias 			strlcat(diffargs, " -i", sizeof(diffargs));
82fd660bf2Stobias 			diff_iflag = 1;
83fd660bf2Stobias 			break;
84*37fdff3fStobias 		case 'k':
85*37fdff3fStobias 			koptstr = optarg;
86*37fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
87*37fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
88*37fdff3fStobias 				cvs_log(LP_ERR,
89*37fdff3fStobias 				    "invalid RCS keyword expension mode");
90*37fdff3fStobias 				fatal("%s", cvs_cmd_add.cmd_synopsis);
91*37fdff3fStobias 			}
92*37fdff3fStobias 			break;
931890abdaSjoris 		case 'l':
941890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
951890abdaSjoris 			break;
96394180a4Sjfb 		case 'n':
97394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
98f9b67873Sniallo 			diff_format = D_RCSDIFF;
99394180a4Sjfb 			break;
10067caf486Sjoris 		case 'N':
1012d6f447eSjoris 			Nflag = 1;
10267caf486Sjoris 			break;
103261cb0daSjoris 		case 'p':
104261cb0daSjoris 			strlcat(diffargs, " -p", sizeof(diffargs));
105261cb0daSjoris 			diff_pflag = 1;
106261cb0daSjoris 			break;
107bcf22459Stobias 		case 'R':
108bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
109bcf22459Stobias 			break;
11008f90673Sjfb 		case 'r':
1114230e8b3Sjoris 			if (rev1 == NULL) {
1124230e8b3Sjoris 				rev1 = optarg;
1134230e8b3Sjoris 			} else if (rev2 == NULL) {
1144230e8b3Sjoris 				rev2 = optarg;
11516cfc147Sjoris 			} else {
1163ad3fb45Sjoris 				fatal("no more than 2 revisions/dates can"
11708f90673Sjfb 				    " be specified");
11808f90673Sjfb 			}
11908f90673Sjfb 			break;
12008f90673Sjfb 		case 'u':
121f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
122f9b67873Sniallo 			diff_format = D_UNIFIED;
12308f90673Sjfb 			break;
124*37fdff3fStobias 		case 'V':
125*37fdff3fStobias 			fatal("the -V option is obsolete "
126*37fdff3fStobias 			    "and should not be used");
12708f90673Sjfb 		default:
1283ad3fb45Sjoris 			fatal("%s", cvs_cmd_diff.cmd_synopsis);
12908f90673Sjfb 		}
13008f90673Sjfb 	}
13108f90673Sjfb 
1323ad3fb45Sjoris 	argc -= optind;
1333ad3fb45Sjoris 	argv += optind;
134dc6a6879Sjfb 
1353ad3fb45Sjoris 	cr.enterdir = NULL;
1363ad3fb45Sjoris 	cr.leavedir = NULL;
1379fac60a5Sjoris 
138fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_RDIFF) {
139fd660bf2Stobias 		if (rev1 == NULL)
140fd660bf2Stobias 			fatal("must specify at least one revision/date!");
141fd660bf2Stobias 
142fd660bf2Stobias 		if (!diff_format) {
143fd660bf2Stobias 			strlcat(diffargs, " -c", sizeof(diffargs));
144fd660bf2Stobias 			diff_format = D_CONTEXT;
145fd660bf2Stobias 		}
146fd660bf2Stobias 
147fd660bf2Stobias 		flags |= CR_REPO;
148fd660bf2Stobias 	}
149fd660bf2Stobias 
1509fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
15180f6ca9bSjoris 		cvs_client_connect_to_server();
1529fac60a5Sjoris 		cr.fileproc = cvs_client_sendfile;
1539fac60a5Sjoris 
1549fac60a5Sjoris 		if (!(flags & CR_RECURSE_DIRS))
1559fac60a5Sjoris 			cvs_client_send_request("Argument -l");
1569fac60a5Sjoris 
157*37fdff3fStobias 		if (kflag)
158*37fdff3fStobias 			cvs_client_send_request("Argument -k%s", koptstr);
159*37fdff3fStobias 
1609fac60a5Sjoris 		switch (diff_format) {
1619fac60a5Sjoris 		case D_CONTEXT:
1629fac60a5Sjoris 			cvs_client_send_request("Argument -c");
1639fac60a5Sjoris 			break;
1649fac60a5Sjoris 		case D_RCSDIFF:
1659fac60a5Sjoris 			cvs_client_send_request("Argument -n");
1669fac60a5Sjoris 			break;
1679fac60a5Sjoris 		case D_UNIFIED:
1689fac60a5Sjoris 			cvs_client_send_request("Argument -u");
1699fac60a5Sjoris 			break;
1709fac60a5Sjoris 		default:
1719fac60a5Sjoris 			break;
1729fac60a5Sjoris 		}
1739fac60a5Sjoris 
1749fac60a5Sjoris 		if (Nflag == 1)
1759fac60a5Sjoris 			cvs_client_send_request("Argument -N");
1769fac60a5Sjoris 
1779fac60a5Sjoris 		if (diff_pflag == 1)
1789fac60a5Sjoris 			cvs_client_send_request("Argument -p");
1799fac60a5Sjoris 
1809fac60a5Sjoris 		if (rev1 != NULL)
1819fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev1);
1829fac60a5Sjoris 		if (rev2 != NULL)
1839fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev2);
1849fac60a5Sjoris 	} else {
185fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_RDIFF &&
186fd660bf2Stobias 		    chdir(current_cvsroot->cr_dir) == -1)
187fd660bf2Stobias 			fatal("cvs_diff: %s", strerror(errno));
188fd660bf2Stobias 
189bc5d89feSjoris 		cr.fileproc = cvs_diff_local;
1909fac60a5Sjoris 	}
1919fac60a5Sjoris 
1921890abdaSjoris 	cr.flags = flags;
193dc6a6879Sjfb 
1944230e8b3Sjoris 	diff_rev1 = diff_rev2 = NULL;
1954230e8b3Sjoris 
196fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF ||
197fd660bf2Stobias 	    current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
1983ad3fb45Sjoris 		if (argc > 0)
1993ad3fb45Sjoris 			cvs_file_run(argc, argv, &cr);
200d990145dSray 		else
2013ad3fb45Sjoris 			cvs_file_run(1, &arg, &cr);
202fd660bf2Stobias 	}
203e4276007Sjfb 
2049fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
2059fac60a5Sjoris 		cvs_client_send_files(argv, argc);
2069fac60a5Sjoris 		cvs_client_senddir(".");
207fd660bf2Stobias 
208fd660bf2Stobias 		cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ?
209fd660bf2Stobias 		    "rdiff" : "diff");
210fd660bf2Stobias 
2119fac60a5Sjoris 		cvs_client_get_responses();
2129fac60a5Sjoris 	}
2139fac60a5Sjoris 
214e4276007Sjfb 	return (0);
215e4276007Sjfb }
216e4276007Sjfb 
21701af718aSjoris void
2183ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf)
219f9b67873Sniallo {
2203ad3fb45Sjoris 	RCSNUM *r1;
221741443cbSjoris 	BUF *b1;
2223ad3fb45Sjoris 	struct stat st;
2233ad3fb45Sjoris 	struct timeval tv[2], tv2[2];
2240a7da307Sxsa 	char rbuf[CVS_REV_BUFSZ], *p1, *p2;
225d71a0c9bSxsa 
226d71a0c9bSxsa 	r1 = NULL;
227741443cbSjoris 	b1 = NULL;
228f9b67873Sniallo 
2293ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path);
2303ad3fb45Sjoris 
2313ad3fb45Sjoris 	if (cf->file_type == CVS_DIR) {
2323ad3fb45Sjoris 		if (verbosity > 1)
2333ad3fb45Sjoris 			cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path);
2343ad3fb45Sjoris 		return;
2353ad3fb45Sjoris 	}
2363ad3fb45Sjoris 
23751ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
2383ad3fb45Sjoris 
2393ad3fb45Sjoris 	if (cf->file_status == FILE_LOST) {
2403ad3fb45Sjoris 		cvs_log(LP_ERR, "cannot find file %s", cf->file_path);
2413ad3fb45Sjoris 		return;
2423ad3fb45Sjoris 	} else if (cf->file_status == FILE_UNKNOWN) {
2433ad3fb45Sjoris 		cvs_log(LP_ERR, "I know nothing about %s", cf->file_path);
2443ad3fb45Sjoris 		return;
2452d6f447eSjoris 	} else if (cf->file_status == FILE_ADDED && Nflag == 0) {
24667caf486Sjoris 		cvs_log(LP_ERR, "%s is a new entry, no comparison available",
24767caf486Sjoris 		    cf->file_path);
2483ad3fb45Sjoris 		return;
2492d6f447eSjoris 	} else if (cf->file_status == FILE_REMOVED && Nflag == 0) {
2502d6f447eSjoris 		cvs_log(LP_ERR, "%s was removed, no comparison available",
2512d6f447eSjoris 		    cf->file_path);
2522d6f447eSjoris 		return;
2538fadf3feSjoris 	} else if (cf->file_status == FILE_UPTODATE && rev1 == NULL &&
2548fadf3feSjoris 	     rev2 == NULL) {
25567caf486Sjoris 		return;
25667caf486Sjoris 	}
2573ad3fb45Sjoris 
258*37fdff3fStobias 	if (kflag)
259*37fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, kflag);
260*37fdff3fStobias 
2614230e8b3Sjoris 	if (rev1 != NULL)
262fd660bf2Stobias 		if ((diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs)) ==
263fd660bf2Stobias 		    NULL) {
264fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF) {
265fd660bf2Stobias 				cvs_log(LP_ERR, "tag %s is not in file %s",
266fd660bf2Stobias 				    rev1, cf->file_path);
26751ef6581Sjoris 				return;
268fd660bf2Stobias 			}
269fd660bf2Stobias  			if (force_head) {
270fd660bf2Stobias 				/* -f is not allowed for unknown symbols */
271fd660bf2Stobias 				diff_rev1 = rcsnum_parse(rev1);
272fd660bf2Stobias 				if (diff_rev1 == NULL)
273fd660bf2Stobias 					fatal("no such tag %s", rev1);
274fd660bf2Stobias 				rcsnum_free(diff_rev1);
275fd660bf2Stobias 
276fd660bf2Stobias 				diff_rev1 = rcsnum_alloc();
277fd660bf2Stobias 				rcsnum_cpy(cf->file_rcs->rf_head, diff_rev1, 0);
278fd660bf2Stobias 			}
279fd660bf2Stobias 		}
28051ef6581Sjoris 
2814230e8b3Sjoris 	if (rev2 != NULL)
2820c3e16f9Stobias 		if ((diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs)) ==
2830c3e16f9Stobias 		    NULL) {
284fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF) {
2850c3e16f9Stobias 				rcsnum_free(diff_rev1);
286fd660bf2Stobias 				cvs_log(LP_ERR, "tag %s is not in file %s",
287fd660bf2Stobias 				    rev2, cf->file_path);
28851ef6581Sjoris 				return;
2890c3e16f9Stobias 			}
290fd660bf2Stobias  			if (force_head) {
291fd660bf2Stobias 				/* -f is not allowed for unknown symbols */
292fd660bf2Stobias 				diff_rev2 = rcsnum_parse(rev2);
293fd660bf2Stobias 				if (diff_rev2 == NULL)
294fd660bf2Stobias 					fatal("no such tag %s", rev2);
295fd660bf2Stobias 				rcsnum_free(diff_rev2);
296fd660bf2Stobias 
297fd660bf2Stobias 				diff_rev2 = rcsnum_alloc();
298fd660bf2Stobias 				rcsnum_cpy(cf->file_rcs->rf_head, diff_rev2, 0);
299fd660bf2Stobias 			}
300fd660bf2Stobias 		}
301fd660bf2Stobias 
302fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL && diff_rev2 == NULL)
303fd660bf2Stobias 		return;
3044230e8b3Sjoris 
3053ad3fb45Sjoris 	diff_file = cf->file_path;
306fd660bf2Stobias 
307fd660bf2Stobias 	cvs_printf("Index: %s\n", cf->file_path);
308fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF)
309fd660bf2Stobias 		cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, cf->file_rpath);
3103ad3fb45Sjoris 
311741443cbSjoris 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
312741443cbSjoris 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
313741443cbSjoris 
31467caf486Sjoris 	if (cf->file_status != FILE_ADDED) {
3153ad3fb45Sjoris 		if (diff_rev1 != NULL)
3163ad3fb45Sjoris 			r1 = diff_rev1;
317fd660bf2Stobias 		else if (cf->file_ent != NULL)
3183ad3fb45Sjoris 			r1 = cf->file_ent->ce_rev;
319fd660bf2Stobias 		else
320fd660bf2Stobias 			r1 = NULL;
3213ad3fb45Sjoris 
3223ad3fb45Sjoris 		diff_rev1 = r1;
323fd660bf2Stobias 
324fd660bf2Stobias 		if (diff_rev1 != NULL) {
325fd660bf2Stobias 			(void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
3263ad3fb45Sjoris 
3273ad3fb45Sjoris 			tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1);
3283ad3fb45Sjoris 			tv[0].tv_usec = 0;
3293ad3fb45Sjoris 			tv[1] = tv[0];
330741443cbSjoris 
331fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF)
332fd660bf2Stobias 				cvs_printf("retrieving revision %s\n", rbuf);
333741443cbSjoris 			rcs_rev_write_stmp(cf->file_rcs, r1, p1, 0);
334fd660bf2Stobias 			if (utimes(p1, tv) == -1)
335fd660bf2Stobias 				fatal("cvs_diff_local: utimes failed");
336fd660bf2Stobias 		}
33767caf486Sjoris 	}
3383ad3fb45Sjoris 
3392d6f447eSjoris 	if (diff_rev2 != NULL && cf->file_status != FILE_ADDED &&
3402d6f447eSjoris 	    cf->file_status != FILE_REMOVED) {
341fd660bf2Stobias 		(void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf));
3423ad3fb45Sjoris 
3433ad3fb45Sjoris 		tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2);
3443ad3fb45Sjoris 		tv2[0].tv_usec = 0;
3453ad3fb45Sjoris 		tv2[1] = tv2[0];
346741443cbSjoris 
347fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_DIFF)
348fd660bf2Stobias 			cvs_printf("retrieving revision %s\n", rbuf);
349741443cbSjoris 		rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0);
350fd660bf2Stobias 		if (utimes(p2, tv2) == -1)
351fd660bf2Stobias 			fatal("cvs_diff_local: utimes failed");
3522d6f447eSjoris 	} else if (cf->file_status != FILE_REMOVED) {
353fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_RDIFF || (cvs_server_active == 1 &&
354fd660bf2Stobias 		    cf->file_status != FILE_MODIFIED)) {
355fd660bf2Stobias 			if (diff_rev2 != NULL) {
356fd660bf2Stobias 				tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs,
357fd660bf2Stobias 				    cf->file_rcsrev);
358fd660bf2Stobias 				tv2[0].tv_usec = 0;
359fd660bf2Stobias 				tv2[1] = tv2[0];
360fd660bf2Stobias 
36151ef6581Sjoris 				rcs_rev_write_stmp(cf->file_rcs,
36251ef6581Sjoris 				    cf->file_rcsrev, p2, 0);
363fd660bf2Stobias 				if (utimes(p2, tv2) == -1)
364fd660bf2Stobias 					fatal("cvs_diff_local: utimes failed");
365fd660bf2Stobias 			}
36651ef6581Sjoris 		} else {
3673ad3fb45Sjoris 			if (fstat(cf->fd, &st) == -1)
3683ad3fb45Sjoris 				fatal("fstat failed %s", strerror(errno));
369741443cbSjoris 			if ((b1 = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL)
3703ad3fb45Sjoris 				fatal("failed to load %s", cf->file_path);
3713ad3fb45Sjoris 
3723ad3fb45Sjoris 			tv2[0].tv_sec = st.st_mtime;
3733ad3fb45Sjoris 			tv2[0].tv_usec = 0;
3743ad3fb45Sjoris 			tv2[1] = tv2[0];
375741443cbSjoris 
376741443cbSjoris 			cvs_buf_write_stmp(b1, p2, tv2);
377741443cbSjoris 			cvs_buf_free(b1);
3783ad3fb45Sjoris 		}
37951ef6581Sjoris 	}
3803ad3fb45Sjoris 
381fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF) {
3823ad3fb45Sjoris 		cvs_printf("%s", diffargs);
3833ad3fb45Sjoris 
38467caf486Sjoris 		if (cf->file_status != FILE_ADDED) {
385fd660bf2Stobias 			(void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
3863ad3fb45Sjoris 			cvs_printf(" -r%s", rbuf);
3873ad3fb45Sjoris 
3883ad3fb45Sjoris 			if (diff_rev2 != NULL) {
389fd660bf2Stobias 				(void)rcsnum_tostr(diff_rev2, rbuf,
390fd660bf2Stobias 				    sizeof(rbuf));
3913ad3fb45Sjoris 				cvs_printf(" -r%s", rbuf);
3923ad3fb45Sjoris 			}
39367caf486Sjoris 		}
3943ad3fb45Sjoris 
395fd660bf2Stobias 		if (diff_rev2 == NULL)
396fd660bf2Stobias 			cvs_printf(" %s\n", cf->file_name);
397fd660bf2Stobias 	} else {
398fd660bf2Stobias 		cvs_printf("diff ");
399fd660bf2Stobias 		switch (diff_format) {
400fd660bf2Stobias 		case D_CONTEXT:
401fd660bf2Stobias 			cvs_printf("-c ");
402fd660bf2Stobias 			break;
403fd660bf2Stobias 		case D_RCSDIFF:
404fd660bf2Stobias 			cvs_printf("-n ");
405fd660bf2Stobias 			break;
406fd660bf2Stobias 		case D_UNIFIED:
407fd660bf2Stobias 			cvs_printf("-u ");
408fd660bf2Stobias 			break;
409fd660bf2Stobias 		default:
410fd660bf2Stobias 			break;
411fd660bf2Stobias 		}
412fd660bf2Stobias 		if (diff_rev1 == NULL) {
413fd660bf2Stobias 			cvs_printf("%s ", CVS_PATH_DEVNULL);
414fd660bf2Stobias 		} else {
415fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
416fd660bf2Stobias 			cvs_printf("%s:%s ", cf->file_path, rbuf);
417fd660bf2Stobias 		}
4183ad3fb45Sjoris 
419fd660bf2Stobias 		if (diff_rev2 == NULL) {
420fd660bf2Stobias 			cvs_printf("%s:removed\n", cf->file_path);
421fd660bf2Stobias 		} else {
422fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 :
423fd660bf2Stobias 			    cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
424fd660bf2Stobias 			cvs_printf("%s:%s\n", cf->file_path, rbuf);
425fd660bf2Stobias 		}
426fd660bf2Stobias 	}
427fd660bf2Stobias 
428fd660bf2Stobias 	if (cf->file_status == FILE_ADDED ||
429fd660bf2Stobias 	    (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL)) {
430741443cbSjoris 		xfree(p1);
431d71a0c9bSxsa 		(void)xasprintf(&p1, "%s", CVS_PATH_DEVNULL);
432fd660bf2Stobias 	} else if (cf->file_status == FILE_REMOVED ||
433fd660bf2Stobias 	    (cvs_cmdop == CVS_OP_RDIFF && diff_rev2 == NULL)) {
434741443cbSjoris 		xfree(p2);
435d71a0c9bSxsa 		(void)xasprintf(&p2, "%s", CVS_PATH_DEVNULL);
436741443cbSjoris 	}
4373ad3fb45Sjoris 
438d71a0c9bSxsa 	if (cvs_diffreg(p1, p2, NULL) == D_ERROR)
439d71a0c9bSxsa 		fatal("cvs_diff_local: failed to get RCS patch");
440d71a0c9bSxsa 
4413ad3fb45Sjoris 	cvs_worklist_run(&temp_files, cvs_worklist_unlink);
44267caf486Sjoris 
443d71a0c9bSxsa 	if (p1 != NULL)
444d71a0c9bSxsa 		xfree(p1);
445d71a0c9bSxsa 	if (p2 != NULL)
446d71a0c9bSxsa 		xfree(p2);
447d71a0c9bSxsa 
448fd660bf2Stobias 	if (diff_rev1 != NULL && diff_rev1 != cf->file_rcs->rf_head &&
449fd660bf2Stobias 	    (cf->file_ent != NULL && diff_rev1 != cf->file_ent->ce_rev))
450261cb0daSjoris 		rcsnum_free(diff_rev1);
451fd660bf2Stobias 	if (diff_rev2 != NULL)
452261cb0daSjoris 		rcsnum_free(diff_rev2);
453261cb0daSjoris 
45467caf486Sjoris 	diff_rev1 = diff_rev2 = NULL;
455f9b67873Sniallo }
456