xref: /openbsd-src/usr.bin/cvs/diff.c (revision bc6ad1fb3d6d0dbae09eeabb8e135ee03fb0bb47)
1*bc6ad1fbStobias /*	$OpenBSD: diff.c,v 1.128 2008/02/09 12:27:31 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;
3437fdff3fStobias 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 ...]",
4437fdff3fStobias 	"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 ...",
5537fdff3fStobias 	"cfD:k:lr:RuV:",
5616cfc147Sjoris 	NULL,
573ad3fb45Sjoris 	cvs_diff
58e4276007Sjfb };
59e4276007Sjfb 
603ad3fb45Sjoris int
613ad3fb45Sjoris cvs_diff(int argc, char **argv)
6208f90673Sjfb {
63e9d83458Stobias 	int ch, flags;
643ad3fb45Sjoris 	char *arg = ".";
653ad3fb45Sjoris 	struct cvs_recursion cr;
6608f90673Sjfb 
671890abdaSjoris 	flags = CR_RECURSE_DIRS;
68*bc6ad1fbStobias 	strlcpy(diffargs, cvs_cmdop == CVS_OP_DIFF ? "diff" : "rdiff",
69*bc6ad1fbStobias 	    sizeof(diffargs));
70dc6a6879Sjfb 
71e9d83458Stobias 	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_DIFF ?
72e9d83458Stobias 	    cvs_cmd_diff.cmd_opts : cvs_cmd_rdiff.cmd_opts)) != -1) {
7308f90673Sjfb 		switch (ch) {
7408f90673Sjfb 		case 'c':
75f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
76f9b67873Sniallo 			diff_format = D_CONTEXT;
7708f90673Sjfb 			break;
78fd660bf2Stobias 		case 'f':
79fd660bf2Stobias 			force_head = 1;
80fd660bf2Stobias 			break;
81fd660bf2Stobias 		case 'i':
82fd660bf2Stobias 			strlcat(diffargs, " -i", sizeof(diffargs));
83fd660bf2Stobias 			diff_iflag = 1;
84fd660bf2Stobias 			break;
8537fdff3fStobias 		case 'k':
8637fdff3fStobias 			koptstr = optarg;
8737fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
8837fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
8937fdff3fStobias 				cvs_log(LP_ERR,
9037fdff3fStobias 				    "invalid RCS keyword expension mode");
9137fdff3fStobias 				fatal("%s", cvs_cmd_add.cmd_synopsis);
9237fdff3fStobias 			}
9337fdff3fStobias 			break;
941890abdaSjoris 		case 'l':
951890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
961890abdaSjoris 			break;
97394180a4Sjfb 		case 'n':
98394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
99f9b67873Sniallo 			diff_format = D_RCSDIFF;
100394180a4Sjfb 			break;
10167caf486Sjoris 		case 'N':
1022d6f447eSjoris 			Nflag = 1;
10367caf486Sjoris 			break;
104261cb0daSjoris 		case 'p':
105261cb0daSjoris 			strlcat(diffargs, " -p", sizeof(diffargs));
106261cb0daSjoris 			diff_pflag = 1;
107261cb0daSjoris 			break;
108bcf22459Stobias 		case 'R':
109bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
110bcf22459Stobias 			break;
11108f90673Sjfb 		case 'r':
1124230e8b3Sjoris 			if (rev1 == NULL) {
1134230e8b3Sjoris 				rev1 = optarg;
1144230e8b3Sjoris 			} else if (rev2 == NULL) {
1154230e8b3Sjoris 				rev2 = optarg;
11616cfc147Sjoris 			} else {
1173ad3fb45Sjoris 				fatal("no more than 2 revisions/dates can"
11808f90673Sjfb 				    " be specified");
11908f90673Sjfb 			}
12008f90673Sjfb 			break;
12108f90673Sjfb 		case 'u':
122f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
123f9b67873Sniallo 			diff_format = D_UNIFIED;
12408f90673Sjfb 			break;
12537fdff3fStobias 		case 'V':
12637fdff3fStobias 			fatal("the -V option is obsolete "
12737fdff3fStobias 			    "and should not be used");
12808f90673Sjfb 		default:
1293ad3fb45Sjoris 			fatal("%s", cvs_cmd_diff.cmd_synopsis);
13008f90673Sjfb 		}
13108f90673Sjfb 	}
13208f90673Sjfb 
1333ad3fb45Sjoris 	argc -= optind;
1343ad3fb45Sjoris 	argv += optind;
135dc6a6879Sjfb 
1363ad3fb45Sjoris 	cr.enterdir = NULL;
1373ad3fb45Sjoris 	cr.leavedir = NULL;
1389fac60a5Sjoris 
139fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_RDIFF) {
140fd660bf2Stobias 		if (rev1 == NULL)
141fd660bf2Stobias 			fatal("must specify at least one revision/date!");
142fd660bf2Stobias 
143fd660bf2Stobias 		if (!diff_format) {
144fd660bf2Stobias 			strlcat(diffargs, " -c", sizeof(diffargs));
145fd660bf2Stobias 			diff_format = D_CONTEXT;
146fd660bf2Stobias 		}
147fd660bf2Stobias 
148fd660bf2Stobias 		flags |= CR_REPO;
149fd660bf2Stobias 	}
150fd660bf2Stobias 
1519fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
15280f6ca9bSjoris 		cvs_client_connect_to_server();
1539fac60a5Sjoris 		cr.fileproc = cvs_client_sendfile;
1549fac60a5Sjoris 
1559fac60a5Sjoris 		if (!(flags & CR_RECURSE_DIRS))
1569fac60a5Sjoris 			cvs_client_send_request("Argument -l");
1579fac60a5Sjoris 
15837fdff3fStobias 		if (kflag)
15937fdff3fStobias 			cvs_client_send_request("Argument -k%s", koptstr);
16037fdff3fStobias 
1619fac60a5Sjoris 		switch (diff_format) {
1629fac60a5Sjoris 		case D_CONTEXT:
1639fac60a5Sjoris 			cvs_client_send_request("Argument -c");
1649fac60a5Sjoris 			break;
1659fac60a5Sjoris 		case D_RCSDIFF:
1669fac60a5Sjoris 			cvs_client_send_request("Argument -n");
1679fac60a5Sjoris 			break;
1689fac60a5Sjoris 		case D_UNIFIED:
1699fac60a5Sjoris 			cvs_client_send_request("Argument -u");
1709fac60a5Sjoris 			break;
1719fac60a5Sjoris 		default:
1729fac60a5Sjoris 			break;
1739fac60a5Sjoris 		}
1749fac60a5Sjoris 
1759fac60a5Sjoris 		if (Nflag == 1)
1769fac60a5Sjoris 			cvs_client_send_request("Argument -N");
1779fac60a5Sjoris 
1789fac60a5Sjoris 		if (diff_pflag == 1)
1799fac60a5Sjoris 			cvs_client_send_request("Argument -p");
1809fac60a5Sjoris 
1819fac60a5Sjoris 		if (rev1 != NULL)
1829fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev1);
1839fac60a5Sjoris 		if (rev2 != NULL)
1849fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev2);
1859fac60a5Sjoris 	} else {
186fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_RDIFF &&
187fd660bf2Stobias 		    chdir(current_cvsroot->cr_dir) == -1)
188fd660bf2Stobias 			fatal("cvs_diff: %s", strerror(errno));
189fd660bf2Stobias 
190bc5d89feSjoris 		cr.fileproc = cvs_diff_local;
1919fac60a5Sjoris 	}
1929fac60a5Sjoris 
1931890abdaSjoris 	cr.flags = flags;
194dc6a6879Sjfb 
1954230e8b3Sjoris 	diff_rev1 = diff_rev2 = NULL;
1964230e8b3Sjoris 
197fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF ||
198fd660bf2Stobias 	    current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
1993ad3fb45Sjoris 		if (argc > 0)
2003ad3fb45Sjoris 			cvs_file_run(argc, argv, &cr);
201d990145dSray 		else
2023ad3fb45Sjoris 			cvs_file_run(1, &arg, &cr);
203fd660bf2Stobias 	}
204e4276007Sjfb 
2059fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
2069fac60a5Sjoris 		cvs_client_send_files(argv, argc);
2079fac60a5Sjoris 		cvs_client_senddir(".");
208fd660bf2Stobias 
209fd660bf2Stobias 		cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ?
210fd660bf2Stobias 		    "rdiff" : "diff");
211fd660bf2Stobias 
2129fac60a5Sjoris 		cvs_client_get_responses();
2139fac60a5Sjoris 	}
2149fac60a5Sjoris 
215e4276007Sjfb 	return (0);
216e4276007Sjfb }
217e4276007Sjfb 
21801af718aSjoris void
2193ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf)
220f9b67873Sniallo {
2213ad3fb45Sjoris 	RCSNUM *r1;
222741443cbSjoris 	BUF *b1;
2233ad3fb45Sjoris 	struct stat st;
2243ad3fb45Sjoris 	struct timeval tv[2], tv2[2];
2250a7da307Sxsa 	char rbuf[CVS_REV_BUFSZ], *p1, *p2;
226d71a0c9bSxsa 
227d71a0c9bSxsa 	r1 = NULL;
228741443cbSjoris 	b1 = NULL;
229f9b67873Sniallo 
2303ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path);
2313ad3fb45Sjoris 
2323ad3fb45Sjoris 	if (cf->file_type == CVS_DIR) {
2333ad3fb45Sjoris 		if (verbosity > 1)
2343ad3fb45Sjoris 			cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path);
2353ad3fb45Sjoris 		return;
2363ad3fb45Sjoris 	}
2373ad3fb45Sjoris 
23851ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
2393ad3fb45Sjoris 
2403ad3fb45Sjoris 	if (cf->file_status == FILE_LOST) {
2413ad3fb45Sjoris 		cvs_log(LP_ERR, "cannot find file %s", cf->file_path);
2423ad3fb45Sjoris 		return;
2433ad3fb45Sjoris 	} else if (cf->file_status == FILE_UNKNOWN) {
2443ad3fb45Sjoris 		cvs_log(LP_ERR, "I know nothing about %s", cf->file_path);
2453ad3fb45Sjoris 		return;
2462d6f447eSjoris 	} else if (cf->file_status == FILE_ADDED && Nflag == 0) {
24767caf486Sjoris 		cvs_log(LP_ERR, "%s is a new entry, no comparison available",
24867caf486Sjoris 		    cf->file_path);
2493ad3fb45Sjoris 		return;
2502d6f447eSjoris 	} else if (cf->file_status == FILE_REMOVED && Nflag == 0) {
2512d6f447eSjoris 		cvs_log(LP_ERR, "%s was removed, no comparison available",
2522d6f447eSjoris 		    cf->file_path);
2532d6f447eSjoris 		return;
2548fadf3feSjoris 	} else if (cf->file_status == FILE_UPTODATE && rev1 == NULL &&
2558fadf3feSjoris 	     rev2 == NULL) {
25667caf486Sjoris 		return;
25767caf486Sjoris 	}
2583ad3fb45Sjoris 
25937fdff3fStobias 	if (kflag)
26037fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, kflag);
26137fdff3fStobias 
2624230e8b3Sjoris 	if (rev1 != NULL)
263fd660bf2Stobias 		if ((diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs)) ==
264fd660bf2Stobias 		    NULL) {
265fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF) {
266fd660bf2Stobias 				cvs_log(LP_ERR, "tag %s is not in file %s",
267fd660bf2Stobias 				    rev1, cf->file_path);
26851ef6581Sjoris 				return;
269fd660bf2Stobias 			}
270fd660bf2Stobias  			if (force_head) {
271fd660bf2Stobias 				/* -f is not allowed for unknown symbols */
272fd660bf2Stobias 				diff_rev1 = rcsnum_parse(rev1);
273fd660bf2Stobias 				if (diff_rev1 == NULL)
274fd660bf2Stobias 					fatal("no such tag %s", rev1);
275fd660bf2Stobias 				rcsnum_free(diff_rev1);
276fd660bf2Stobias 
277fd660bf2Stobias 				diff_rev1 = rcsnum_alloc();
278fd660bf2Stobias 				rcsnum_cpy(cf->file_rcs->rf_head, diff_rev1, 0);
279fd660bf2Stobias 			}
280fd660bf2Stobias 		}
28151ef6581Sjoris 
2824230e8b3Sjoris 	if (rev2 != NULL)
2830c3e16f9Stobias 		if ((diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs)) ==
2840c3e16f9Stobias 		    NULL) {
285fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF) {
2860c3e16f9Stobias 				rcsnum_free(diff_rev1);
287fd660bf2Stobias 				cvs_log(LP_ERR, "tag %s is not in file %s",
288fd660bf2Stobias 				    rev2, cf->file_path);
28951ef6581Sjoris 				return;
2900c3e16f9Stobias 			}
291fd660bf2Stobias  			if (force_head) {
292fd660bf2Stobias 				/* -f is not allowed for unknown symbols */
293fd660bf2Stobias 				diff_rev2 = rcsnum_parse(rev2);
294fd660bf2Stobias 				if (diff_rev2 == NULL)
295fd660bf2Stobias 					fatal("no such tag %s", rev2);
296fd660bf2Stobias 				rcsnum_free(diff_rev2);
297fd660bf2Stobias 
298fd660bf2Stobias 				diff_rev2 = rcsnum_alloc();
299fd660bf2Stobias 				rcsnum_cpy(cf->file_rcs->rf_head, diff_rev2, 0);
300fd660bf2Stobias 			}
301fd660bf2Stobias 		}
302fd660bf2Stobias 
303fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL && diff_rev2 == NULL)
304fd660bf2Stobias 		return;
3054230e8b3Sjoris 
3063ad3fb45Sjoris 	diff_file = cf->file_path;
307fd660bf2Stobias 
308fd660bf2Stobias 	cvs_printf("Index: %s\n", cf->file_path);
309fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF)
310fd660bf2Stobias 		cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, cf->file_rpath);
3113ad3fb45Sjoris 
312741443cbSjoris 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
313741443cbSjoris 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
314741443cbSjoris 
31567caf486Sjoris 	if (cf->file_status != FILE_ADDED) {
3163ad3fb45Sjoris 		if (diff_rev1 != NULL)
3173ad3fb45Sjoris 			r1 = diff_rev1;
318fd660bf2Stobias 		else if (cf->file_ent != NULL)
3193ad3fb45Sjoris 			r1 = cf->file_ent->ce_rev;
320fd660bf2Stobias 		else
321fd660bf2Stobias 			r1 = NULL;
3223ad3fb45Sjoris 
3233ad3fb45Sjoris 		diff_rev1 = r1;
324fd660bf2Stobias 
325fd660bf2Stobias 		if (diff_rev1 != NULL) {
326fd660bf2Stobias 			(void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
3273ad3fb45Sjoris 
3283ad3fb45Sjoris 			tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1);
3293ad3fb45Sjoris 			tv[0].tv_usec = 0;
3303ad3fb45Sjoris 			tv[1] = tv[0];
331741443cbSjoris 
332fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_DIFF)
333fd660bf2Stobias 				cvs_printf("retrieving revision %s\n", rbuf);
334741443cbSjoris 			rcs_rev_write_stmp(cf->file_rcs, r1, p1, 0);
335fd660bf2Stobias 			if (utimes(p1, tv) == -1)
336fd660bf2Stobias 				fatal("cvs_diff_local: utimes failed");
337fd660bf2Stobias 		}
33867caf486Sjoris 	}
3393ad3fb45Sjoris 
3402d6f447eSjoris 	if (diff_rev2 != NULL && cf->file_status != FILE_ADDED &&
3412d6f447eSjoris 	    cf->file_status != FILE_REMOVED) {
342fd660bf2Stobias 		(void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf));
3433ad3fb45Sjoris 
3443ad3fb45Sjoris 		tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2);
3453ad3fb45Sjoris 		tv2[0].tv_usec = 0;
3463ad3fb45Sjoris 		tv2[1] = tv2[0];
347741443cbSjoris 
348fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_DIFF)
349fd660bf2Stobias 			cvs_printf("retrieving revision %s\n", rbuf);
350741443cbSjoris 		rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0);
351fd660bf2Stobias 		if (utimes(p2, tv2) == -1)
352fd660bf2Stobias 			fatal("cvs_diff_local: utimes failed");
3532d6f447eSjoris 	} else if (cf->file_status != FILE_REMOVED) {
354fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_RDIFF || (cvs_server_active == 1 &&
355fd660bf2Stobias 		    cf->file_status != FILE_MODIFIED)) {
356fd660bf2Stobias 			if (diff_rev2 != NULL) {
357fd660bf2Stobias 				tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs,
358fd660bf2Stobias 				    cf->file_rcsrev);
359fd660bf2Stobias 				tv2[0].tv_usec = 0;
360fd660bf2Stobias 				tv2[1] = tv2[0];
361fd660bf2Stobias 
36251ef6581Sjoris 				rcs_rev_write_stmp(cf->file_rcs,
36351ef6581Sjoris 				    cf->file_rcsrev, p2, 0);
364fd660bf2Stobias 				if (utimes(p2, tv2) == -1)
365fd660bf2Stobias 					fatal("cvs_diff_local: utimes failed");
366fd660bf2Stobias 			}
36751ef6581Sjoris 		} else {
3683ad3fb45Sjoris 			if (fstat(cf->fd, &st) == -1)
3693ad3fb45Sjoris 				fatal("fstat failed %s", strerror(errno));
370741443cbSjoris 			if ((b1 = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL)
3713ad3fb45Sjoris 				fatal("failed to load %s", cf->file_path);
3723ad3fb45Sjoris 
3733ad3fb45Sjoris 			tv2[0].tv_sec = st.st_mtime;
3743ad3fb45Sjoris 			tv2[0].tv_usec = 0;
3753ad3fb45Sjoris 			tv2[1] = tv2[0];
376741443cbSjoris 
377741443cbSjoris 			cvs_buf_write_stmp(b1, p2, tv2);
378741443cbSjoris 			cvs_buf_free(b1);
3793ad3fb45Sjoris 		}
38051ef6581Sjoris 	}
3813ad3fb45Sjoris 
382fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF) {
3833ad3fb45Sjoris 		cvs_printf("%s", diffargs);
3843ad3fb45Sjoris 
38567caf486Sjoris 		if (cf->file_status != FILE_ADDED) {
386fd660bf2Stobias 			(void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
3873ad3fb45Sjoris 			cvs_printf(" -r%s", rbuf);
3883ad3fb45Sjoris 
3893ad3fb45Sjoris 			if (diff_rev2 != NULL) {
390fd660bf2Stobias 				(void)rcsnum_tostr(diff_rev2, rbuf,
391fd660bf2Stobias 				    sizeof(rbuf));
3923ad3fb45Sjoris 				cvs_printf(" -r%s", rbuf);
3933ad3fb45Sjoris 			}
39467caf486Sjoris 		}
3953ad3fb45Sjoris 
396fd660bf2Stobias 		if (diff_rev2 == NULL)
397fd660bf2Stobias 			cvs_printf(" %s\n", cf->file_name);
398fd660bf2Stobias 	} else {
399fd660bf2Stobias 		cvs_printf("diff ");
400fd660bf2Stobias 		switch (diff_format) {
401fd660bf2Stobias 		case D_CONTEXT:
402fd660bf2Stobias 			cvs_printf("-c ");
403fd660bf2Stobias 			break;
404fd660bf2Stobias 		case D_RCSDIFF:
405fd660bf2Stobias 			cvs_printf("-n ");
406fd660bf2Stobias 			break;
407fd660bf2Stobias 		case D_UNIFIED:
408fd660bf2Stobias 			cvs_printf("-u ");
409fd660bf2Stobias 			break;
410fd660bf2Stobias 		default:
411fd660bf2Stobias 			break;
412fd660bf2Stobias 		}
413fd660bf2Stobias 		if (diff_rev1 == NULL) {
414fd660bf2Stobias 			cvs_printf("%s ", CVS_PATH_DEVNULL);
415fd660bf2Stobias 		} else {
416fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
417fd660bf2Stobias 			cvs_printf("%s:%s ", cf->file_path, rbuf);
418fd660bf2Stobias 		}
4193ad3fb45Sjoris 
420fd660bf2Stobias 		if (diff_rev2 == NULL) {
421fd660bf2Stobias 			cvs_printf("%s:removed\n", cf->file_path);
422fd660bf2Stobias 		} else {
423fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 :
424fd660bf2Stobias 			    cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
425fd660bf2Stobias 			cvs_printf("%s:%s\n", cf->file_path, rbuf);
426fd660bf2Stobias 		}
427fd660bf2Stobias 	}
428fd660bf2Stobias 
429fd660bf2Stobias 	if (cf->file_status == FILE_ADDED ||
430fd660bf2Stobias 	    (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL)) {
431741443cbSjoris 		xfree(p1);
432d71a0c9bSxsa 		(void)xasprintf(&p1, "%s", CVS_PATH_DEVNULL);
433fd660bf2Stobias 	} else if (cf->file_status == FILE_REMOVED ||
434fd660bf2Stobias 	    (cvs_cmdop == CVS_OP_RDIFF && diff_rev2 == NULL)) {
435741443cbSjoris 		xfree(p2);
436d71a0c9bSxsa 		(void)xasprintf(&p2, "%s", CVS_PATH_DEVNULL);
437741443cbSjoris 	}
4383ad3fb45Sjoris 
439d71a0c9bSxsa 	if (cvs_diffreg(p1, p2, NULL) == D_ERROR)
440d71a0c9bSxsa 		fatal("cvs_diff_local: failed to get RCS patch");
441d71a0c9bSxsa 
4423ad3fb45Sjoris 	cvs_worklist_run(&temp_files, cvs_worklist_unlink);
44367caf486Sjoris 
444d71a0c9bSxsa 	if (p1 != NULL)
445d71a0c9bSxsa 		xfree(p1);
446d71a0c9bSxsa 	if (p2 != NULL)
447d71a0c9bSxsa 		xfree(p2);
448d71a0c9bSxsa 
449fd660bf2Stobias 	if (diff_rev1 != NULL && diff_rev1 != cf->file_rcs->rf_head &&
450fd660bf2Stobias 	    (cf->file_ent != NULL && diff_rev1 != cf->file_ent->ce_rev))
451261cb0daSjoris 		rcsnum_free(diff_rev1);
452fd660bf2Stobias 	if (diff_rev2 != NULL)
453261cb0daSjoris 		rcsnum_free(diff_rev2);
454261cb0daSjoris 
45567caf486Sjoris 	diff_rev1 = diff_rev2 = NULL;
456f9b67873Sniallo }
457