xref: /openbsd-src/usr.bin/cvs/diff.c (revision e91b8a587210b3807223f1ef58df690d9849fdb2)
1*e91b8a58Sjoris /*	$OpenBSD: diff.c,v 1.149 2009/04/02 21:13:50 joris Exp $	*/
208f90673Sjfb /*
3fd687d09Stobias  * Copyright (c) 2008 Tobias Stoeckmann <tobias@openbsd.org>
43ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
508f90673Sjfb  *
63ad3fb45Sjoris  * Permission to use, copy, modify, and distribute this software for any
73ad3fb45Sjoris  * purpose with or without fee is hereby granted, provided that the above
83ad3fb45Sjoris  * copyright notice and this permission notice appear in all copies.
908f90673Sjfb  *
103ad3fb45Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113ad3fb45Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123ad3fb45Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133ad3fb45Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143ad3fb45Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153ad3fb45Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163ad3fb45Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1708f90673Sjfb  */
1808f90673Sjfb 
191f8531bdSotto #include <sys/stat.h>
20fd660bf2Stobias #include <sys/time.h>
211f8531bdSotto 
221f8531bdSotto #include <errno.h>
232e0d696aSjoris #include <fcntl.h>
24fd660bf2Stobias #include <stdlib.h>
251f8531bdSotto #include <string.h>
266534056aStobias #include <time.h>
271f8531bdSotto #include <unistd.h>
2808f90673Sjfb 
2908f90673Sjfb #include "cvs.h"
30af5bb824Sniallo #include "diff.h"
319fac60a5Sjoris #include "remote.h"
3208f90673Sjfb 
333ad3fb45Sjoris void	cvs_diff_local(struct cvs_file *);
34ec6ed1abSjoris 
354230e8b3Sjoris static int	 Nflag = 0;
36fd660bf2Stobias static int	 force_head = 0;
3737fdff3fStobias static char	*koptstr;
384230e8b3Sjoris static char	*rev1 = NULL;
394230e8b3Sjoris static char	*rev2 = NULL;
40be756b91Stobias static time_t	 date1 = -1;
41be756b91Stobias static time_t	 date2 = -1;
42909bf3e0Stobias static char	*dateflag1 = NULL;
43909bf3e0Stobias static char	*dateflag2 = NULL;
4467caf486Sjoris 
45e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
46f331ff59Stobias 	CVS_OP_DIFF, CVS_USE_WDIR, "diff",
47e4276007Sjfb 	{ "di", "dif" },
48e4276007Sjfb 	"Show differences between revisions",
49bcf22459Stobias 	"[-cilNnpRu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
50c9150269Sxsa 	"[-k mode] [file ...]",
5137fdff3fStobias 	"cfD:ik:lNnpr:Ru",
52fd660bf2Stobias 	NULL,
53fd660bf2Stobias 	cvs_diff
54fd660bf2Stobias };
55fd660bf2Stobias 
56fd660bf2Stobias struct cvs_cmd cvs_cmd_rdiff = {
57fd660bf2Stobias 	CVS_OP_RDIFF, 0, "rdiff",
58fd660bf2Stobias 	{ "patch", "pa" },
59fd660bf2Stobias 	"Show differences between revisions",
60fd660bf2Stobias 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev\n"
61fd660bf2Stobias 	"[-D date2 | -r rev2] [-k mode] module ...",
6237fdff3fStobias 	"cfD:k:lr:RuV:",
6316cfc147Sjoris 	NULL,
643ad3fb45Sjoris 	cvs_diff
65e4276007Sjfb };
66e4276007Sjfb 
673ad3fb45Sjoris int
683ad3fb45Sjoris cvs_diff(int argc, char **argv)
6908f90673Sjfb {
70e9d83458Stobias 	int ch, flags;
713ad3fb45Sjoris 	char *arg = ".";
723ad3fb45Sjoris 	struct cvs_recursion cr;
7308f90673Sjfb 
741890abdaSjoris 	flags = CR_RECURSE_DIRS;
75bc6ad1fbStobias 	strlcpy(diffargs, cvs_cmdop == CVS_OP_DIFF ? "diff" : "rdiff",
76bc6ad1fbStobias 	    sizeof(diffargs));
77dc6a6879Sjfb 
78e9d83458Stobias 	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_DIFF ?
79e9d83458Stobias 	    cvs_cmd_diff.cmd_opts : cvs_cmd_rdiff.cmd_opts)) != -1) {
8008f90673Sjfb 		switch (ch) {
8108f90673Sjfb 		case 'c':
82f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
83f9b67873Sniallo 			diff_format = D_CONTEXT;
8408f90673Sjfb 			break;
85be756b91Stobias 		case 'D':
86be756b91Stobias 			if (date1 == -1 && rev1 == NULL) {
87be756b91Stobias 				date1 = cvs_date_parse(optarg);
88909bf3e0Stobias 				dateflag1 = optarg;
89be756b91Stobias 			} else if (date2 == -1 && rev2 == NULL) {
90be756b91Stobias 				date2 = cvs_date_parse(optarg);
91909bf3e0Stobias 				dateflag2 = optarg;
92be756b91Stobias 			} else {
93be756b91Stobias 				fatal("no more than 2 revisions/dates can"
94be756b91Stobias 				    " be specified");
95be756b91Stobias 			}
96be756b91Stobias 			break;
97fd660bf2Stobias 		case 'f':
98fd660bf2Stobias 			force_head = 1;
99fd660bf2Stobias 			break;
100fd660bf2Stobias 		case 'i':
101fd660bf2Stobias 			strlcat(diffargs, " -i", sizeof(diffargs));
102fd660bf2Stobias 			diff_iflag = 1;
103fd660bf2Stobias 			break;
10437fdff3fStobias 		case 'k':
10537fdff3fStobias 			koptstr = optarg;
10637fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
10737fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
10837fdff3fStobias 				cvs_log(LP_ERR,
1090bc1d395Stobias 				    "invalid RCS keyword expansion mode");
110aeaaf4a6Stobias 				fatal("%s", cvs_cmdop == CVS_OP_DIFF ?
111aeaaf4a6Stobias 				    cvs_cmd_diff.cmd_synopsis :
112aeaaf4a6Stobias 				    cvs_cmd_rdiff.cmd_synopsis);
11337fdff3fStobias 			}
11437fdff3fStobias 			break;
1151890abdaSjoris 		case 'l':
1161890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
1171890abdaSjoris 			break;
118394180a4Sjfb 		case 'n':
119394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
120f9b67873Sniallo 			diff_format = D_RCSDIFF;
121394180a4Sjfb 			break;
12267caf486Sjoris 		case 'N':
123fd687d09Stobias 			strlcat(diffargs, " -N", sizeof(diffargs));
1242d6f447eSjoris 			Nflag = 1;
12567caf486Sjoris 			break;
126261cb0daSjoris 		case 'p':
127261cb0daSjoris 			strlcat(diffargs, " -p", sizeof(diffargs));
128261cb0daSjoris 			diff_pflag = 1;
129261cb0daSjoris 			break;
130bcf22459Stobias 		case 'R':
131bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
132bcf22459Stobias 			break;
13308f90673Sjfb 		case 'r':
134be756b91Stobias 			if (date1 == -1 && rev1 == NULL) {
1354230e8b3Sjoris 				rev1 = optarg;
136be756b91Stobias 			} else if (date2 == -1 && rev2 == NULL) {
1374230e8b3Sjoris 				rev2 = optarg;
13816cfc147Sjoris 			} else {
1393ad3fb45Sjoris 				fatal("no more than 2 revisions/dates can"
14008f90673Sjfb 				    " be specified");
14108f90673Sjfb 			}
14208f90673Sjfb 			break;
14308f90673Sjfb 		case 'u':
144f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
145f9b67873Sniallo 			diff_format = D_UNIFIED;
14608f90673Sjfb 			break;
14737fdff3fStobias 		case 'V':
14837fdff3fStobias 			fatal("the -V option is obsolete "
14937fdff3fStobias 			    "and should not be used");
15008f90673Sjfb 		default:
151aeaaf4a6Stobias 			fatal("%s", cvs_cmdop == CVS_OP_DIFF ?
152aeaaf4a6Stobias 			    cvs_cmd_diff.cmd_synopsis :
153aeaaf4a6Stobias 			    cvs_cmd_rdiff.cmd_synopsis);
15408f90673Sjfb 		}
15508f90673Sjfb 	}
15608f90673Sjfb 
1573ad3fb45Sjoris 	argc -= optind;
1583ad3fb45Sjoris 	argv += optind;
159dc6a6879Sjfb 
1603ad3fb45Sjoris 	cr.enterdir = NULL;
1613ad3fb45Sjoris 	cr.leavedir = NULL;
1629fac60a5Sjoris 
163fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_RDIFF) {
164909bf3e0Stobias 		if (rev1 == NULL && rev2 == NULL && dateflag1 == NULL &&
165909bf3e0Stobias 		    dateflag2 == NULL)
166fd660bf2Stobias 			fatal("must specify at least one revision/date!");
167fd660bf2Stobias 
168fd687d09Stobias 		if (!argc)
169fd687d09Stobias 			fatal("%s", cvs_cmd_rdiff.cmd_synopsis);
170fd687d09Stobias 
171fd660bf2Stobias 		if (!diff_format) {
172fd660bf2Stobias 			strlcat(diffargs, " -c", sizeof(diffargs));
173fd660bf2Stobias 			diff_format = D_CONTEXT;
174fd660bf2Stobias 		}
175fd660bf2Stobias 
176fd660bf2Stobias 		flags |= CR_REPO;
177fd660bf2Stobias 	}
178fd660bf2Stobias 
1799fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
18080f6ca9bSjoris 		cvs_client_connect_to_server();
1819fac60a5Sjoris 		cr.fileproc = cvs_client_sendfile;
1829fac60a5Sjoris 
1839fac60a5Sjoris 		if (!(flags & CR_RECURSE_DIRS))
1849fac60a5Sjoris 			cvs_client_send_request("Argument -l");
1859fac60a5Sjoris 
18637fdff3fStobias 		if (kflag)
18737fdff3fStobias 			cvs_client_send_request("Argument -k%s", koptstr);
18837fdff3fStobias 
1899fac60a5Sjoris 		switch (diff_format) {
1909fac60a5Sjoris 		case D_CONTEXT:
1919fac60a5Sjoris 			cvs_client_send_request("Argument -c");
1929fac60a5Sjoris 			break;
1939fac60a5Sjoris 		case D_RCSDIFF:
1949fac60a5Sjoris 			cvs_client_send_request("Argument -n");
1959fac60a5Sjoris 			break;
1969fac60a5Sjoris 		case D_UNIFIED:
1979fac60a5Sjoris 			cvs_client_send_request("Argument -u");
1989fac60a5Sjoris 			break;
1999fac60a5Sjoris 		default:
2009fac60a5Sjoris 			break;
2019fac60a5Sjoris 		}
2029fac60a5Sjoris 
2039fac60a5Sjoris 		if (Nflag == 1)
2049fac60a5Sjoris 			cvs_client_send_request("Argument -N");
2059fac60a5Sjoris 
2069fac60a5Sjoris 		if (diff_pflag == 1)
2079fac60a5Sjoris 			cvs_client_send_request("Argument -p");
2089fac60a5Sjoris 
2099fac60a5Sjoris 		if (rev1 != NULL)
2109fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev1);
2119fac60a5Sjoris 		if (rev2 != NULL)
2129fac60a5Sjoris 			cvs_client_send_request("Argument -r%s", rev2);
213909bf3e0Stobias 
214909bf3e0Stobias 		if (dateflag1 != NULL)
215909bf3e0Stobias 			cvs_client_send_request("Argument -D%s", dateflag1);
216909bf3e0Stobias 		if (dateflag2 != NULL)
217909bf3e0Stobias 			cvs_client_send_request("Argument -D%s", dateflag2);
2189fac60a5Sjoris 	} else {
219fd660bf2Stobias 		if (cvs_cmdop == CVS_OP_RDIFF &&
220fd660bf2Stobias 		    chdir(current_cvsroot->cr_dir) == -1)
221fd660bf2Stobias 			fatal("cvs_diff: %s", strerror(errno));
222fd660bf2Stobias 
223bc5d89feSjoris 		cr.fileproc = cvs_diff_local;
2249fac60a5Sjoris 	}
2259fac60a5Sjoris 
2261890abdaSjoris 	cr.flags = flags;
227dc6a6879Sjfb 
2284230e8b3Sjoris 	diff_rev1 = diff_rev2 = NULL;
2294230e8b3Sjoris 
230fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF ||
231fd660bf2Stobias 	    current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
2323ad3fb45Sjoris 		if (argc > 0)
2333ad3fb45Sjoris 			cvs_file_run(argc, argv, &cr);
234d990145dSray 		else
2353ad3fb45Sjoris 			cvs_file_run(1, &arg, &cr);
236fd660bf2Stobias 	}
237e4276007Sjfb 
2389fac60a5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
2399fac60a5Sjoris 		cvs_client_send_files(argv, argc);
2409fac60a5Sjoris 		cvs_client_senddir(".");
241fd660bf2Stobias 
242fd660bf2Stobias 		cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ?
243fd660bf2Stobias 		    "rdiff" : "diff");
244fd660bf2Stobias 
2459fac60a5Sjoris 		cvs_client_get_responses();
2469fac60a5Sjoris 	}
2479fac60a5Sjoris 
248e4276007Sjfb 	return (0);
249e4276007Sjfb }
250e4276007Sjfb 
25101af718aSjoris void
2523ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf)
253f9b67873Sniallo {
254741443cbSjoris 	BUF *b1;
2552e0d696aSjoris 	int fd1, fd2;
2563ad3fb45Sjoris 	struct stat st;
2573ad3fb45Sjoris 	struct timeval tv[2], tv2[2];
2586534056aStobias 	struct tm datetm;
259be756b91Stobias 	char rbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ], *p1, *p2;
260d71a0c9bSxsa 
261741443cbSjoris 	b1 = NULL;
262fd687d09Stobias 	fd1 = fd2 = -1;
263fd687d09Stobias 	p1 = p2 = NULL;
264f9b67873Sniallo 
2653ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path);
2663ad3fb45Sjoris 
2673ad3fb45Sjoris 	if (cf->file_type == CVS_DIR) {
2683ad3fb45Sjoris 		if (verbosity > 1)
2691489d205Sjoris 			cvs_log(LP_ERR, "Diffing inside %s", cf->file_path);
2703ad3fb45Sjoris 		return;
2713ad3fb45Sjoris 	}
2723ad3fb45Sjoris 
27351ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
2743ad3fb45Sjoris 
275fd687d09Stobias 	if (cvs_cmdop == CVS_OP_DIFF) {
276fd687d09Stobias 		if (cf->file_ent == NULL) {
277fd687d09Stobias 			cvs_log(LP_ERR, "I know nothing about %s",
27867caf486Sjoris 			    cf->file_path);
2793ad3fb45Sjoris 			return;
28067caf486Sjoris 		}
2813ad3fb45Sjoris 
282fd687d09Stobias 		switch (cf->file_ent->ce_status) {
283fd687d09Stobias 		case CVS_ENT_ADDED:
284fd687d09Stobias 			if (Nflag == 0) {
285fd687d09Stobias 				cvs_log(LP_ERR, "%s is a new entry, no "
286fd687d09Stobias 				    "comparison available", cf->file_path);
287fd687d09Stobias 				return;
288fd687d09Stobias 			}
2895cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
290fd687d09Stobias 				cvs_log(LP_ERR, "cannot find %s",
291fd687d09Stobias 				    cf->file_path);
292fd687d09Stobias 				return;
293fd687d09Stobias 			}
294fd687d09Stobias 			break;
295fd687d09Stobias 		case CVS_ENT_REMOVED:
296fd687d09Stobias 			if (Nflag == 0) {
297fd687d09Stobias 				cvs_log(LP_ERR, "%s was removed, no "
298fd687d09Stobias 				    "comparison available", cf->file_path);
299fd687d09Stobias 				return;
300fd687d09Stobias 			}
301fd687d09Stobias 			if (cf->file_rcs == NULL) {
302fd687d09Stobias 				cvs_log(LP_ERR, "cannot find RCS file for %s",
303fd687d09Stobias 				    cf->file_path);
304fd687d09Stobias 				return;
305fd687d09Stobias 			}
306fd687d09Stobias 			break;
307fd687d09Stobias 		default:
3085cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
3091489d205Sjoris 				cvs_printf("? %s\n", cf->file_path);
310fd687d09Stobias 				return;
311fd687d09Stobias 			}
312c638a04bSjoris 
313fd687d09Stobias 			if (cf->file_rcs == NULL) {
314fd687d09Stobias 				cvs_log(LP_ERR, "cannot find RCS file for %s",
315fd687d09Stobias 				    cf->file_path);
316fd687d09Stobias 				return;
317fd687d09Stobias 			}
318fd687d09Stobias 			break;
319fd687d09Stobias 		}
320fd687d09Stobias 	}
321fd687d09Stobias 
322be756b91Stobias 	if (cf->file_status == FILE_UPTODATE && rev1 == NULL && rev2 == NULL &&
323be756b91Stobias 	    date1 == -1 && date2 == -1)
324fd687d09Stobias 		return;
325fd687d09Stobias 
326ebd92267Sjoris 	if (cf->file_rcs != NULL && cf->file_rcs->rf_head == NULL) {
327e28eda4eStobias 		cvs_log(LP_ERR, "no head revision in RCS file for %s\n",
328e28eda4eStobias 		    cf->file_path);
329e28eda4eStobias 		return;
330e28eda4eStobias 	}
331e28eda4eStobias 
332fd687d09Stobias 	if (kflag && cf->file_rcs != NULL)
33337fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, kflag);
33437fdff3fStobias 
335fd687d09Stobias 	if (cf->file_rcs == NULL)
336fd687d09Stobias 		diff_rev1 = NULL;
337be756b91Stobias 	else if (rev1 != NULL || date1 != -1) {
338be756b91Stobias 		cvs_specified_date = date1;
339fd687d09Stobias 		diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs);
340fd687d09Stobias 		if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_DIFF) {
341223285fdStobias 			if (rev1 != NULL) {
342fd687d09Stobias 				cvs_log(LP_ERR, "tag %s not in file %s", rev1,
343fd687d09Stobias 				    cf->file_path);
344223285fdStobias 				goto cleanup;
345223285fdStobias 			} else if (Nflag) {
346223285fdStobias 				diff_rev1 = NULL;
347223285fdStobias 			} else {
3486534056aStobias 				gmtime_r(&cvs_specified_date, &datetm);
349be756b91Stobias 				strftime(tbuf, sizeof(tbuf),
3506534056aStobias 				    "%Y.%m.%d.%H.%M.%S", &datetm);
351be756b91Stobias 				cvs_log(LP_ERR, "no revision for date %s in "
352be756b91Stobias 				    "file %s", tbuf, cf->file_path);
353fd687d09Stobias 				goto cleanup;
354223285fdStobias 			}
355fd687d09Stobias 		} else if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_RDIFF &&
356fd687d09Stobias 		    force_head) {
357fd660bf2Stobias 			/* -f is not allowed for unknown symbols */
358fd687d09Stobias 			if ((diff_rev1 = rcsnum_parse(rev1)) == NULL)
359fd660bf2Stobias 				fatal("no such tag %s", rev1);
360fd660bf2Stobias 			rcsnum_free(diff_rev1);
361fd660bf2Stobias 
362fd687d09Stobias 			diff_rev1 = cf->file_rcs->rf_head;
363fd660bf2Stobias 		}
364be756b91Stobias 		cvs_specified_date = -1;
365fd687d09Stobias 	} else if (cvs_cmdop == CVS_OP_DIFF) {
366fd687d09Stobias 		if (cf->file_ent->ce_status == CVS_ENT_ADDED)
367fd687d09Stobias 			diff_rev1 = NULL;
368fd687d09Stobias 		else
369fd687d09Stobias 			diff_rev1 = cf->file_ent->ce_rev;
370fd660bf2Stobias 	}
37151ef6581Sjoris 
372fd687d09Stobias 	if (cf->file_rcs == NULL)
373fd687d09Stobias 		diff_rev2 = NULL;
374be756b91Stobias 	else if (rev2 != NULL || date2 != -1) {
375be756b91Stobias 		cvs_specified_date = date2;
376fd687d09Stobias 		diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs);
377fd687d09Stobias 		if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_DIFF) {
378be756b91Stobias 			if (rev2 != NULL) {
379fd687d09Stobias 				cvs_log(LP_ERR, "tag %s not in file %s", rev2,
380fd687d09Stobias 				    cf->file_path);
381223285fdStobias 				goto cleanup;
382223285fdStobias 			} else if (Nflag) {
383223285fdStobias 				diff_rev2 = NULL;
384be756b91Stobias 			} else {
3856534056aStobias 				gmtime_r(&cvs_specified_date, &datetm);
386be756b91Stobias 				strftime(tbuf, sizeof(tbuf),
3876534056aStobias 				    "%Y.%m.%d.%H.%M.%S", &datetm);
388be756b91Stobias 				cvs_log(LP_ERR, "no revision for date %s in "
389be756b91Stobias 				    "file %s", tbuf, cf->file_path);
390fd687d09Stobias 				goto cleanup;
391223285fdStobias 			}
392fd687d09Stobias 		} else if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_RDIFF &&
393fd687d09Stobias 		    force_head) {
394fd660bf2Stobias 			/* -f is not allowed for unknown symbols */
395fd687d09Stobias 			if ((diff_rev2 = rcsnum_parse(rev2)) == NULL)
396fd660bf2Stobias 				fatal("no such tag %s", rev2);
397fd660bf2Stobias 			rcsnum_free(diff_rev2);
398fd660bf2Stobias 
399fd687d09Stobias 			diff_rev2 = cf->file_rcs->rf_head;
400fd660bf2Stobias 		}
401be756b91Stobias 		cvs_specified_date = -1;
402fd687d09Stobias 	} else if (cvs_cmdop == CVS_OP_RDIFF)
403fd687d09Stobias 		diff_rev2 = cf->file_rcs->rf_head;
404fd687d09Stobias 	else if (cf->file_ent->ce_status == CVS_ENT_REMOVED)
405fd687d09Stobias 		diff_rev2 = NULL;
406fd687d09Stobias 
407fd687d09Stobias 	if (diff_rev1 != NULL && diff_rev2 != NULL &&
408fd687d09Stobias 	    rcsnum_cmp(diff_rev1, diff_rev2, 0) == 0)
409fd687d09Stobias 		goto cleanup;
410fd687d09Stobias 
411fd687d09Stobias 	switch (cvs_cmdop) {
412fd687d09Stobias 	case CVS_OP_DIFF:
413*e91b8a58Sjoris 		if (diff_rev1 != NULL && diff_rev2 == NULL &&
414*e91b8a58Sjoris 		    rcsnum_cmp(diff_rev1, cf->file_ent->ce_rev, 0) == 0)
415fd687d09Stobias 			goto cleanup;
416fd687d09Stobias 		break;
417fd687d09Stobias 	case CVS_OP_RDIFF:
418fd687d09Stobias 		if (diff_rev1 == NULL && diff_rev2 == NULL)
419fd687d09Stobias 			goto cleanup;
420fd687d09Stobias 		break;
421fd660bf2Stobias 	}
422fd660bf2Stobias 
423fd660bf2Stobias 	cvs_printf("Index: %s\n", cf->file_path);
424fd660bf2Stobias 	if (cvs_cmdop == CVS_OP_DIFF)
425fd687d09Stobias 		cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV,
426fd687d09Stobias 		    cf->file_rcs != NULL ? cf->file_rpath : cf->file_path);
427fd660bf2Stobias 
428fd660bf2Stobias 	if (diff_rev1 != NULL) {
429fd687d09Stobias 		if (cvs_cmdop == CVS_OP_DIFF && diff_rev1 != NULL) {
430fd687d09Stobias 			(void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
431fd687d09Stobias 			cvs_printf("retrieving revision %s\n", rbuf);
432fd687d09Stobias 		}
4333ad3fb45Sjoris 
434fd687d09Stobias 		tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev1);
4353ad3fb45Sjoris 		tv[0].tv_usec = 0;
4363ad3fb45Sjoris 		tv[1] = tv[0];
437741443cbSjoris 
438fd687d09Stobias 		(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
439fd687d09Stobias 		fd1 = rcs_rev_write_stmp(cf->file_rcs, diff_rev1, p1, 0);
4402e0d696aSjoris 		if (futimes(fd1, tv) == -1)
441fd660bf2Stobias 			fatal("cvs_diff_local: utimes failed");
442fd660bf2Stobias 	}
4433ad3fb45Sjoris 
444fd687d09Stobias 	if (diff_rev2 != NULL) {
445fd687d09Stobias 		if (cvs_cmdop == CVS_OP_DIFF && rev2 != NULL) {
446fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf));
447fd687d09Stobias 			cvs_printf("retrieving revision %s\n", rbuf);
448fd687d09Stobias 		}
4493ad3fb45Sjoris 
4503ad3fb45Sjoris 		tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2);
4513ad3fb45Sjoris 		tv2[0].tv_usec = 0;
4523ad3fb45Sjoris 		tv2[1] = tv2[0];
453741443cbSjoris 
454fd687d09Stobias 		(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
4552e0d696aSjoris 		fd2 = rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0);
4562e0d696aSjoris 		if (futimes(fd2, tv2) == -1)
457fd660bf2Stobias 			fatal("cvs_diff_local: utimes failed");
4585cf15c45Sjoris 	} else if (cvs_cmdop == CVS_OP_DIFF &&
4595cf15c45Sjoris 	    (cf->file_flags & FILE_ON_DISK) &&
460fd687d09Stobias 	    cf->file_ent->ce_status != CVS_ENT_REMOVED) {
461*e91b8a58Sjoris 		(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
462*e91b8a58Sjoris 		if (cvs_server_active == 1 && cf->fd == -1) {
463*e91b8a58Sjoris 			tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs,
464*e91b8a58Sjoris 			    cf->file_ent->ce_rev);
465*e91b8a58Sjoris 			tv2[0].tv_usec = 0;
466*e91b8a58Sjoris 			tv2[1] = tv2[0];
467*e91b8a58Sjoris 
468*e91b8a58Sjoris 			fd2 = rcs_rev_write_stmp(cf->file_rcs,
469*e91b8a58Sjoris 			    cf->file_ent->ce_rev, p2, 0);
470*e91b8a58Sjoris 			if (futimes(fd2, tv2) == -1)
471*e91b8a58Sjoris 				fatal("cvs_diff_local: futimes failed");
472*e91b8a58Sjoris 		} else {
4733ad3fb45Sjoris 			if (fstat(cf->fd, &st) == -1)
4743ad3fb45Sjoris 				fatal("fstat failed %s", strerror(errno));
4759aad96bcStobias 			b1 = cvs_buf_load_fd(cf->fd);
4763ad3fb45Sjoris 
4773ad3fb45Sjoris 			tv2[0].tv_sec = st.st_mtime;
4783ad3fb45Sjoris 			tv2[0].tv_usec = 0;
4793ad3fb45Sjoris 			tv2[1] = tv2[0];
480741443cbSjoris 
4812e0d696aSjoris 			fd2 = cvs_buf_write_stmp(b1, p2, tv2);
482741443cbSjoris 			cvs_buf_free(b1);
4833ad3fb45Sjoris 		}
484*e91b8a58Sjoris 	}
4853ad3fb45Sjoris 
486fd687d09Stobias 	switch (cvs_cmdop) {
487fd687d09Stobias 	case CVS_OP_DIFF:
4883ad3fb45Sjoris 		cvs_printf("%s", diffargs);
4893ad3fb45Sjoris 
490fd687d09Stobias 		if (rev1 != NULL && diff_rev1 != NULL) {
491fd687d09Stobias 			(void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
4923ad3fb45Sjoris 			cvs_printf(" -r%s", rbuf);
4933ad3fb45Sjoris 
494fd687d09Stobias 			if (rev2 != NULL && diff_rev2 != NULL) {
495fd660bf2Stobias 				(void)rcsnum_tostr(diff_rev2, rbuf,
496fd660bf2Stobias 				    sizeof(rbuf));
4973ad3fb45Sjoris 				cvs_printf(" -r%s", rbuf);
4983ad3fb45Sjoris 			}
49967caf486Sjoris 		}
5003ad3fb45Sjoris 
501fd660bf2Stobias 		if (diff_rev2 == NULL)
502fd687d09Stobias 			cvs_printf(" %s", cf->file_path);
50309d59ca3Stobias 		cvs_printf("\n");
504fd687d09Stobias 		break;
505fd687d09Stobias 	case CVS_OP_RDIFF:
506fd660bf2Stobias 		cvs_printf("diff ");
507fd660bf2Stobias 		switch (diff_format) {
508fd660bf2Stobias 		case D_CONTEXT:
509fd660bf2Stobias 			cvs_printf("-c ");
510fd660bf2Stobias 			break;
511fd660bf2Stobias 		case D_RCSDIFF:
512fd660bf2Stobias 			cvs_printf("-n ");
513fd660bf2Stobias 			break;
514fd660bf2Stobias 		case D_UNIFIED:
515fd660bf2Stobias 			cvs_printf("-u ");
516fd660bf2Stobias 			break;
517fd660bf2Stobias 		default:
518fd660bf2Stobias 			break;
519fd660bf2Stobias 		}
520fd660bf2Stobias 		if (diff_rev1 == NULL) {
521fd660bf2Stobias 			cvs_printf("%s ", CVS_PATH_DEVNULL);
522fd660bf2Stobias 		} else {
523fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
524fd660bf2Stobias 			cvs_printf("%s:%s ", cf->file_path, rbuf);
525fd660bf2Stobias 		}
5263ad3fb45Sjoris 
527fd660bf2Stobias 		if (diff_rev2 == NULL) {
528fd660bf2Stobias 			cvs_printf("%s:removed\n", cf->file_path);
529fd660bf2Stobias 		} else {
530fd660bf2Stobias 			(void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 :
531fd660bf2Stobias 			    cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
532fd660bf2Stobias 			cvs_printf("%s:%s\n", cf->file_path, rbuf);
533fd660bf2Stobias 		}
534fd687d09Stobias 		break;
535fd660bf2Stobias 	}
536fd660bf2Stobias 
537fd687d09Stobias 	if (fd1 == -1) {
538fd687d09Stobias 		if ((fd1 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1)
539fd687d09Stobias 			fatal("cannot open %s", CVS_PATH_DEVNULL);
540fd687d09Stobias 	}
541fd687d09Stobias 	if (fd2 == -1) {
542fd687d09Stobias 		if ((fd2 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1)
543fd687d09Stobias 			fatal("cannot open %s", CVS_PATH_DEVNULL);
544741443cbSjoris 	}
5453ad3fb45Sjoris 
546fd687d09Stobias 	if (cvs_diffreg(p1 != NULL ? cf->file_path : CVS_PATH_DEVNULL,
547fd687d09Stobias 	    p2 != NULL ? cf->file_path : CVS_PATH_DEVNULL, fd1, fd2, NULL)
548fd687d09Stobias 	    == D_ERROR)
549d71a0c9bSxsa 		fatal("cvs_diff_local: failed to get RCS patch");
550d71a0c9bSxsa 
5512e0d696aSjoris 	close(fd1);
5522e0d696aSjoris 	close(fd2);
5532e0d696aSjoris 
5543ad3fb45Sjoris 	cvs_worklist_run(&temp_files, cvs_worklist_unlink);
55567caf486Sjoris 
556d71a0c9bSxsa 	if (p1 != NULL)
557d71a0c9bSxsa 		xfree(p1);
558d71a0c9bSxsa 	if (p2 != NULL)
559d71a0c9bSxsa 		xfree(p2);
560d71a0c9bSxsa 
561fd687d09Stobias cleanup:
562fd687d09Stobias 	if (diff_rev1 != NULL &&
563fd687d09Stobias 	    (cf->file_rcs == NULL || diff_rev1 != cf->file_rcs->rf_head) &&
564fd687d09Stobias 	    (cf->file_ent == NULL || diff_rev1 != cf->file_ent->ce_rev))
565fd687d09Stobias 		xfree(diff_rev1);
566fd687d09Stobias 	diff_rev1 = NULL;
567261cb0daSjoris 
568fd687d09Stobias 	if (diff_rev2 != NULL &&
569fd687d09Stobias 	    (cf->file_rcs == NULL || diff_rev2 != cf->file_rcs->rf_head))
570fd687d09Stobias 		xfree(diff_rev2);
571fd687d09Stobias 	diff_rev2 = NULL;
572f9b67873Sniallo }
573