xref: /dflybsd-src/contrib/cvs-1.12/src/diff.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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