1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino *
4*86d7f5d3SJohn Marino * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino * and others.
6*86d7f5d3SJohn Marino *
7*86d7f5d3SJohn Marino * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino *
10*86d7f5d3SJohn Marino * You may distribute under the terms of the GNU General Public License as
11*86d7f5d3SJohn Marino * specified in the README file that comes with the CVS source distribution.
12*86d7f5d3SJohn Marino *
13*86d7f5d3SJohn Marino * Difference
14*86d7f5d3SJohn Marino *
15*86d7f5d3SJohn Marino * Run diff against versions in the repository. Options that are specified are
16*86d7f5d3SJohn Marino * passed on directly to "rcsdiff".
17*86d7f5d3SJohn Marino *
18*86d7f5d3SJohn Marino * Without any file arguments, runs diff against all the currently modified
19*86d7f5d3SJohn Marino * files.
20*86d7f5d3SJohn Marino */
21*86d7f5d3SJohn Marino
22*86d7f5d3SJohn Marino #include "cvs.h"
23*86d7f5d3SJohn Marino
24*86d7f5d3SJohn Marino enum diff_file
25*86d7f5d3SJohn Marino {
26*86d7f5d3SJohn Marino DIFF_ERROR,
27*86d7f5d3SJohn Marino DIFF_ADDED,
28*86d7f5d3SJohn Marino DIFF_REMOVED,
29*86d7f5d3SJohn Marino DIFF_DIFFERENT,
30*86d7f5d3SJohn Marino DIFF_SAME
31*86d7f5d3SJohn Marino };
32*86d7f5d3SJohn Marino
33*86d7f5d3SJohn Marino static Dtype diff_dirproc (void *callerdat, const char *dir,
34*86d7f5d3SJohn Marino const char *pos_repos, const char *update_dir,
35*86d7f5d3SJohn Marino List *entries);
36*86d7f5d3SJohn Marino static int diff_filesdoneproc (void *callerdat, int err,
37*86d7f5d3SJohn Marino const char *repos, const char *update_dir,
38*86d7f5d3SJohn Marino List *entries);
39*86d7f5d3SJohn Marino static int diff_dirleaveproc (void *callerdat, const char *dir,
40*86d7f5d3SJohn Marino int err, const char *update_dir,
41*86d7f5d3SJohn Marino List *entries);
42*86d7f5d3SJohn Marino static enum diff_file diff_file_nodiff (struct file_info *finfo, Vers_TS *vers,
43*86d7f5d3SJohn Marino enum diff_file, char **rev1_cache );
44*86d7f5d3SJohn Marino static int diff_fileproc (void *callerdat, struct file_info *finfo);
45*86d7f5d3SJohn Marino static void diff_mark_errors (int err);
46*86d7f5d3SJohn Marino
47*86d7f5d3SJohn Marino
48*86d7f5d3SJohn Marino /* Global variables. Would be cleaner if we just put this stuff in a
49*86d7f5d3SJohn Marino struct like log.c does. */
50*86d7f5d3SJohn Marino
51*86d7f5d3SJohn Marino /* Command line tags, from -r option. Points into argv. */
52*86d7f5d3SJohn Marino static char *diff_rev1, *diff_rev2;
53*86d7f5d3SJohn Marino /* Command line dates, from -D option. Malloc'd. */
54*86d7f5d3SJohn Marino static char *diff_date1, *diff_date2;
55*86d7f5d3SJohn Marino static char *diff_join1, *diff_join2;
56*86d7f5d3SJohn Marino static char *use_rev1, *use_rev2;
57*86d7f5d3SJohn Marino static int have_rev1_label, have_rev2_label;
58*86d7f5d3SJohn Marino
59*86d7f5d3SJohn Marino /* Revision of the user file, if it is unchanged from something in the
60*86d7f5d3SJohn Marino repository and we want to use that fact. */
61*86d7f5d3SJohn Marino static char *user_file_rev;
62*86d7f5d3SJohn Marino
63*86d7f5d3SJohn Marino static char *options;
64*86d7f5d3SJohn Marino static char **diff_argv;
65*86d7f5d3SJohn Marino static int diff_argc;
66*86d7f5d3SJohn Marino static size_t diff_arg_allocated;
67*86d7f5d3SJohn Marino static int diff_errors;
68*86d7f5d3SJohn Marino static int empty_files;
69*86d7f5d3SJohn Marino
70*86d7f5d3SJohn Marino static const char *const diff_usage[] =
71*86d7f5d3SJohn Marino {
72*86d7f5d3SJohn Marino "Usage: %s %s [-lR] [-k kopt] [format_options]\n",
73*86d7f5d3SJohn Marino " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
74*86d7f5d3SJohn Marino "\t-l\tLocal directory only, not recursive\n",
75*86d7f5d3SJohn Marino "\t-R\tProcess directories recursively.\n",
76*86d7f5d3SJohn Marino "\t-k kopt\tSpecify keyword expansion mode.\n",
77*86d7f5d3SJohn Marino "\t-D d1\tDiff revision for date against working file.\n",
78*86d7f5d3SJohn Marino "\t-D d2\tDiff rev1/date1 against date2.\n",
79*86d7f5d3SJohn Marino "\t-r rev1\tDiff revision for rev1 against working file.\n",
80*86d7f5d3SJohn Marino "\t-r rev2\tDiff rev1/date1 against rev2.\n",
81*86d7f5d3SJohn Marino "\nformat_options:\n",
82*86d7f5d3SJohn Marino " -i --ignore-case Consider upper- and lower-case to be the same.\n",
83*86d7f5d3SJohn Marino " -w --ignore-all-space Ignore all white space.\n",
84*86d7f5d3SJohn Marino " -b --ignore-space-change Ignore changes in the amount of white space.\n",
85*86d7f5d3SJohn Marino " -B --ignore-blank-lines Ignore changes whose lines are all blank.\n",
86*86d7f5d3SJohn Marino " -I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.\n",
87*86d7f5d3SJohn Marino " --binary Read and write data in binary mode.\n",
88*86d7f5d3SJohn Marino " -a --text Treat all files as text.\n\n",
89*86d7f5d3SJohn Marino " -c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.\n",
90*86d7f5d3SJohn Marino " -u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.\n",
91*86d7f5d3SJohn Marino " -NUM Use NUM context lines.\n",
92*86d7f5d3SJohn Marino " -L LABEL --label LABEL Use LABEL instead of file name.\n",
93*86d7f5d3SJohn Marino " -p --show-c-function Show which C function each change is in.\n",
94*86d7f5d3SJohn Marino " -F RE --show-function-line=RE Show the most recent line matching RE.\n",
95*86d7f5d3SJohn Marino " --brief Output only whether files differ.\n",
96*86d7f5d3SJohn Marino " -e --ed Output an ed script.\n",
97*86d7f5d3SJohn Marino " -f --forward-ed Output something like an ed script in forward order.\n",
98*86d7f5d3SJohn Marino " -n --rcs Output an RCS format diff.\n",
99*86d7f5d3SJohn Marino " -y --side-by-side Output in two columns.\n",
100*86d7f5d3SJohn Marino " -W NUM --width=NUM Output at most NUM (default 130) characters per line.\n",
101*86d7f5d3SJohn Marino " --left-column Output only the left column of common lines.\n",
102*86d7f5d3SJohn Marino " --suppress-common-lines Do not output common lines.\n",
103*86d7f5d3SJohn Marino " --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.\n",
104*86d7f5d3SJohn Marino " --GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.\n",
105*86d7f5d3SJohn Marino " --line-format=LFMT Similar, but format all input lines with LFMT.\n",
106*86d7f5d3SJohn Marino " --LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.\n",
107*86d7f5d3SJohn Marino " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.\n",
108*86d7f5d3SJohn Marino " GFMT may contain:\n",
109*86d7f5d3SJohn Marino " %%< lines from FILE1\n",
110*86d7f5d3SJohn Marino " %%> lines from FILE2\n",
111*86d7f5d3SJohn Marino " %%= lines common to FILE1 and FILE2\n",
112*86d7f5d3SJohn Marino " %%[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n",
113*86d7f5d3SJohn Marino " LETTERs are as follows for new group, lower case for old group:\n",
114*86d7f5d3SJohn Marino " F first line number\n",
115*86d7f5d3SJohn Marino " L last line number\n",
116*86d7f5d3SJohn Marino " N number of lines = L-F+1\n",
117*86d7f5d3SJohn Marino " E F-1\n",
118*86d7f5d3SJohn Marino " M L+1\n",
119*86d7f5d3SJohn Marino " LFMT may contain:\n",
120*86d7f5d3SJohn Marino " %%L contents of line\n",
121*86d7f5d3SJohn Marino " %%l contents of line, excluding any trailing newline\n",
122*86d7f5d3SJohn Marino " %%[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number\n",
123*86d7f5d3SJohn Marino " Either GFMT or LFMT may contain:\n",
124*86d7f5d3SJohn Marino " %%%% %%\n",
125*86d7f5d3SJohn Marino " %%c'C' the single character C\n",
126*86d7f5d3SJohn Marino " %%c'\\OOO' the character with octal code OOO\n\n",
127*86d7f5d3SJohn Marino " -t --expand-tabs Expand tabs to spaces in output.\n",
128*86d7f5d3SJohn Marino " -T --initial-tab Make tabs line up by prepending a tab.\n\n",
129*86d7f5d3SJohn Marino " -N --new-file Treat absent files as empty.\n",
130*86d7f5d3SJohn Marino " -s --report-identical-files Report when two files are the same.\n",
131*86d7f5d3SJohn Marino " --horizon-lines=NUM Keep NUM lines of the common prefix and suffix.\n",
132*86d7f5d3SJohn Marino " -d --minimal Try hard to find a smaller set of changes.\n",
133*86d7f5d3SJohn Marino " -H --speed-large-files Assume large files and many scattered small changes.\n",
134*86d7f5d3SJohn Marino "\n(Specify the --help global option for a list of other help options)\n",
135*86d7f5d3SJohn Marino NULL
136*86d7f5d3SJohn Marino };
137*86d7f5d3SJohn Marino
138*86d7f5d3SJohn Marino /* I copied this array directly out of diff.c in diffutils 2.7, after
139*86d7f5d3SJohn Marino removing the following entries, none of which seem relevant to use
140*86d7f5d3SJohn Marino with CVS:
141*86d7f5d3SJohn Marino --help
142*86d7f5d3SJohn Marino --version (-v)
143*86d7f5d3SJohn Marino --recursive (-r)
144*86d7f5d3SJohn Marino --unidirectional-new-file (-P)
145*86d7f5d3SJohn Marino --starting-file (-S)
146*86d7f5d3SJohn Marino --exclude (-x)
147*86d7f5d3SJohn Marino --exclude-from (-X)
148*86d7f5d3SJohn Marino --sdiff-merge-assist
149*86d7f5d3SJohn Marino --paginate (-l) (doesn't work with library callbacks)
150*86d7f5d3SJohn Marino
151*86d7f5d3SJohn Marino I changed the options which take optional arguments (--context and
152*86d7f5d3SJohn Marino --unified) to return a number rather than a letter, so that the
153*86d7f5d3SJohn Marino optional argument could be handled more easily. I changed the
154*86d7f5d3SJohn Marino --brief and --ifdef options to return numbers, since -q and -D mean
155*86d7f5d3SJohn Marino something else to cvs diff.
156*86d7f5d3SJohn Marino
157*86d7f5d3SJohn Marino The numbers 129- that appear in the fourth element of some entries
158*86d7f5d3SJohn Marino tell the big switch in `diff' how to process those options. -- Ian
159*86d7f5d3SJohn Marino
160*86d7f5d3SJohn Marino The following options, which diff lists as "An alias, no longer
161*86d7f5d3SJohn Marino recommended" have been removed: --file-label --entire-new-file
162*86d7f5d3SJohn Marino --ascii --print. */
163*86d7f5d3SJohn Marino
164*86d7f5d3SJohn Marino static struct option const longopts[] =
165*86d7f5d3SJohn Marino {
166*86d7f5d3SJohn Marino {"ignore-blank-lines", 0, 0, 'B'},
167*86d7f5d3SJohn Marino {"context", 2, 0, 143},
168*86d7f5d3SJohn Marino {"ifdef", 1, 0, 131},
169*86d7f5d3SJohn Marino {"show-function-line", 1, 0, 'F'},
170*86d7f5d3SJohn Marino {"speed-large-files", 0, 0, 'H'},
171*86d7f5d3SJohn Marino {"ignore-matching-lines", 1, 0, 'I'},
172*86d7f5d3SJohn Marino {"label", 1, 0, 'L'},
173*86d7f5d3SJohn Marino {"new-file", 0, 0, 'N'},
174*86d7f5d3SJohn Marino {"initial-tab", 0, 0, 'T'},
175*86d7f5d3SJohn Marino {"width", 1, 0, 'W'},
176*86d7f5d3SJohn Marino {"text", 0, 0, 'a'},
177*86d7f5d3SJohn Marino {"ignore-space-change", 0, 0, 'b'},
178*86d7f5d3SJohn Marino {"minimal", 0, 0, 'd'},
179*86d7f5d3SJohn Marino {"ed", 0, 0, 'e'},
180*86d7f5d3SJohn Marino {"forward-ed", 0, 0, 'f'},
181*86d7f5d3SJohn Marino {"ignore-case", 0, 0, 'i'},
182*86d7f5d3SJohn Marino {"rcs", 0, 0, 'n'},
183*86d7f5d3SJohn Marino {"show-c-function", 0, 0, 'p'},
184*86d7f5d3SJohn Marino
185*86d7f5d3SJohn Marino /* This is a potentially very useful option, except the output is so
186*86d7f5d3SJohn Marino silly. It would be much better for it to look like "cvs rdiff -s"
187*86d7f5d3SJohn Marino which displays all the same info, minus quite a few lines of
188*86d7f5d3SJohn Marino extraneous garbage. */
189*86d7f5d3SJohn Marino {"brief", 0, 0, 145},
190*86d7f5d3SJohn Marino
191*86d7f5d3SJohn Marino {"report-identical-files", 0, 0, 's'},
192*86d7f5d3SJohn Marino {"expand-tabs", 0, 0, 't'},
193*86d7f5d3SJohn Marino {"ignore-all-space", 0, 0, 'w'},
194*86d7f5d3SJohn Marino {"side-by-side", 0, 0, 'y'},
195*86d7f5d3SJohn Marino {"unified", 2, 0, 146},
196*86d7f5d3SJohn Marino {"left-column", 0, 0, 129},
197*86d7f5d3SJohn Marino {"suppress-common-lines", 0, 0, 130},
198*86d7f5d3SJohn Marino {"old-line-format", 1, 0, 132},
199*86d7f5d3SJohn Marino {"new-line-format", 1, 0, 133},
200*86d7f5d3SJohn Marino {"unchanged-line-format", 1, 0, 134},
201*86d7f5d3SJohn Marino {"line-format", 1, 0, 135},
202*86d7f5d3SJohn Marino {"old-group-format", 1, 0, 136},
203*86d7f5d3SJohn Marino {"new-group-format", 1, 0, 137},
204*86d7f5d3SJohn Marino {"unchanged-group-format", 1, 0, 138},
205*86d7f5d3SJohn Marino {"changed-group-format", 1, 0, 139},
206*86d7f5d3SJohn Marino {"horizon-lines", 1, 0, 140},
207*86d7f5d3SJohn Marino {"binary", 0, 0, 142},
208*86d7f5d3SJohn Marino {0, 0, 0, 0}
209*86d7f5d3SJohn Marino };
210*86d7f5d3SJohn Marino
211*86d7f5d3SJohn Marino
212*86d7f5d3SJohn Marino
213*86d7f5d3SJohn Marino /* Add one of OPT or LONGOPT, and ARGUMENT, when present, to global DIFF_ARGV.
214*86d7f5d3SJohn Marino *
215*86d7f5d3SJohn Marino * INPUTS
216*86d7f5d3SJohn Marino * opt A character option representation.
217*86d7f5d3SJohn Marino * longopt A long option name.
218*86d7f5d3SJohn Marino * argument Optional option argument.
219*86d7f5d3SJohn Marino *
220*86d7f5d3SJohn Marino * GLOBALS
221*86d7f5d3SJohn Marino * diff_argc The number of arguments in DIFF_ARGV.
222*86d7f5d3SJohn Marino * diff_argv Array of argument strings.
223*86d7f5d3SJohn Marino * diff_arg_allocated Allocated length of DIFF_ARGV.
224*86d7f5d3SJohn Marino *
225*86d7f5d3SJohn Marino * NOTES
226*86d7f5d3SJohn Marino * Behavior when both OPT & LONGOPT are provided is undefined.
227*86d7f5d3SJohn Marino *
228*86d7f5d3SJohn Marino * RETURNS
229*86d7f5d3SJohn Marino * Nothing.
230*86d7f5d3SJohn Marino */
231*86d7f5d3SJohn Marino static void
add_diff_args(char opt,const char * longopt,const char * argument)232*86d7f5d3SJohn Marino add_diff_args (char opt, const char *longopt, const char *argument)
233*86d7f5d3SJohn Marino {
234*86d7f5d3SJohn Marino char *tmp;
235*86d7f5d3SJohn Marino
236*86d7f5d3SJohn Marino /* Add opt or longopt to diff_arv. */
237*86d7f5d3SJohn Marino assert (opt || (longopt && *longopt));
238*86d7f5d3SJohn Marino assert (!(opt && (longopt && *longopt)));
239*86d7f5d3SJohn Marino if (opt) tmp = Xasprintf ("-%c", opt);
240*86d7f5d3SJohn Marino else tmp = Xasprintf ("--%s", longopt);
241*86d7f5d3SJohn Marino run_add_arg_p (&diff_argc, &diff_arg_allocated, &diff_argv, tmp);
242*86d7f5d3SJohn Marino free (tmp);
243*86d7f5d3SJohn Marino
244*86d7f5d3SJohn Marino /* When present, add ARGUMENT to DIFF_ARGV. */
245*86d7f5d3SJohn Marino if (argument)
246*86d7f5d3SJohn Marino run_add_arg_p (&diff_argc, &diff_arg_allocated, &diff_argv, argument);
247*86d7f5d3SJohn Marino }
248*86d7f5d3SJohn Marino
249*86d7f5d3SJohn Marino
250*86d7f5d3SJohn Marino
251*86d7f5d3SJohn Marino /* CVS 1.9 and similar versions seemed to have pretty weird handling
252*86d7f5d3SJohn Marino of -y and -T. In the cases where it called rcsdiff,
253*86d7f5d3SJohn Marino they would have the meanings mentioned below. In the cases where it
254*86d7f5d3SJohn Marino called diff, they would have the meanings mentioned in "longopts".
255*86d7f5d3SJohn Marino Noone seems to have missed them, so I think the right thing to do is
256*86d7f5d3SJohn Marino just to remove the options altogether (which I have done).
257*86d7f5d3SJohn Marino
258*86d7f5d3SJohn Marino In the case of -z and -q, "cvs diff" did not accept them even back
259*86d7f5d3SJohn Marino when we called rcsdiff (at least, it hasn't accepted them
260*86d7f5d3SJohn Marino recently).
261*86d7f5d3SJohn Marino
262*86d7f5d3SJohn Marino In comparing rcsdiff to the new CVS implementation, I noticed that
263*86d7f5d3SJohn Marino the following rcsdiff flags are not handled by CVS diff:
264*86d7f5d3SJohn Marino
265*86d7f5d3SJohn Marino -y: perform diff even when the requested revisions are the
266*86d7f5d3SJohn Marino same revision number
267*86d7f5d3SJohn Marino -q: run quietly
268*86d7f5d3SJohn Marino -T: preserve modification time on the RCS file
269*86d7f5d3SJohn Marino -z: specify timezone for use in file labels
270*86d7f5d3SJohn Marino
271*86d7f5d3SJohn Marino I think these are not really relevant. -y is undocumented even in
272*86d7f5d3SJohn Marino RCS 5.7, and seems like a minor change at best. According to RCS
273*86d7f5d3SJohn Marino documentation, -T only applies when a RCS file has been modified
274*86d7f5d3SJohn Marino because of lock changes; doesn't CVS sidestep RCS's entire lock
275*86d7f5d3SJohn Marino structure? -z seems to be unsupported by CVS diff, and has a
276*86d7f5d3SJohn Marino different meaning as a global option anyway. (Adding it could be
277*86d7f5d3SJohn Marino a feature, but if it is left out for now, it should not break
278*86d7f5d3SJohn Marino anything.) For the purposes of producing output, CVS diff appears
279*86d7f5d3SJohn Marino mostly to ignore -q. Maybe this should be fixed, but I think it's
280*86d7f5d3SJohn Marino a larger issue than the changes included here. */
281*86d7f5d3SJohn Marino
282*86d7f5d3SJohn Marino int
diff(int argc,char ** argv)283*86d7f5d3SJohn Marino diff (int argc, char **argv)
284*86d7f5d3SJohn Marino {
285*86d7f5d3SJohn Marino int c, err = 0;
286*86d7f5d3SJohn Marino int local = 0;
287*86d7f5d3SJohn Marino int which;
288*86d7f5d3SJohn Marino int option_index;
289*86d7f5d3SJohn Marino char *diff_orig1, *diff_orig2;
290*86d7f5d3SJohn Marino
291*86d7f5d3SJohn Marino if (argc == -1)
292*86d7f5d3SJohn Marino usage (diff_usage);
293*86d7f5d3SJohn Marino
294*86d7f5d3SJohn Marino have_rev1_label = have_rev2_label = 0;
295*86d7f5d3SJohn Marino
296*86d7f5d3SJohn Marino /*
297*86d7f5d3SJohn Marino * Note that we catch all the valid arguments here, so that we can
298*86d7f5d3SJohn Marino * intercept the -r arguments for doing revision diffs; and -l/-R for a
299*86d7f5d3SJohn Marino * non-recursive/recursive diff.
300*86d7f5d3SJohn Marino */
301*86d7f5d3SJohn Marino
302*86d7f5d3SJohn Marino /* Clean out our global variables (multiroot can call us multiple
303*86d7f5d3SJohn Marino times and the server can too, if the client sends several
304*86d7f5d3SJohn Marino diff commands). */
305*86d7f5d3SJohn Marino run_arg_free_p (diff_argc, diff_argv);
306*86d7f5d3SJohn Marino diff_argc = 0;
307*86d7f5d3SJohn Marino
308*86d7f5d3SJohn Marino diff_orig1 = NULL;
309*86d7f5d3SJohn Marino diff_orig2 = NULL;
310*86d7f5d3SJohn Marino diff_rev1 = NULL;
311*86d7f5d3SJohn Marino diff_rev2 = NULL;
312*86d7f5d3SJohn Marino diff_date1 = NULL;
313*86d7f5d3SJohn Marino diff_date2 = NULL;
314*86d7f5d3SJohn Marino diff_join1 = NULL;
315*86d7f5d3SJohn Marino diff_join2 = NULL;
316*86d7f5d3SJohn Marino
317*86d7f5d3SJohn Marino optind = 0;
318*86d7f5d3SJohn Marino /* FIXME: This should really be allocating an argv to be passed to diff
319*86d7f5d3SJohn Marino * later rather than strcatting onto the opts variable. We have some
320*86d7f5d3SJohn Marino * handling routines that can already handle most of the argc/argv
321*86d7f5d3SJohn Marino * maintenance for us and currently, if anyone were to attempt to pass a
322*86d7f5d3SJohn Marino * quoted string in here, it would be split on spaces and tabs on its way
323*86d7f5d3SJohn Marino * to diff.
324*86d7f5d3SJohn Marino */
325*86d7f5d3SJohn Marino while ((c = getopt_long (argc, argv,
326*86d7f5d3SJohn Marino "+abcdefhij:lnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:",
327*86d7f5d3SJohn Marino longopts, &option_index)) != -1)
328*86d7f5d3SJohn Marino {
329*86d7f5d3SJohn Marino switch (c)
330*86d7f5d3SJohn Marino {
331*86d7f5d3SJohn Marino case 'y':
332*86d7f5d3SJohn Marino add_diff_args (0, "side-by-side", NULL);
333*86d7f5d3SJohn Marino break;
334*86d7f5d3SJohn Marino case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
335*86d7f5d3SJohn Marino case 'h': case 'i': case 'n': case 'p': case 's': case 't':
336*86d7f5d3SJohn Marino case 'u': case 'w':
337*86d7f5d3SJohn Marino case '0': case '1': case '2': case '3': case '4': case '5':
338*86d7f5d3SJohn Marino case '6': case '7': case '8': case '9':
339*86d7f5d3SJohn Marino case 'B': case 'H': case 'T':
340*86d7f5d3SJohn Marino add_diff_args (c, NULL, NULL);
341*86d7f5d3SJohn Marino break;
342*86d7f5d3SJohn Marino case 'L':
343*86d7f5d3SJohn Marino if (have_rev1_label++)
344*86d7f5d3SJohn Marino if (have_rev2_label++)
345*86d7f5d3SJohn Marino {
346*86d7f5d3SJohn Marino error (0, 0, "extra -L arguments ignored");
347*86d7f5d3SJohn Marino break;
348*86d7f5d3SJohn Marino }
349*86d7f5d3SJohn Marino /* Fall through. */
350*86d7f5d3SJohn Marino case 'C': case 'F': case 'I': case 'U': case 'W':
351*86d7f5d3SJohn Marino add_diff_args (c, NULL, optarg);
352*86d7f5d3SJohn Marino break;
353*86d7f5d3SJohn Marino case 129: case 130: case 131: case 132: case 133: case 134:
354*86d7f5d3SJohn Marino case 135: case 136: case 137: case 138: case 139: case 140:
355*86d7f5d3SJohn Marino case 141: case 142: case 143: case 145: case 146:
356*86d7f5d3SJohn Marino add_diff_args (0, longopts[option_index].name,
357*86d7f5d3SJohn Marino longopts[option_index].has_arg ? optarg : NULL);
358*86d7f5d3SJohn Marino break;
359*86d7f5d3SJohn Marino case 'R':
360*86d7f5d3SJohn Marino local = 0;
361*86d7f5d3SJohn Marino break;
362*86d7f5d3SJohn Marino case 'l':
363*86d7f5d3SJohn Marino local = 1;
364*86d7f5d3SJohn Marino break;
365*86d7f5d3SJohn Marino case 'k':
366*86d7f5d3SJohn Marino if (options)
367*86d7f5d3SJohn Marino free (options);
368*86d7f5d3SJohn Marino options = RCS_check_kflag (optarg);
369*86d7f5d3SJohn Marino break;
370*86d7f5d3SJohn Marino case 'j':
371*86d7f5d3SJohn Marino {
372*86d7f5d3SJohn Marino char *ptr;
373*86d7f5d3SJohn Marino char *cpy = strdup(optarg);
374*86d7f5d3SJohn Marino
375*86d7f5d3SJohn Marino if ((ptr = strchr(optarg, ':')) != NULL)
376*86d7f5d3SJohn Marino *ptr++ = 0;
377*86d7f5d3SJohn Marino if (diff_rev2 != NULL || diff_date2 != NULL)
378*86d7f5d3SJohn Marino error (1, 0, "no more than two revisions/dates can be specified");
379*86d7f5d3SJohn Marino if (diff_rev1 != NULL || diff_date1 != NULL) {
380*86d7f5d3SJohn Marino diff_join2 = cpy;
381*86d7f5d3SJohn Marino diff_rev2 = optarg;
382*86d7f5d3SJohn Marino diff_date2 = ptr ? Make_Date(ptr) : NULL;
383*86d7f5d3SJohn Marino } else {
384*86d7f5d3SJohn Marino diff_join1 = cpy;
385*86d7f5d3SJohn Marino diff_rev1 = optarg;
386*86d7f5d3SJohn Marino diff_date1 = ptr ? Make_Date(ptr) : NULL;
387*86d7f5d3SJohn Marino }
388*86d7f5d3SJohn Marino }
389*86d7f5d3SJohn Marino break;
390*86d7f5d3SJohn Marino case 'r':
391*86d7f5d3SJohn Marino if (diff_rev2 || diff_date2)
392*86d7f5d3SJohn Marino error (1, 0,
393*86d7f5d3SJohn Marino "no more than two revisions/dates can be specified");
394*86d7f5d3SJohn Marino if (diff_rev1 || diff_date1)
395*86d7f5d3SJohn Marino {
396*86d7f5d3SJohn Marino diff_orig2 = xstrdup (optarg);
397*86d7f5d3SJohn Marino parse_tagdate (&diff_rev2, &diff_date2, optarg);
398*86d7f5d3SJohn Marino }
399*86d7f5d3SJohn Marino else
400*86d7f5d3SJohn Marino {
401*86d7f5d3SJohn Marino diff_orig1 = xstrdup (optarg);
402*86d7f5d3SJohn Marino parse_tagdate (&diff_rev1, &diff_date1, optarg);
403*86d7f5d3SJohn Marino }
404*86d7f5d3SJohn Marino break;
405*86d7f5d3SJohn Marino case 'D':
406*86d7f5d3SJohn Marino if (diff_rev2 || diff_date2)
407*86d7f5d3SJohn Marino error (1, 0,
408*86d7f5d3SJohn Marino "no more than two revisions/dates can be specified");
409*86d7f5d3SJohn Marino if (diff_rev1 || diff_date1)
410*86d7f5d3SJohn Marino diff_date2 = Make_Date (optarg);
411*86d7f5d3SJohn Marino else
412*86d7f5d3SJohn Marino diff_date1 = Make_Date (optarg);
413*86d7f5d3SJohn Marino break;
414*86d7f5d3SJohn Marino case 'N':
415*86d7f5d3SJohn Marino empty_files = 1;
416*86d7f5d3SJohn Marino break;
417*86d7f5d3SJohn Marino case '?':
418*86d7f5d3SJohn Marino default:
419*86d7f5d3SJohn Marino usage (diff_usage);
420*86d7f5d3SJohn Marino break;
421*86d7f5d3SJohn Marino }
422*86d7f5d3SJohn Marino }
423*86d7f5d3SJohn Marino argc -= optind;
424*86d7f5d3SJohn Marino argv += optind;
425*86d7f5d3SJohn Marino
426*86d7f5d3SJohn Marino /* make sure options is non-null */
427*86d7f5d3SJohn Marino if (!options)
428*86d7f5d3SJohn Marino options = xstrdup ("");
429*86d7f5d3SJohn Marino
430*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
431*86d7f5d3SJohn Marino if (current_parsed_root->isremote) {
432*86d7f5d3SJohn Marino /* We're the client side. Fire up the remote server. */
433*86d7f5d3SJohn Marino start_server ();
434*86d7f5d3SJohn Marino
435*86d7f5d3SJohn Marino ign_setup ();
436*86d7f5d3SJohn Marino
437*86d7f5d3SJohn Marino if (local)
438*86d7f5d3SJohn Marino send_arg("-l");
439*86d7f5d3SJohn Marino if (empty_files)
440*86d7f5d3SJohn Marino send_arg("-N");
441*86d7f5d3SJohn Marino send_options (diff_argc, diff_argv);
442*86d7f5d3SJohn Marino if (options[0] != '\0')
443*86d7f5d3SJohn Marino send_arg (options);
444*86d7f5d3SJohn Marino if (diff_join1)
445*86d7f5d3SJohn Marino option_with_arg ("-j", diff_join1);
446*86d7f5d3SJohn Marino else if (diff_orig1)
447*86d7f5d3SJohn Marino option_with_arg ("-r", diff_orig1);
448*86d7f5d3SJohn Marino else if (diff_date1)
449*86d7f5d3SJohn Marino client_senddate (diff_date1);
450*86d7f5d3SJohn Marino if (diff_join2)
451*86d7f5d3SJohn Marino option_with_arg ("-j", diff_join2);
452*86d7f5d3SJohn Marino else if (diff_orig2)
453*86d7f5d3SJohn Marino option_with_arg ("-r", diff_orig2);
454*86d7f5d3SJohn Marino else if (diff_date2)
455*86d7f5d3SJohn Marino client_senddate (diff_date2);
456*86d7f5d3SJohn Marino send_arg ("--");
457*86d7f5d3SJohn Marino
458*86d7f5d3SJohn Marino /* Send the current files unless diffing two revs from the archive */
459*86d7f5d3SJohn Marino if (!diff_rev2 && !diff_date2)
460*86d7f5d3SJohn Marino send_files (argc, argv, local, 0, 0);
461*86d7f5d3SJohn Marino else
462*86d7f5d3SJohn Marino send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
463*86d7f5d3SJohn Marino
464*86d7f5d3SJohn Marino send_file_names (argc, argv, SEND_EXPAND_WILD);
465*86d7f5d3SJohn Marino
466*86d7f5d3SJohn Marino send_to_server ("diff\012", 0);
467*86d7f5d3SJohn Marino err = get_responses_and_close ();
468*86d7f5d3SJohn Marino } else
469*86d7f5d3SJohn Marino #endif
470*86d7f5d3SJohn Marino {
471*86d7f5d3SJohn Marino if (diff_rev1 != NULL)
472*86d7f5d3SJohn Marino tag_check_valid (diff_rev1, argc, argv, local, 0, "", false);
473*86d7f5d3SJohn Marino if (diff_rev2 != NULL)
474*86d7f5d3SJohn Marino tag_check_valid (diff_rev2, argc, argv, local, 0, "", false);
475*86d7f5d3SJohn Marino
476*86d7f5d3SJohn Marino which = W_LOCAL;
477*86d7f5d3SJohn Marino if (diff_rev1 || diff_date1)
478*86d7f5d3SJohn Marino which |= W_REPOS | W_ATTIC;
479*86d7f5d3SJohn Marino
480*86d7f5d3SJohn Marino wrap_setup ();
481*86d7f5d3SJohn Marino
482*86d7f5d3SJohn Marino /* start the recursion processor */
483*86d7f5d3SJohn Marino err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
484*86d7f5d3SJohn Marino diff_dirleaveproc, NULL, argc, argv, local,
485*86d7f5d3SJohn Marino which, 0, CVS_LOCK_READ, NULL, 1, NULL);
486*86d7f5d3SJohn Marino
487*86d7f5d3SJohn Marino }
488*86d7f5d3SJohn Marino /* clean up */
489*86d7f5d3SJohn Marino free (options);
490*86d7f5d3SJohn Marino options = NULL;
491*86d7f5d3SJohn Marino
492*86d7f5d3SJohn Marino if (diff_date1 != NULL)
493*86d7f5d3SJohn Marino free (diff_date1);
494*86d7f5d3SJohn Marino if (diff_date2 != NULL)
495*86d7f5d3SJohn Marino free (diff_date2);
496*86d7f5d3SJohn Marino if (diff_join1 != NULL)
497*86d7f5d3SJohn Marino free (diff_join1);
498*86d7f5d3SJohn Marino if (diff_join2 != NULL)
499*86d7f5d3SJohn Marino free (diff_join2);
500*86d7f5d3SJohn Marino
501*86d7f5d3SJohn Marino return err;
502*86d7f5d3SJohn Marino }
503*86d7f5d3SJohn Marino
504*86d7f5d3SJohn Marino
505*86d7f5d3SJohn Marino
506*86d7f5d3SJohn Marino /*
507*86d7f5d3SJohn Marino * Do a file diff
508*86d7f5d3SJohn Marino */
509*86d7f5d3SJohn Marino /* ARGSUSED */
510*86d7f5d3SJohn Marino static int
diff_fileproc(void * callerdat,struct file_info * finfo)511*86d7f5d3SJohn Marino diff_fileproc (void *callerdat, struct file_info *finfo)
512*86d7f5d3SJohn Marino {
513*86d7f5d3SJohn Marino int status, err = 2; /* 2 == trouble, like rcsdiff */
514*86d7f5d3SJohn Marino Vers_TS *vers;
515*86d7f5d3SJohn Marino enum diff_file empty_file = DIFF_DIFFERENT;
516*86d7f5d3SJohn Marino char *tmp = NULL;
517*86d7f5d3SJohn Marino char *tocvsPath = NULL;
518*86d7f5d3SJohn Marino char *fname = NULL;
519*86d7f5d3SJohn Marino char *label1;
520*86d7f5d3SJohn Marino char *label2;
521*86d7f5d3SJohn Marino char *rev1_cache = NULL;
522*86d7f5d3SJohn Marino
523*86d7f5d3SJohn Marino user_file_rev = 0;
524*86d7f5d3SJohn Marino vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
525*86d7f5d3SJohn Marino
526*86d7f5d3SJohn Marino if (diff_rev2 || diff_date2)
527*86d7f5d3SJohn Marino {
528*86d7f5d3SJohn Marino /* Skip all the following checks regarding the user file; we're
529*86d7f5d3SJohn Marino not using it. */
530*86d7f5d3SJohn Marino }
531*86d7f5d3SJohn Marino else if (vers->vn_user == NULL)
532*86d7f5d3SJohn Marino {
533*86d7f5d3SJohn Marino /* The file does not exist in the working directory. */
534*86d7f5d3SJohn Marino if ((diff_rev1 || diff_date1)
535*86d7f5d3SJohn Marino && vers->srcfile != NULL)
536*86d7f5d3SJohn Marino {
537*86d7f5d3SJohn Marino /* The file does exist in the repository. */
538*86d7f5d3SJohn Marino if (empty_files)
539*86d7f5d3SJohn Marino empty_file = DIFF_REMOVED;
540*86d7f5d3SJohn Marino else
541*86d7f5d3SJohn Marino {
542*86d7f5d3SJohn Marino int exists;
543*86d7f5d3SJohn Marino
544*86d7f5d3SJohn Marino exists = 0;
545*86d7f5d3SJohn Marino /* special handling for TAG_HEAD */
546*86d7f5d3SJohn Marino if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
547*86d7f5d3SJohn Marino {
548*86d7f5d3SJohn Marino char *head =
549*86d7f5d3SJohn Marino (vers->vn_rcs == NULL
550*86d7f5d3SJohn Marino ? NULL
551*86d7f5d3SJohn Marino : RCS_branch_head (vers->srcfile, vers->vn_rcs));
552*86d7f5d3SJohn Marino exists = head != NULL && !RCS_isdead (vers->srcfile, head);
553*86d7f5d3SJohn Marino if (head != NULL)
554*86d7f5d3SJohn Marino free (head);
555*86d7f5d3SJohn Marino }
556*86d7f5d3SJohn Marino else
557*86d7f5d3SJohn Marino {
558*86d7f5d3SJohn Marino Vers_TS *xvers;
559*86d7f5d3SJohn Marino
560*86d7f5d3SJohn Marino xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
561*86d7f5d3SJohn Marino 1, 0);
562*86d7f5d3SJohn Marino exists = xvers->vn_rcs && !RCS_isdead (xvers->srcfile,
563*86d7f5d3SJohn Marino xvers->vn_rcs);
564*86d7f5d3SJohn Marino freevers_ts (&xvers);
565*86d7f5d3SJohn Marino }
566*86d7f5d3SJohn Marino if (exists)
567*86d7f5d3SJohn Marino error (0, 0,
568*86d7f5d3SJohn Marino "%s no longer exists, no comparison available",
569*86d7f5d3SJohn Marino finfo->fullname);
570*86d7f5d3SJohn Marino goto out;
571*86d7f5d3SJohn Marino }
572*86d7f5d3SJohn Marino }
573*86d7f5d3SJohn Marino else
574*86d7f5d3SJohn Marino {
575*86d7f5d3SJohn Marino error (0, 0, "I know nothing about %s", finfo->fullname);
576*86d7f5d3SJohn Marino goto out;
577*86d7f5d3SJohn Marino }
578*86d7f5d3SJohn Marino }
579*86d7f5d3SJohn Marino else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
580*86d7f5d3SJohn Marino {
581*86d7f5d3SJohn Marino /* The file was added locally. */
582*86d7f5d3SJohn Marino int exists = 0;
583*86d7f5d3SJohn Marino
584*86d7f5d3SJohn Marino if (vers->srcfile != NULL)
585*86d7f5d3SJohn Marino {
586*86d7f5d3SJohn Marino /* The file does exist in the repository. */
587*86d7f5d3SJohn Marino
588*86d7f5d3SJohn Marino if (diff_rev1 || diff_date1)
589*86d7f5d3SJohn Marino {
590*86d7f5d3SJohn Marino /* special handling for TAG_HEAD */
591*86d7f5d3SJohn Marino if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
592*86d7f5d3SJohn Marino {
593*86d7f5d3SJohn Marino char *head =
594*86d7f5d3SJohn Marino (vers->vn_rcs == NULL
595*86d7f5d3SJohn Marino ? NULL
596*86d7f5d3SJohn Marino : RCS_branch_head (vers->srcfile, vers->vn_rcs));
597*86d7f5d3SJohn Marino exists = head && !RCS_isdead (vers->srcfile, head);
598*86d7f5d3SJohn Marino if (head != NULL)
599*86d7f5d3SJohn Marino free (head);
600*86d7f5d3SJohn Marino }
601*86d7f5d3SJohn Marino else
602*86d7f5d3SJohn Marino {
603*86d7f5d3SJohn Marino Vers_TS *xvers;
604*86d7f5d3SJohn Marino
605*86d7f5d3SJohn Marino xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
606*86d7f5d3SJohn Marino 1, 0);
607*86d7f5d3SJohn Marino exists = xvers->vn_rcs
608*86d7f5d3SJohn Marino && !RCS_isdead (xvers->srcfile, xvers->vn_rcs);
609*86d7f5d3SJohn Marino freevers_ts (&xvers);
610*86d7f5d3SJohn Marino }
611*86d7f5d3SJohn Marino }
612*86d7f5d3SJohn Marino else
613*86d7f5d3SJohn Marino {
614*86d7f5d3SJohn Marino /* The file was added locally, but an RCS archive exists. Our
615*86d7f5d3SJohn Marino * base revision must be dead.
616*86d7f5d3SJohn Marino */
617*86d7f5d3SJohn Marino /* No need to set, exists = 0, here. That's the default. */
618*86d7f5d3SJohn Marino }
619*86d7f5d3SJohn Marino }
620*86d7f5d3SJohn Marino if (!exists)
621*86d7f5d3SJohn Marino {
622*86d7f5d3SJohn Marino /* If we got here, then either the RCS archive does not exist or
623*86d7f5d3SJohn Marino * the relevant revision is dead.
624*86d7f5d3SJohn Marino */
625*86d7f5d3SJohn Marino if (empty_files)
626*86d7f5d3SJohn Marino empty_file = DIFF_ADDED;
627*86d7f5d3SJohn Marino else
628*86d7f5d3SJohn Marino {
629*86d7f5d3SJohn Marino error (0, 0, "%s is a new entry, no comparison available",
630*86d7f5d3SJohn Marino finfo->fullname);
631*86d7f5d3SJohn Marino goto out;
632*86d7f5d3SJohn Marino }
633*86d7f5d3SJohn Marino }
634*86d7f5d3SJohn Marino }
635*86d7f5d3SJohn Marino else if (vers->vn_user[0] == '-')
636*86d7f5d3SJohn Marino {
637*86d7f5d3SJohn Marino if (empty_files)
638*86d7f5d3SJohn Marino empty_file = DIFF_REMOVED;
639*86d7f5d3SJohn Marino else
640*86d7f5d3SJohn Marino {
641*86d7f5d3SJohn Marino error (0, 0, "%s was removed, no comparison available",
642*86d7f5d3SJohn Marino finfo->fullname);
643*86d7f5d3SJohn Marino goto out;
644*86d7f5d3SJohn Marino }
645*86d7f5d3SJohn Marino }
646*86d7f5d3SJohn Marino else
647*86d7f5d3SJohn Marino {
648*86d7f5d3SJohn Marino if (!vers->vn_rcs && !vers->srcfile)
649*86d7f5d3SJohn Marino {
650*86d7f5d3SJohn Marino error (0, 0, "cannot find revision control file for %s",
651*86d7f5d3SJohn Marino finfo->fullname);
652*86d7f5d3SJohn Marino goto out;
653*86d7f5d3SJohn Marino }
654*86d7f5d3SJohn Marino else
655*86d7f5d3SJohn Marino {
656*86d7f5d3SJohn Marino if (vers->ts_user == NULL)
657*86d7f5d3SJohn Marino {
658*86d7f5d3SJohn Marino error (0, 0, "cannot find %s", finfo->fullname);
659*86d7f5d3SJohn Marino goto out;
660*86d7f5d3SJohn Marino }
661*86d7f5d3SJohn Marino else if (!strcmp (vers->ts_user, vers->ts_rcs))
662*86d7f5d3SJohn Marino {
663*86d7f5d3SJohn Marino /* The user file matches some revision in the repository
664*86d7f5d3SJohn Marino Diff against the repository (for remote CVS, we might not
665*86d7f5d3SJohn Marino have a copy of the user file around). */
666*86d7f5d3SJohn Marino user_file_rev = vers->vn_user;
667*86d7f5d3SJohn Marino }
668*86d7f5d3SJohn Marino }
669*86d7f5d3SJohn Marino }
670*86d7f5d3SJohn Marino
671*86d7f5d3SJohn Marino empty_file = diff_file_nodiff (finfo, vers, empty_file, &rev1_cache);
672*86d7f5d3SJohn Marino if (empty_file == DIFF_SAME)
673*86d7f5d3SJohn Marino {
674*86d7f5d3SJohn Marino /* In the server case, would be nice to send a "Checked-in"
675*86d7f5d3SJohn Marino response, so that the client can rewrite its timestamp.
676*86d7f5d3SJohn Marino server_checked_in by itself isn't the right thing (it
677*86d7f5d3SJohn Marino needs a server_register), but I'm not sure what is.
678*86d7f5d3SJohn Marino It isn't clear to me how "cvs status" handles this (that
679*86d7f5d3SJohn Marino is, for a client which sends Modified not Is-modified to
680*86d7f5d3SJohn Marino "cvs status"), but it does. */
681*86d7f5d3SJohn Marino err = 0;
682*86d7f5d3SJohn Marino goto out;
683*86d7f5d3SJohn Marino }
684*86d7f5d3SJohn Marino else if (empty_file == DIFF_ERROR)
685*86d7f5d3SJohn Marino goto out;
686*86d7f5d3SJohn Marino
687*86d7f5d3SJohn Marino /* Output an "Index:" line for patch to use */
688*86d7f5d3SJohn Marino cvs_output ("Index: ", 0);
689*86d7f5d3SJohn Marino cvs_output (finfo->fullname, 0);
690*86d7f5d3SJohn Marino cvs_output ("\n", 1);
691*86d7f5d3SJohn Marino
692*86d7f5d3SJohn Marino tocvsPath = wrap_tocvs_process_file (finfo->file);
693*86d7f5d3SJohn Marino if (tocvsPath)
694*86d7f5d3SJohn Marino {
695*86d7f5d3SJohn Marino /* Backup the current version of the file to CVS/,,filename */
696*86d7f5d3SJohn Marino fname = Xasprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
697*86d7f5d3SJohn Marino if (unlink_file_dir (fname) < 0)
698*86d7f5d3SJohn Marino if (!existence_error (errno))
699*86d7f5d3SJohn Marino error (1, errno, "cannot remove %s", fname);
700*86d7f5d3SJohn Marino rename_file (finfo->file, fname);
701*86d7f5d3SJohn Marino /* Copy the wrapped file to the current directory then go to work */
702*86d7f5d3SJohn Marino copy_file (tocvsPath, finfo->file);
703*86d7f5d3SJohn Marino }
704*86d7f5d3SJohn Marino
705*86d7f5d3SJohn Marino /* Set up file labels appropriate for compatibility with the Larry Wall
706*86d7f5d3SJohn Marino * implementation of patch if the user didn't specify. This is irrelevant
707*86d7f5d3SJohn Marino * according to the POSIX.2 specification.
708*86d7f5d3SJohn Marino */
709*86d7f5d3SJohn Marino label1 = NULL;
710*86d7f5d3SJohn Marino label2 = NULL;
711*86d7f5d3SJohn Marino /* The user cannot set the rev2 label without first setting the rev1
712*86d7f5d3SJohn Marino * label.
713*86d7f5d3SJohn Marino */
714*86d7f5d3SJohn Marino if (!have_rev2_label)
715*86d7f5d3SJohn Marino {
716*86d7f5d3SJohn Marino if (empty_file == DIFF_REMOVED)
717*86d7f5d3SJohn Marino label2 = make_file_label (DEVNULL, NULL, NULL);
718*86d7f5d3SJohn Marino else
719*86d7f5d3SJohn Marino label2 = make_file_label (finfo->fullname, use_rev2,
720*86d7f5d3SJohn Marino vers->srcfile);
721*86d7f5d3SJohn Marino if (!have_rev1_label)
722*86d7f5d3SJohn Marino {
723*86d7f5d3SJohn Marino if (empty_file == DIFF_ADDED)
724*86d7f5d3SJohn Marino label1 = make_file_label (DEVNULL, NULL, NULL);
725*86d7f5d3SJohn Marino else
726*86d7f5d3SJohn Marino label1 = make_file_label (finfo->fullname, use_rev1,
727*86d7f5d3SJohn Marino vers->srcfile);
728*86d7f5d3SJohn Marino }
729*86d7f5d3SJohn Marino }
730*86d7f5d3SJohn Marino
731*86d7f5d3SJohn Marino if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
732*86d7f5d3SJohn Marino {
733*86d7f5d3SJohn Marino /* This is fullname, not file, possibly despite the POSIX.2
734*86d7f5d3SJohn Marino * specification, because that's the way all the Larry Wall
735*86d7f5d3SJohn Marino * implementations of patch (are there other implementations?) want
736*86d7f5d3SJohn Marino * things and the POSIX.2 spec appears to leave room for this.
737*86d7f5d3SJohn Marino */
738*86d7f5d3SJohn Marino cvs_output ("\
739*86d7f5d3SJohn Marino ===================================================================\n\
740*86d7f5d3SJohn Marino RCS file: ", 0);
741*86d7f5d3SJohn Marino cvs_output (finfo->fullname, 0);
742*86d7f5d3SJohn Marino cvs_output ("\n", 1);
743*86d7f5d3SJohn Marino
744*86d7f5d3SJohn Marino cvs_output ("diff -N ", 0);
745*86d7f5d3SJohn Marino cvs_output (finfo->fullname, 0);
746*86d7f5d3SJohn Marino cvs_output ("\n", 1);
747*86d7f5d3SJohn Marino
748*86d7f5d3SJohn Marino if (empty_file == DIFF_ADDED)
749*86d7f5d3SJohn Marino {
750*86d7f5d3SJohn Marino if (use_rev2 == NULL)
751*86d7f5d3SJohn Marino status = diff_exec (DEVNULL, finfo->file, label1, label2,
752*86d7f5d3SJohn Marino diff_argc, diff_argv, RUN_TTY);
753*86d7f5d3SJohn Marino else
754*86d7f5d3SJohn Marino {
755*86d7f5d3SJohn Marino int retcode;
756*86d7f5d3SJohn Marino
757*86d7f5d3SJohn Marino tmp = cvs_temp_name ();
758*86d7f5d3SJohn Marino retcode = RCS_checkout (vers->srcfile, NULL, use_rev2, NULL,
759*86d7f5d3SJohn Marino *options ? options : vers->options,
760*86d7f5d3SJohn Marino tmp, NULL, NULL);
761*86d7f5d3SJohn Marino if (retcode != 0)
762*86d7f5d3SJohn Marino goto out;
763*86d7f5d3SJohn Marino
764*86d7f5d3SJohn Marino status = diff_exec (DEVNULL, tmp, label1, label2,
765*86d7f5d3SJohn Marino diff_argc, diff_argv, RUN_TTY);
766*86d7f5d3SJohn Marino }
767*86d7f5d3SJohn Marino }
768*86d7f5d3SJohn Marino else
769*86d7f5d3SJohn Marino {
770*86d7f5d3SJohn Marino int retcode;
771*86d7f5d3SJohn Marino
772*86d7f5d3SJohn Marino tmp = cvs_temp_name ();
773*86d7f5d3SJohn Marino retcode = RCS_checkout (vers->srcfile, NULL, use_rev1, NULL,
774*86d7f5d3SJohn Marino *options ? options : vers->options,
775*86d7f5d3SJohn Marino tmp, NULL, NULL);
776*86d7f5d3SJohn Marino if (retcode != 0)
777*86d7f5d3SJohn Marino goto out;
778*86d7f5d3SJohn Marino
779*86d7f5d3SJohn Marino status = diff_exec (tmp, DEVNULL, label1, label2,
780*86d7f5d3SJohn Marino diff_argc, diff_argv, RUN_TTY);
781*86d7f5d3SJohn Marino }
782*86d7f5d3SJohn Marino }
783*86d7f5d3SJohn Marino else
784*86d7f5d3SJohn Marino {
785*86d7f5d3SJohn Marino status = RCS_exec_rcsdiff (vers->srcfile, diff_argc, diff_argv,
786*86d7f5d3SJohn Marino *options ? options : vers->options,
787*86d7f5d3SJohn Marino use_rev1, rev1_cache, use_rev2,
788*86d7f5d3SJohn Marino label1, label2, finfo->file);
789*86d7f5d3SJohn Marino
790*86d7f5d3SJohn Marino }
791*86d7f5d3SJohn Marino
792*86d7f5d3SJohn Marino if (label1) free (label1);
793*86d7f5d3SJohn Marino if (label2) free (label2);
794*86d7f5d3SJohn Marino
795*86d7f5d3SJohn Marino switch (status)
796*86d7f5d3SJohn Marino {
797*86d7f5d3SJohn Marino case -1: /* fork failed */
798*86d7f5d3SJohn Marino error (1, errno, "fork failed while diffing %s",
799*86d7f5d3SJohn Marino vers->srcfile->path);
800*86d7f5d3SJohn Marino case 0: /* everything ok */
801*86d7f5d3SJohn Marino err = 0;
802*86d7f5d3SJohn Marino break;
803*86d7f5d3SJohn Marino default: /* other error */
804*86d7f5d3SJohn Marino err = status;
805*86d7f5d3SJohn Marino break;
806*86d7f5d3SJohn Marino }
807*86d7f5d3SJohn Marino
808*86d7f5d3SJohn Marino out:
809*86d7f5d3SJohn Marino if( tocvsPath != NULL )
810*86d7f5d3SJohn Marino {
811*86d7f5d3SJohn Marino if (unlink_file_dir (finfo->file) < 0)
812*86d7f5d3SJohn Marino if (! existence_error (errno))
813*86d7f5d3SJohn Marino error (1, errno, "cannot remove %s", finfo->file);
814*86d7f5d3SJohn Marino
815*86d7f5d3SJohn Marino rename_file (fname, finfo->file);
816*86d7f5d3SJohn Marino if (unlink_file (tocvsPath) < 0)
817*86d7f5d3SJohn Marino error (1, errno, "cannot remove %s", tocvsPath);
818*86d7f5d3SJohn Marino free (fname);
819*86d7f5d3SJohn Marino }
820*86d7f5d3SJohn Marino
821*86d7f5d3SJohn Marino /* Call CVS_UNLINK() rather than unlink_file() below to avoid the check
822*86d7f5d3SJohn Marino * for noexec.
823*86d7f5d3SJohn Marino */
824*86d7f5d3SJohn Marino if (tmp != NULL)
825*86d7f5d3SJohn Marino {
826*86d7f5d3SJohn Marino if (CVS_UNLINK (tmp) < 0)
827*86d7f5d3SJohn Marino error (0, errno, "cannot remove %s", tmp);
828*86d7f5d3SJohn Marino free (tmp);
829*86d7f5d3SJohn Marino }
830*86d7f5d3SJohn Marino if (rev1_cache != NULL)
831*86d7f5d3SJohn Marino {
832*86d7f5d3SJohn Marino if (CVS_UNLINK (rev1_cache) < 0)
833*86d7f5d3SJohn Marino error (0, errno, "cannot remove %s", rev1_cache);
834*86d7f5d3SJohn Marino free (rev1_cache);
835*86d7f5d3SJohn Marino }
836*86d7f5d3SJohn Marino
837*86d7f5d3SJohn Marino freevers_ts (&vers);
838*86d7f5d3SJohn Marino diff_mark_errors (err);
839*86d7f5d3SJohn Marino return err;
840*86d7f5d3SJohn Marino }
841*86d7f5d3SJohn Marino
842*86d7f5d3SJohn Marino
843*86d7f5d3SJohn Marino
844*86d7f5d3SJohn Marino /*
845*86d7f5d3SJohn Marino * Remember the exit status for each file.
846*86d7f5d3SJohn Marino */
847*86d7f5d3SJohn Marino static void
diff_mark_errors(int err)848*86d7f5d3SJohn Marino diff_mark_errors (int err)
849*86d7f5d3SJohn Marino {
850*86d7f5d3SJohn Marino if (err > diff_errors)
851*86d7f5d3SJohn Marino diff_errors = err;
852*86d7f5d3SJohn Marino }
853*86d7f5d3SJohn Marino
854*86d7f5d3SJohn Marino
855*86d7f5d3SJohn Marino
856*86d7f5d3SJohn Marino /*
857*86d7f5d3SJohn Marino * Print a warm fuzzy message when we enter a dir
858*86d7f5d3SJohn Marino *
859*86d7f5d3SJohn Marino * Don't try to diff directories that don't exist! -- DW
860*86d7f5d3SJohn Marino */
861*86d7f5d3SJohn Marino /* ARGSUSED */
862*86d7f5d3SJohn Marino static Dtype
diff_dirproc(void * callerdat,const char * dir,const char * pos_repos,const char * update_dir,List * entries)863*86d7f5d3SJohn Marino diff_dirproc (void *callerdat, const char *dir, const char *pos_repos,
864*86d7f5d3SJohn Marino const char *update_dir, List *entries)
865*86d7f5d3SJohn Marino {
866*86d7f5d3SJohn Marino /* XXX - check for dirs we don't want to process??? */
867*86d7f5d3SJohn Marino
868*86d7f5d3SJohn Marino /* YES ... for instance dirs that don't exist!!! -- DW */
869*86d7f5d3SJohn Marino if (!isdir (dir))
870*86d7f5d3SJohn Marino return R_SKIP_ALL;
871*86d7f5d3SJohn Marino
872*86d7f5d3SJohn Marino if (!quiet)
873*86d7f5d3SJohn Marino error (0, 0, "Diffing %s", update_dir);
874*86d7f5d3SJohn Marino return R_PROCESS;
875*86d7f5d3SJohn Marino }
876*86d7f5d3SJohn Marino
877*86d7f5d3SJohn Marino
878*86d7f5d3SJohn Marino
879*86d7f5d3SJohn Marino /*
880*86d7f5d3SJohn Marino * Concoct the proper exit status - done with files
881*86d7f5d3SJohn Marino */
882*86d7f5d3SJohn Marino /* ARGSUSED */
883*86d7f5d3SJohn Marino static int
diff_filesdoneproc(void * callerdat,int err,const char * repos,const char * update_dir,List * entries)884*86d7f5d3SJohn Marino diff_filesdoneproc (void *callerdat, int err, const char *repos,
885*86d7f5d3SJohn Marino const char *update_dir, List *entries)
886*86d7f5d3SJohn Marino {
887*86d7f5d3SJohn Marino return diff_errors;
888*86d7f5d3SJohn Marino }
889*86d7f5d3SJohn Marino
890*86d7f5d3SJohn Marino
891*86d7f5d3SJohn Marino
892*86d7f5d3SJohn Marino /*
893*86d7f5d3SJohn Marino * Concoct the proper exit status - leaving directories
894*86d7f5d3SJohn Marino */
895*86d7f5d3SJohn Marino /* ARGSUSED */
896*86d7f5d3SJohn Marino static int
diff_dirleaveproc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)897*86d7f5d3SJohn Marino diff_dirleaveproc (void *callerdat, const char *dir, int err,
898*86d7f5d3SJohn Marino const char *update_dir, List *entries)
899*86d7f5d3SJohn Marino {
900*86d7f5d3SJohn Marino return diff_errors;
901*86d7f5d3SJohn Marino }
902*86d7f5d3SJohn Marino
903*86d7f5d3SJohn Marino
904*86d7f5d3SJohn Marino
905*86d7f5d3SJohn Marino /*
906*86d7f5d3SJohn Marino * verify that a file is different
907*86d7f5d3SJohn Marino *
908*86d7f5d3SJohn Marino * INPUTS
909*86d7f5d3SJohn Marino * finfo
910*86d7f5d3SJohn Marino * vers
911*86d7f5d3SJohn Marino * empty_file
912*86d7f5d3SJohn Marino *
913*86d7f5d3SJohn Marino * OUTPUTS
914*86d7f5d3SJohn Marino * rev1_cache Cache the contents of rev1 if we look it up.
915*86d7f5d3SJohn Marino */
916*86d7f5d3SJohn Marino static enum diff_file
diff_file_nodiff(struct file_info * finfo,Vers_TS * vers,enum diff_file empty_file,char ** rev1_cache)917*86d7f5d3SJohn Marino diff_file_nodiff (struct file_info *finfo, Vers_TS *vers,
918*86d7f5d3SJohn Marino enum diff_file empty_file, char **rev1_cache)
919*86d7f5d3SJohn Marino {
920*86d7f5d3SJohn Marino Vers_TS *xvers;
921*86d7f5d3SJohn Marino int retcode;
922*86d7f5d3SJohn Marino
923*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "diff_file_nodiff (%s, %d)",
924*86d7f5d3SJohn Marino finfo->fullname ? finfo->fullname : "(null)", empty_file);
925*86d7f5d3SJohn Marino
926*86d7f5d3SJohn Marino /* free up any old use_rev* variables and reset 'em */
927*86d7f5d3SJohn Marino if (use_rev1)
928*86d7f5d3SJohn Marino free (use_rev1);
929*86d7f5d3SJohn Marino if (use_rev2)
930*86d7f5d3SJohn Marino free (use_rev2);
931*86d7f5d3SJohn Marino use_rev1 = use_rev2 = NULL;
932*86d7f5d3SJohn Marino
933*86d7f5d3SJohn Marino if (diff_rev1 || diff_date1)
934*86d7f5d3SJohn Marino {
935*86d7f5d3SJohn Marino /* special handling for TAG_HEAD */
936*86d7f5d3SJohn Marino if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
937*86d7f5d3SJohn Marino {
938*86d7f5d3SJohn Marino if (vers->vn_rcs != NULL && vers->srcfile != NULL)
939*86d7f5d3SJohn Marino use_rev1 = RCS_branch_head (vers->srcfile, vers->vn_rcs);
940*86d7f5d3SJohn Marino }
941*86d7f5d3SJohn Marino else
942*86d7f5d3SJohn Marino {
943*86d7f5d3SJohn Marino xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0);
944*86d7f5d3SJohn Marino if (xvers->vn_rcs != NULL)
945*86d7f5d3SJohn Marino use_rev1 = xstrdup (xvers->vn_rcs);
946*86d7f5d3SJohn Marino freevers_ts (&xvers);
947*86d7f5d3SJohn Marino }
948*86d7f5d3SJohn Marino }
949*86d7f5d3SJohn Marino if (diff_rev2 || diff_date2)
950*86d7f5d3SJohn Marino {
951*86d7f5d3SJohn Marino /* special handling for TAG_HEAD */
952*86d7f5d3SJohn Marino if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
953*86d7f5d3SJohn Marino {
954*86d7f5d3SJohn Marino if (vers->vn_rcs && vers->srcfile)
955*86d7f5d3SJohn Marino use_rev2 = RCS_branch_head (vers->srcfile, vers->vn_rcs);
956*86d7f5d3SJohn Marino }
957*86d7f5d3SJohn Marino else
958*86d7f5d3SJohn Marino {
959*86d7f5d3SJohn Marino xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0);
960*86d7f5d3SJohn Marino if (xvers->vn_rcs != NULL)
961*86d7f5d3SJohn Marino use_rev2 = xstrdup (xvers->vn_rcs);
962*86d7f5d3SJohn Marino freevers_ts (&xvers);
963*86d7f5d3SJohn Marino }
964*86d7f5d3SJohn Marino
965*86d7f5d3SJohn Marino if (use_rev1 == NULL || RCS_isdead (vers->srcfile, use_rev1))
966*86d7f5d3SJohn Marino {
967*86d7f5d3SJohn Marino /* The first revision does not exist. If EMPTY_FILES is
968*86d7f5d3SJohn Marino true, treat this as an added file. Otherwise, warn
969*86d7f5d3SJohn Marino about the missing tag. */
970*86d7f5d3SJohn Marino if (use_rev2 == NULL || RCS_isdead (vers->srcfile, use_rev2))
971*86d7f5d3SJohn Marino /* At least in the case where DIFF_REV1 and DIFF_REV2
972*86d7f5d3SJohn Marino * are both numeric (and non-existant (NULL), as opposed to
973*86d7f5d3SJohn Marino * dead?), we should be returning some kind of error (see
974*86d7f5d3SJohn Marino * basicb-8a0 in testsuite). The symbolic case may be more
975*86d7f5d3SJohn Marino * complicated.
976*86d7f5d3SJohn Marino */
977*86d7f5d3SJohn Marino return DIFF_SAME;
978*86d7f5d3SJohn Marino if (empty_files)
979*86d7f5d3SJohn Marino return DIFF_ADDED;
980*86d7f5d3SJohn Marino if (use_rev1 != NULL)
981*86d7f5d3SJohn Marino {
982*86d7f5d3SJohn Marino if (diff_rev1)
983*86d7f5d3SJohn Marino {
984*86d7f5d3SJohn Marino error (0, 0,
985*86d7f5d3SJohn Marino "Tag %s refers to a dead (removed) revision in file `%s'.",
986*86d7f5d3SJohn Marino diff_rev1, finfo->fullname);
987*86d7f5d3SJohn Marino }
988*86d7f5d3SJohn Marino else
989*86d7f5d3SJohn Marino {
990*86d7f5d3SJohn Marino error (0, 0,
991*86d7f5d3SJohn Marino "Date %s refers to a dead (removed) revision in file `%s'.",
992*86d7f5d3SJohn Marino diff_date1, finfo->fullname);
993*86d7f5d3SJohn Marino }
994*86d7f5d3SJohn Marino error (0, 0,
995*86d7f5d3SJohn Marino "No comparison available. Pass `-N' to `%s diff'?",
996*86d7f5d3SJohn Marino program_name);
997*86d7f5d3SJohn Marino }
998*86d7f5d3SJohn Marino else if (diff_rev1)
999*86d7f5d3SJohn Marino error (0, 0, "tag %s is not in file %s", diff_rev1,
1000*86d7f5d3SJohn Marino finfo->fullname);
1001*86d7f5d3SJohn Marino else
1002*86d7f5d3SJohn Marino error (0, 0, "no revision for date %s in file %s",
1003*86d7f5d3SJohn Marino diff_date1, finfo->fullname);
1004*86d7f5d3SJohn Marino return DIFF_ERROR;
1005*86d7f5d3SJohn Marino }
1006*86d7f5d3SJohn Marino
1007*86d7f5d3SJohn Marino assert( use_rev1 != NULL );
1008*86d7f5d3SJohn Marino if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
1009*86d7f5d3SJohn Marino {
1010*86d7f5d3SJohn Marino /* The second revision does not exist. If EMPTY_FILES is
1011*86d7f5d3SJohn Marino true, treat this as a removed file. Otherwise warn
1012*86d7f5d3SJohn Marino about the missing tag. */
1013*86d7f5d3SJohn Marino if (empty_files)
1014*86d7f5d3SJohn Marino return DIFF_REMOVED;
1015*86d7f5d3SJohn Marino if( use_rev2 != NULL )
1016*86d7f5d3SJohn Marino {
1017*86d7f5d3SJohn Marino if (diff_rev2)
1018*86d7f5d3SJohn Marino {
1019*86d7f5d3SJohn Marino error( 0, 0,
1020*86d7f5d3SJohn Marino "Tag %s refers to a dead (removed) revision in file `%s'.",
1021*86d7f5d3SJohn Marino diff_rev2, finfo->fullname );
1022*86d7f5d3SJohn Marino }
1023*86d7f5d3SJohn Marino else
1024*86d7f5d3SJohn Marino {
1025*86d7f5d3SJohn Marino error( 0, 0,
1026*86d7f5d3SJohn Marino "Date %s refers to a dead (removed) revision in file `%s'.",
1027*86d7f5d3SJohn Marino diff_date2, finfo->fullname );
1028*86d7f5d3SJohn Marino }
1029*86d7f5d3SJohn Marino error( 0, 0,
1030*86d7f5d3SJohn Marino "No comparison available. Pass `-N' to `%s diff'?",
1031*86d7f5d3SJohn Marino program_name );
1032*86d7f5d3SJohn Marino }
1033*86d7f5d3SJohn Marino else if (diff_rev2)
1034*86d7f5d3SJohn Marino error (0, 0, "tag %s is not in file %s", diff_rev2,
1035*86d7f5d3SJohn Marino finfo->fullname);
1036*86d7f5d3SJohn Marino else
1037*86d7f5d3SJohn Marino error (0, 0, "no revision for date %s in file %s",
1038*86d7f5d3SJohn Marino diff_date2, finfo->fullname);
1039*86d7f5d3SJohn Marino return DIFF_ERROR;
1040*86d7f5d3SJohn Marino }
1041*86d7f5d3SJohn Marino /* Now, see if we really need to do the diff. We can't assume that the
1042*86d7f5d3SJohn Marino * files are different when the revs are.
1043*86d7f5d3SJohn Marino */
1044*86d7f5d3SJohn Marino assert( use_rev2 != NULL );
1045*86d7f5d3SJohn Marino if( strcmp (use_rev1, use_rev2) == 0 )
1046*86d7f5d3SJohn Marino return DIFF_SAME;
1047*86d7f5d3SJohn Marino /* else fall through and do the diff */
1048*86d7f5d3SJohn Marino }
1049*86d7f5d3SJohn Marino
1050*86d7f5d3SJohn Marino /* If we had a r1/d1 & r2/d2, then at this point we must have a C3P0...
1051*86d7f5d3SJohn Marino * err... ok, then both rev1 & rev2 must have resolved to an existing,
1052*86d7f5d3SJohn Marino * live version due to if statement we just closed.
1053*86d7f5d3SJohn Marino */
1054*86d7f5d3SJohn Marino assert (!(diff_rev2 || diff_date2) || (use_rev1 && use_rev2));
1055*86d7f5d3SJohn Marino
1056*86d7f5d3SJohn Marino if ((diff_rev1 || diff_date1) &&
1057*86d7f5d3SJohn Marino (use_rev1 == NULL || RCS_isdead (vers->srcfile, use_rev1)))
1058*86d7f5d3SJohn Marino {
1059*86d7f5d3SJohn Marino /* The first revision does not exist, and no second revision
1060*86d7f5d3SJohn Marino was given. */
1061*86d7f5d3SJohn Marino if (empty_files)
1062*86d7f5d3SJohn Marino {
1063*86d7f5d3SJohn Marino if (empty_file == DIFF_REMOVED)
1064*86d7f5d3SJohn Marino return DIFF_SAME;
1065*86d7f5d3SJohn Marino if( user_file_rev && use_rev2 == NULL )
1066*86d7f5d3SJohn Marino use_rev2 = xstrdup( user_file_rev );
1067*86d7f5d3SJohn Marino return DIFF_ADDED;
1068*86d7f5d3SJohn Marino }
1069*86d7f5d3SJohn Marino if( use_rev1 != NULL )
1070*86d7f5d3SJohn Marino {
1071*86d7f5d3SJohn Marino if (diff_rev1)
1072*86d7f5d3SJohn Marino {
1073*86d7f5d3SJohn Marino error( 0, 0,
1074*86d7f5d3SJohn Marino "Tag %s refers to a dead (removed) revision in file `%s'.",
1075*86d7f5d3SJohn Marino diff_rev1, finfo->fullname );
1076*86d7f5d3SJohn Marino }
1077*86d7f5d3SJohn Marino else
1078*86d7f5d3SJohn Marino {
1079*86d7f5d3SJohn Marino error( 0, 0,
1080*86d7f5d3SJohn Marino "Date %s refers to a dead (removed) revision in file `%s'.",
1081*86d7f5d3SJohn Marino diff_date1, finfo->fullname );
1082*86d7f5d3SJohn Marino }
1083*86d7f5d3SJohn Marino error( 0, 0,
1084*86d7f5d3SJohn Marino "No comparison available. Pass `-N' to `%s diff'?",
1085*86d7f5d3SJohn Marino program_name );
1086*86d7f5d3SJohn Marino }
1087*86d7f5d3SJohn Marino else if ( diff_rev1 )
1088*86d7f5d3SJohn Marino error( 0, 0, "tag %s is not in file %s", diff_rev1,
1089*86d7f5d3SJohn Marino finfo->fullname );
1090*86d7f5d3SJohn Marino else
1091*86d7f5d3SJohn Marino error( 0, 0, "no revision for date %s in file %s",
1092*86d7f5d3SJohn Marino diff_date1, finfo->fullname );
1093*86d7f5d3SJohn Marino return DIFF_ERROR;
1094*86d7f5d3SJohn Marino }
1095*86d7f5d3SJohn Marino
1096*86d7f5d3SJohn Marino assert( !diff_rev1 || use_rev1 );
1097*86d7f5d3SJohn Marino
1098*86d7f5d3SJohn Marino if (user_file_rev)
1099*86d7f5d3SJohn Marino {
1100*86d7f5d3SJohn Marino /* drop user_file_rev into first unused use_rev */
1101*86d7f5d3SJohn Marino if (!use_rev1)
1102*86d7f5d3SJohn Marino use_rev1 = xstrdup (user_file_rev);
1103*86d7f5d3SJohn Marino else if (!use_rev2)
1104*86d7f5d3SJohn Marino use_rev2 = xstrdup (user_file_rev);
1105*86d7f5d3SJohn Marino /* and if not, it wasn't needed anyhow */
1106*86d7f5d3SJohn Marino user_file_rev = NULL;
1107*86d7f5d3SJohn Marino }
1108*86d7f5d3SJohn Marino
1109*86d7f5d3SJohn Marino /* Now, see if we really need to do the diff. We can't assume that the
1110*86d7f5d3SJohn Marino * files are different when the revs are.
1111*86d7f5d3SJohn Marino */
1112*86d7f5d3SJohn Marino if( use_rev1 && use_rev2)
1113*86d7f5d3SJohn Marino {
1114*86d7f5d3SJohn Marino if (strcmp (use_rev1, use_rev2) == 0)
1115*86d7f5d3SJohn Marino return DIFF_SAME;
1116*86d7f5d3SJohn Marino /* Fall through and do the diff. */
1117*86d7f5d3SJohn Marino }
1118*86d7f5d3SJohn Marino /* Don't want to do the timestamp check with both use_rev1 & use_rev2 set.
1119*86d7f5d3SJohn Marino * The timestamp check is just for the default case of diffing the
1120*86d7f5d3SJohn Marino * workspace file against its base revision.
1121*86d7f5d3SJohn Marino */
1122*86d7f5d3SJohn Marino else if( use_rev1 == NULL
1123*86d7f5d3SJohn Marino || ( vers->vn_user != NULL
1124*86d7f5d3SJohn Marino && strcmp( use_rev1, vers->vn_user ) == 0 ) )
1125*86d7f5d3SJohn Marino {
1126*86d7f5d3SJohn Marino if (empty_file == DIFF_DIFFERENT
1127*86d7f5d3SJohn Marino && vers->ts_user != NULL
1128*86d7f5d3SJohn Marino && strcmp (vers->ts_rcs, vers->ts_user) == 0
1129*86d7f5d3SJohn Marino && (!(*options) || strcmp (options, vers->options) == 0))
1130*86d7f5d3SJohn Marino {
1131*86d7f5d3SJohn Marino return DIFF_SAME;
1132*86d7f5d3SJohn Marino }
1133*86d7f5d3SJohn Marino if (use_rev1 == NULL
1134*86d7f5d3SJohn Marino && (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0'))
1135*86d7f5d3SJohn Marino {
1136*86d7f5d3SJohn Marino if (vers->vn_user[0] == '-')
1137*86d7f5d3SJohn Marino use_rev1 = xstrdup (vers->vn_user + 1);
1138*86d7f5d3SJohn Marino else
1139*86d7f5d3SJohn Marino use_rev1 = xstrdup (vers->vn_user);
1140*86d7f5d3SJohn Marino }
1141*86d7f5d3SJohn Marino }
1142*86d7f5d3SJohn Marino
1143*86d7f5d3SJohn Marino /* If we already know that the file is being added or removed,
1144*86d7f5d3SJohn Marino then we don't want to do an actual file comparison here. */
1145*86d7f5d3SJohn Marino if (empty_file != DIFF_DIFFERENT)
1146*86d7f5d3SJohn Marino return empty_file;
1147*86d7f5d3SJohn Marino
1148*86d7f5d3SJohn Marino /*
1149*86d7f5d3SJohn Marino * Run a quick cmp to see if we should bother with a full diff.
1150*86d7f5d3SJohn Marino */
1151*86d7f5d3SJohn Marino
1152*86d7f5d3SJohn Marino retcode = RCS_cmp_file( vers->srcfile, use_rev1, rev1_cache,
1153*86d7f5d3SJohn Marino use_rev2, *options ? options : vers->options,
1154*86d7f5d3SJohn Marino finfo->file );
1155*86d7f5d3SJohn Marino
1156*86d7f5d3SJohn Marino return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT;
1157*86d7f5d3SJohn Marino }
1158