1*86d7f5d3SJohn Marino /* GNU DIFF entry routine.
2*86d7f5d3SJohn Marino Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino
4*86d7f5d3SJohn Marino This file is part of GNU DIFF.
5*86d7f5d3SJohn Marino
6*86d7f5d3SJohn Marino GNU DIFF is free software; you can redistribute it and/or modify
7*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
8*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
9*86d7f5d3SJohn Marino any later version.
10*86d7f5d3SJohn Marino
11*86d7f5d3SJohn Marino GNU DIFF is distributed in the hope that it will be useful,
12*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
13*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*86d7f5d3SJohn Marino GNU General Public License for more details.
15*86d7f5d3SJohn Marino
16*86d7f5d3SJohn Marino */
17*86d7f5d3SJohn Marino
18*86d7f5d3SJohn Marino /* GNU DIFF was written by Mike Haertel, David Hayes,
19*86d7f5d3SJohn Marino Richard Stallman, Len Tower, and Paul Eggert. */
20*86d7f5d3SJohn Marino
21*86d7f5d3SJohn Marino #define GDIFF_MAIN
22*86d7f5d3SJohn Marino #include "diff.h"
23*86d7f5d3SJohn Marino #include <signal.h>
24*86d7f5d3SJohn Marino #include "error.h"
25*86d7f5d3SJohn Marino #include "getopt.h"
26*86d7f5d3SJohn Marino
27*86d7f5d3SJohn Marino #ifdef HAVE_FNMATCH
28*86d7f5d3SJohn Marino # include <fnmatch.h> /* This is supposed to be available on Posix systems */
29*86d7f5d3SJohn Marino #else /* HAVE_FNMATCH */
30*86d7f5d3SJohn Marino # include "fnmatch.h" /* Our substitute */
31*86d7f5d3SJohn Marino #endif /* HAVE_FNMATCH */
32*86d7f5d3SJohn Marino
33*86d7f5d3SJohn Marino #ifndef DEFAULT_WIDTH
34*86d7f5d3SJohn Marino #define DEFAULT_WIDTH 130
35*86d7f5d3SJohn Marino #endif
36*86d7f5d3SJohn Marino
37*86d7f5d3SJohn Marino #ifndef GUTTER_WIDTH_MINIMUM
38*86d7f5d3SJohn Marino #define GUTTER_WIDTH_MINIMUM 3
39*86d7f5d3SJohn Marino #endif
40*86d7f5d3SJohn Marino
41*86d7f5d3SJohn Marino /* diff.c has a real initialize_main function. */
42*86d7f5d3SJohn Marino #ifdef initialize_main
43*86d7f5d3SJohn Marino #undef initialize_main
44*86d7f5d3SJohn Marino #endif
45*86d7f5d3SJohn Marino
46*86d7f5d3SJohn Marino static char const *filetype PARAMS((struct stat const *));
47*86d7f5d3SJohn Marino static char *option_list PARAMS((char **, int));
48*86d7f5d3SJohn Marino static int add_exclude_file PARAMS((char const *));
49*86d7f5d3SJohn Marino static int ck_atoi PARAMS((char const *, int *));
50*86d7f5d3SJohn Marino static int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
51*86d7f5d3SJohn Marino static int specify_format PARAMS((char **, char *));
52*86d7f5d3SJohn Marino static void add_exclude PARAMS((char const *));
53*86d7f5d3SJohn Marino static void add_regexp PARAMS((struct regexp_list **, char const *));
54*86d7f5d3SJohn Marino static void specify_style PARAMS((enum output_style));
55*86d7f5d3SJohn Marino static int try_help PARAMS((char const *));
56*86d7f5d3SJohn Marino static void check_output PARAMS((FILE *));
57*86d7f5d3SJohn Marino static void usage PARAMS((void));
58*86d7f5d3SJohn Marino static void initialize_main PARAMS((int *, char ***));
59*86d7f5d3SJohn Marino
60*86d7f5d3SJohn Marino /* Nonzero for -r: if comparing two directories,
61*86d7f5d3SJohn Marino compare their common subdirectories recursively. */
62*86d7f5d3SJohn Marino
63*86d7f5d3SJohn Marino static int recursive;
64*86d7f5d3SJohn Marino
65*86d7f5d3SJohn Marino /* For debugging: don't do discard_confusing_lines. */
66*86d7f5d3SJohn Marino
67*86d7f5d3SJohn Marino int no_discards;
68*86d7f5d3SJohn Marino
69*86d7f5d3SJohn Marino #if HAVE_SETMODE
70*86d7f5d3SJohn Marino /* I/O mode: nonzero only if using binary input/output. */
71*86d7f5d3SJohn Marino static int binary_I_O;
72*86d7f5d3SJohn Marino #endif
73*86d7f5d3SJohn Marino
74*86d7f5d3SJohn Marino /* Return a string containing the command options with which diff was invoked.
75*86d7f5d3SJohn Marino Spaces appear between what were separate ARGV-elements.
76*86d7f5d3SJohn Marino There is a space at the beginning but none at the end.
77*86d7f5d3SJohn Marino If there were no options, the result is an empty string.
78*86d7f5d3SJohn Marino
79*86d7f5d3SJohn Marino Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
80*86d7f5d3SJohn Marino the length of that vector. */
81*86d7f5d3SJohn Marino
82*86d7f5d3SJohn Marino static char *
option_list(optionvec,count)83*86d7f5d3SJohn Marino option_list (optionvec, count)
84*86d7f5d3SJohn Marino char **optionvec; /* Was `vector', but that collides on Alliant. */
85*86d7f5d3SJohn Marino int count;
86*86d7f5d3SJohn Marino {
87*86d7f5d3SJohn Marino int i;
88*86d7f5d3SJohn Marino size_t length = 0;
89*86d7f5d3SJohn Marino char *result;
90*86d7f5d3SJohn Marino
91*86d7f5d3SJohn Marino for (i = 0; i < count; i++)
92*86d7f5d3SJohn Marino length += strlen (optionvec[i]) + 1;
93*86d7f5d3SJohn Marino
94*86d7f5d3SJohn Marino result = xmalloc (length + 1);
95*86d7f5d3SJohn Marino result[0] = 0;
96*86d7f5d3SJohn Marino
97*86d7f5d3SJohn Marino for (i = 0; i < count; i++)
98*86d7f5d3SJohn Marino {
99*86d7f5d3SJohn Marino strcat (result, " ");
100*86d7f5d3SJohn Marino strcat (result, optionvec[i]);
101*86d7f5d3SJohn Marino }
102*86d7f5d3SJohn Marino
103*86d7f5d3SJohn Marino return result;
104*86d7f5d3SJohn Marino }
105*86d7f5d3SJohn Marino
106*86d7f5d3SJohn Marino /* Convert STR to a positive integer, storing the result in *OUT.
107*86d7f5d3SJohn Marino If STR is not a valid integer, return -1 (otherwise 0). */
108*86d7f5d3SJohn Marino static int
ck_atoi(str,out)109*86d7f5d3SJohn Marino ck_atoi (str, out)
110*86d7f5d3SJohn Marino char const *str;
111*86d7f5d3SJohn Marino int *out;
112*86d7f5d3SJohn Marino {
113*86d7f5d3SJohn Marino char const *p;
114*86d7f5d3SJohn Marino for (p = str; *p; p++)
115*86d7f5d3SJohn Marino if (*p < '0' || *p > '9')
116*86d7f5d3SJohn Marino return -1;
117*86d7f5d3SJohn Marino
118*86d7f5d3SJohn Marino *out = atoi (optarg);
119*86d7f5d3SJohn Marino return 0;
120*86d7f5d3SJohn Marino }
121*86d7f5d3SJohn Marino
122*86d7f5d3SJohn Marino /* Keep track of excluded file name patterns. */
123*86d7f5d3SJohn Marino
124*86d7f5d3SJohn Marino static char const **exclude;
125*86d7f5d3SJohn Marino static int exclude_alloc, exclude_count;
126*86d7f5d3SJohn Marino
127*86d7f5d3SJohn Marino int
excluded_filename(f)128*86d7f5d3SJohn Marino excluded_filename (f)
129*86d7f5d3SJohn Marino char const *f;
130*86d7f5d3SJohn Marino {
131*86d7f5d3SJohn Marino int i;
132*86d7f5d3SJohn Marino for (i = 0; i < exclude_count; i++)
133*86d7f5d3SJohn Marino if (fnmatch (exclude[i], f, 0) == 0)
134*86d7f5d3SJohn Marino return 1;
135*86d7f5d3SJohn Marino return 0;
136*86d7f5d3SJohn Marino }
137*86d7f5d3SJohn Marino
138*86d7f5d3SJohn Marino static void
add_exclude(pattern)139*86d7f5d3SJohn Marino add_exclude (pattern)
140*86d7f5d3SJohn Marino char const *pattern;
141*86d7f5d3SJohn Marino {
142*86d7f5d3SJohn Marino if (exclude_alloc <= exclude_count)
143*86d7f5d3SJohn Marino exclude = (char const **)
144*86d7f5d3SJohn Marino (exclude_alloc == 0
145*86d7f5d3SJohn Marino ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
146*86d7f5d3SJohn Marino : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
147*86d7f5d3SJohn Marino
148*86d7f5d3SJohn Marino exclude[exclude_count++] = pattern;
149*86d7f5d3SJohn Marino }
150*86d7f5d3SJohn Marino
151*86d7f5d3SJohn Marino static int
add_exclude_file(name)152*86d7f5d3SJohn Marino add_exclude_file (name)
153*86d7f5d3SJohn Marino char const *name;
154*86d7f5d3SJohn Marino {
155*86d7f5d3SJohn Marino struct file_data f;
156*86d7f5d3SJohn Marino char *p, *q, *lim;
157*86d7f5d3SJohn Marino
158*86d7f5d3SJohn Marino f.name = optarg;
159*86d7f5d3SJohn Marino f.desc = (strcmp (optarg, "-") == 0
160*86d7f5d3SJohn Marino ? STDIN_FILENO
161*86d7f5d3SJohn Marino : open (optarg, O_RDONLY, 0));
162*86d7f5d3SJohn Marino if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
163*86d7f5d3SJohn Marino return -1;
164*86d7f5d3SJohn Marino
165*86d7f5d3SJohn Marino sip (&f, 1);
166*86d7f5d3SJohn Marino slurp (&f);
167*86d7f5d3SJohn Marino
168*86d7f5d3SJohn Marino for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q)
169*86d7f5d3SJohn Marino {
170*86d7f5d3SJohn Marino q = (char *) memchr (p, '\n', lim - p);
171*86d7f5d3SJohn Marino if (!q)
172*86d7f5d3SJohn Marino q = lim;
173*86d7f5d3SJohn Marino *q++ = 0;
174*86d7f5d3SJohn Marino add_exclude (p);
175*86d7f5d3SJohn Marino }
176*86d7f5d3SJohn Marino
177*86d7f5d3SJohn Marino return close (f.desc);
178*86d7f5d3SJohn Marino }
179*86d7f5d3SJohn Marino
180*86d7f5d3SJohn Marino /* The numbers 129- that appear in the fourth element of some entries
181*86d7f5d3SJohn Marino tell the big switch in `diff_run' how to process those options. */
182*86d7f5d3SJohn Marino
183*86d7f5d3SJohn Marino static struct option const longopts[] =
184*86d7f5d3SJohn Marino {
185*86d7f5d3SJohn Marino {"ignore-blank-lines", 0, 0, 'B'},
186*86d7f5d3SJohn Marino {"context", 2, 0, 'C'},
187*86d7f5d3SJohn Marino {"ifdef", 1, 0, 'D'},
188*86d7f5d3SJohn Marino {"show-function-line", 1, 0, 'F'},
189*86d7f5d3SJohn Marino {"speed-large-files", 0, 0, 'H'},
190*86d7f5d3SJohn Marino {"ignore-matching-lines", 1, 0, 'I'},
191*86d7f5d3SJohn Marino {"label", 1, 0, 'L'},
192*86d7f5d3SJohn Marino {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */
193*86d7f5d3SJohn Marino {"new-file", 0, 0, 'N'},
194*86d7f5d3SJohn Marino {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */
195*86d7f5d3SJohn Marino {"unidirectional-new-file", 0, 0, 'P'},
196*86d7f5d3SJohn Marino {"starting-file", 1, 0, 'S'},
197*86d7f5d3SJohn Marino {"initial-tab", 0, 0, 'T'},
198*86d7f5d3SJohn Marino {"width", 1, 0, 'W'},
199*86d7f5d3SJohn Marino {"text", 0, 0, 'a'},
200*86d7f5d3SJohn Marino {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */
201*86d7f5d3SJohn Marino {"ignore-space-change", 0, 0, 'b'},
202*86d7f5d3SJohn Marino {"minimal", 0, 0, 'd'},
203*86d7f5d3SJohn Marino {"ed", 0, 0, 'e'},
204*86d7f5d3SJohn Marino {"forward-ed", 0, 0, 'f'},
205*86d7f5d3SJohn Marino {"ignore-case", 0, 0, 'i'},
206*86d7f5d3SJohn Marino {"paginate", 0, 0, 'l'},
207*86d7f5d3SJohn Marino {"print", 0, 0, 'l'}, /* An alias, no longer recommended */
208*86d7f5d3SJohn Marino {"rcs", 0, 0, 'n'},
209*86d7f5d3SJohn Marino {"show-c-function", 0, 0, 'p'},
210*86d7f5d3SJohn Marino {"brief", 0, 0, 'q'},
211*86d7f5d3SJohn Marino {"recursive", 0, 0, 'r'},
212*86d7f5d3SJohn Marino {"report-identical-files", 0, 0, 's'},
213*86d7f5d3SJohn Marino {"expand-tabs", 0, 0, 't'},
214*86d7f5d3SJohn Marino {"version", 0, 0, 'v'},
215*86d7f5d3SJohn Marino {"ignore-all-space", 0, 0, 'w'},
216*86d7f5d3SJohn Marino {"exclude", 1, 0, 'x'},
217*86d7f5d3SJohn Marino {"exclude-from", 1, 0, 'X'},
218*86d7f5d3SJohn Marino {"side-by-side", 0, 0, 'y'},
219*86d7f5d3SJohn Marino {"unified", 2, 0, 'U'},
220*86d7f5d3SJohn Marino {"left-column", 0, 0, 129},
221*86d7f5d3SJohn Marino {"suppress-common-lines", 0, 0, 130},
222*86d7f5d3SJohn Marino {"sdiff-merge-assist", 0, 0, 131},
223*86d7f5d3SJohn Marino {"old-line-format", 1, 0, 132},
224*86d7f5d3SJohn Marino {"new-line-format", 1, 0, 133},
225*86d7f5d3SJohn Marino {"unchanged-line-format", 1, 0, 134},
226*86d7f5d3SJohn Marino {"line-format", 1, 0, 135},
227*86d7f5d3SJohn Marino {"old-group-format", 1, 0, 136},
228*86d7f5d3SJohn Marino {"new-group-format", 1, 0, 137},
229*86d7f5d3SJohn Marino {"unchanged-group-format", 1, 0, 138},
230*86d7f5d3SJohn Marino {"changed-group-format", 1, 0, 139},
231*86d7f5d3SJohn Marino {"horizon-lines", 1, 0, 140},
232*86d7f5d3SJohn Marino {"help", 0, 0, 141},
233*86d7f5d3SJohn Marino {"binary", 0, 0, 142},
234*86d7f5d3SJohn Marino {0, 0, 0, 0}
235*86d7f5d3SJohn Marino };
236*86d7f5d3SJohn Marino
237*86d7f5d3SJohn Marino
238*86d7f5d3SJohn Marino
239*86d7f5d3SJohn Marino int
diff_run(argc,argv,out,callbacks_arg)240*86d7f5d3SJohn Marino diff_run (argc, argv, out, callbacks_arg)
241*86d7f5d3SJohn Marino int argc;
242*86d7f5d3SJohn Marino char *argv[];
243*86d7f5d3SJohn Marino const char *out;
244*86d7f5d3SJohn Marino const struct diff_callbacks *callbacks_arg;
245*86d7f5d3SJohn Marino {
246*86d7f5d3SJohn Marino int val;
247*86d7f5d3SJohn Marino int c;
248*86d7f5d3SJohn Marino int prev = -1;
249*86d7f5d3SJohn Marino int width = DEFAULT_WIDTH;
250*86d7f5d3SJohn Marino int show_c_function = 0;
251*86d7f5d3SJohn Marino int optind_old;
252*86d7f5d3SJohn Marino int opened_file = 0;
253*86d7f5d3SJohn Marino
254*86d7f5d3SJohn Marino callbacks = callbacks_arg;
255*86d7f5d3SJohn Marino
256*86d7f5d3SJohn Marino /* Do our initializations. */
257*86d7f5d3SJohn Marino initialize_main (&argc, &argv);
258*86d7f5d3SJohn Marino optind_old = optind;
259*86d7f5d3SJohn Marino optind = 0;
260*86d7f5d3SJohn Marino
261*86d7f5d3SJohn Marino /* Set the jump buffer, so that diff may abort execution without
262*86d7f5d3SJohn Marino terminating the process. */
263*86d7f5d3SJohn Marino val = setjmp (diff_abort_buf);
264*86d7f5d3SJohn Marino if (val != 0)
265*86d7f5d3SJohn Marino {
266*86d7f5d3SJohn Marino optind = optind_old;
267*86d7f5d3SJohn Marino if (opened_file)
268*86d7f5d3SJohn Marino fclose (outfile);
269*86d7f5d3SJohn Marino return val;
270*86d7f5d3SJohn Marino }
271*86d7f5d3SJohn Marino
272*86d7f5d3SJohn Marino /* Decode the options. */
273*86d7f5d3SJohn Marino while ((c = getopt_long (argc, argv,
274*86d7f5d3SJohn Marino "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
275*86d7f5d3SJohn Marino longopts, 0)) != EOF)
276*86d7f5d3SJohn Marino {
277*86d7f5d3SJohn Marino switch (c)
278*86d7f5d3SJohn Marino {
279*86d7f5d3SJohn Marino /* All digits combine in decimal to specify the context-size. */
280*86d7f5d3SJohn Marino case '1':
281*86d7f5d3SJohn Marino case '2':
282*86d7f5d3SJohn Marino case '3':
283*86d7f5d3SJohn Marino case '4':
284*86d7f5d3SJohn Marino case '5':
285*86d7f5d3SJohn Marino case '6':
286*86d7f5d3SJohn Marino case '7':
287*86d7f5d3SJohn Marino case '8':
288*86d7f5d3SJohn Marino case '9':
289*86d7f5d3SJohn Marino case '0':
290*86d7f5d3SJohn Marino if (context == -1)
291*86d7f5d3SJohn Marino context = 0;
292*86d7f5d3SJohn Marino /* If a context length has already been specified,
293*86d7f5d3SJohn Marino more digits allowed only if they follow right after the others.
294*86d7f5d3SJohn Marino Reject two separate runs of digits, or digits after -C. */
295*86d7f5d3SJohn Marino else if (prev < '0' || prev > '9')
296*86d7f5d3SJohn Marino fatal ("context length specified twice");
297*86d7f5d3SJohn Marino
298*86d7f5d3SJohn Marino context = context * 10 + c - '0';
299*86d7f5d3SJohn Marino break;
300*86d7f5d3SJohn Marino
301*86d7f5d3SJohn Marino case 'a':
302*86d7f5d3SJohn Marino /* Treat all files as text files; never treat as binary. */
303*86d7f5d3SJohn Marino always_text_flag = 1;
304*86d7f5d3SJohn Marino break;
305*86d7f5d3SJohn Marino
306*86d7f5d3SJohn Marino case 'b':
307*86d7f5d3SJohn Marino /* Ignore changes in amount of white space. */
308*86d7f5d3SJohn Marino ignore_space_change_flag = 1;
309*86d7f5d3SJohn Marino ignore_some_changes = 1;
310*86d7f5d3SJohn Marino ignore_some_line_changes = 1;
311*86d7f5d3SJohn Marino break;
312*86d7f5d3SJohn Marino
313*86d7f5d3SJohn Marino case 'B':
314*86d7f5d3SJohn Marino /* Ignore changes affecting only blank lines. */
315*86d7f5d3SJohn Marino ignore_blank_lines_flag = 1;
316*86d7f5d3SJohn Marino ignore_some_changes = 1;
317*86d7f5d3SJohn Marino break;
318*86d7f5d3SJohn Marino
319*86d7f5d3SJohn Marino case 'C': /* +context[=lines] */
320*86d7f5d3SJohn Marino case 'U': /* +unified[=lines] */
321*86d7f5d3SJohn Marino if (optarg)
322*86d7f5d3SJohn Marino {
323*86d7f5d3SJohn Marino if (context >= 0)
324*86d7f5d3SJohn Marino fatal ("context length specified twice");
325*86d7f5d3SJohn Marino
326*86d7f5d3SJohn Marino if (ck_atoi (optarg, &context))
327*86d7f5d3SJohn Marino fatal ("invalid context length argument");
328*86d7f5d3SJohn Marino }
329*86d7f5d3SJohn Marino
330*86d7f5d3SJohn Marino /* Falls through. */
331*86d7f5d3SJohn Marino case 'c':
332*86d7f5d3SJohn Marino /* Make context-style output. */
333*86d7f5d3SJohn Marino specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
334*86d7f5d3SJohn Marino break;
335*86d7f5d3SJohn Marino
336*86d7f5d3SJohn Marino case 'd':
337*86d7f5d3SJohn Marino /* Don't discard lines. This makes things slower (sometimes much
338*86d7f5d3SJohn Marino slower) but will find a guaranteed minimal set of changes. */
339*86d7f5d3SJohn Marino no_discards = 1;
340*86d7f5d3SJohn Marino break;
341*86d7f5d3SJohn Marino
342*86d7f5d3SJohn Marino case 'D':
343*86d7f5d3SJohn Marino /* Make merged #ifdef output. */
344*86d7f5d3SJohn Marino specify_style (OUTPUT_IFDEF);
345*86d7f5d3SJohn Marino {
346*86d7f5d3SJohn Marino int i, err = 0;
347*86d7f5d3SJohn Marino static char const C_ifdef_group_formats[] =
348*86d7f5d3SJohn Marino "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
349*86d7f5d3SJohn Marino char *b = xmalloc (sizeof (C_ifdef_group_formats)
350*86d7f5d3SJohn Marino + 7 * strlen(optarg) - 14 /* 7*"%s" */
351*86d7f5d3SJohn Marino - 8 /* 5*"%%" + 3*"%c" */);
352*86d7f5d3SJohn Marino sprintf (b, C_ifdef_group_formats,
353*86d7f5d3SJohn Marino optarg, optarg, 0,
354*86d7f5d3SJohn Marino optarg, optarg, 0, 0,
355*86d7f5d3SJohn Marino optarg, optarg, optarg);
356*86d7f5d3SJohn Marino for (i = 0; i < 4; i++)
357*86d7f5d3SJohn Marino {
358*86d7f5d3SJohn Marino err |= specify_format (&group_format[i], b);
359*86d7f5d3SJohn Marino b += strlen (b) + 1;
360*86d7f5d3SJohn Marino }
361*86d7f5d3SJohn Marino if (err)
362*86d7f5d3SJohn Marino diff_error ("conflicting #ifdef formats", 0, 0);
363*86d7f5d3SJohn Marino }
364*86d7f5d3SJohn Marino break;
365*86d7f5d3SJohn Marino
366*86d7f5d3SJohn Marino case 'e':
367*86d7f5d3SJohn Marino /* Make output that is a valid `ed' script. */
368*86d7f5d3SJohn Marino specify_style (OUTPUT_ED);
369*86d7f5d3SJohn Marino break;
370*86d7f5d3SJohn Marino
371*86d7f5d3SJohn Marino case 'f':
372*86d7f5d3SJohn Marino /* Make output that looks vaguely like an `ed' script
373*86d7f5d3SJohn Marino but has changes in the order they appear in the file. */
374*86d7f5d3SJohn Marino specify_style (OUTPUT_FORWARD_ED);
375*86d7f5d3SJohn Marino break;
376*86d7f5d3SJohn Marino
377*86d7f5d3SJohn Marino case 'F':
378*86d7f5d3SJohn Marino /* Show, for each set of changes, the previous line that
379*86d7f5d3SJohn Marino matches the specified regexp. Currently affects only
380*86d7f5d3SJohn Marino context-style output. */
381*86d7f5d3SJohn Marino add_regexp (&function_regexp_list, optarg);
382*86d7f5d3SJohn Marino break;
383*86d7f5d3SJohn Marino
384*86d7f5d3SJohn Marino case 'h':
385*86d7f5d3SJohn Marino /* Split the files into chunks of around 1500 lines
386*86d7f5d3SJohn Marino for faster processing. Usually does not change the result.
387*86d7f5d3SJohn Marino
388*86d7f5d3SJohn Marino This currently has no effect. */
389*86d7f5d3SJohn Marino break;
390*86d7f5d3SJohn Marino
391*86d7f5d3SJohn Marino case 'H':
392*86d7f5d3SJohn Marino /* Turn on heuristics that speed processing of large files
393*86d7f5d3SJohn Marino with a small density of changes. */
394*86d7f5d3SJohn Marino heuristic = 1;
395*86d7f5d3SJohn Marino break;
396*86d7f5d3SJohn Marino
397*86d7f5d3SJohn Marino case 'i':
398*86d7f5d3SJohn Marino /* Ignore changes in case. */
399*86d7f5d3SJohn Marino ignore_case_flag = 1;
400*86d7f5d3SJohn Marino ignore_some_changes = 1;
401*86d7f5d3SJohn Marino ignore_some_line_changes = 1;
402*86d7f5d3SJohn Marino break;
403*86d7f5d3SJohn Marino
404*86d7f5d3SJohn Marino case 'I':
405*86d7f5d3SJohn Marino /* Ignore changes affecting only lines that match the
406*86d7f5d3SJohn Marino specified regexp. */
407*86d7f5d3SJohn Marino add_regexp (&ignore_regexp_list, optarg);
408*86d7f5d3SJohn Marino ignore_some_changes = 1;
409*86d7f5d3SJohn Marino break;
410*86d7f5d3SJohn Marino
411*86d7f5d3SJohn Marino case 'l':
412*86d7f5d3SJohn Marino /* Pass the output through `pr' to paginate it. */
413*86d7f5d3SJohn Marino paginate_flag = 1;
414*86d7f5d3SJohn Marino #if !defined(SIGCHLD) && defined(SIGCLD)
415*86d7f5d3SJohn Marino #define SIGCHLD SIGCLD
416*86d7f5d3SJohn Marino #endif
417*86d7f5d3SJohn Marino #ifdef SIGCHLD
418*86d7f5d3SJohn Marino /* Pagination requires forking and waiting, and
419*86d7f5d3SJohn Marino System V fork+wait does not work if SIGCHLD is ignored. */
420*86d7f5d3SJohn Marino signal (SIGCHLD, SIG_DFL);
421*86d7f5d3SJohn Marino #endif
422*86d7f5d3SJohn Marino break;
423*86d7f5d3SJohn Marino
424*86d7f5d3SJohn Marino case 'L':
425*86d7f5d3SJohn Marino /* Specify file labels for `-c' output headers. */
426*86d7f5d3SJohn Marino if (!file_label[0])
427*86d7f5d3SJohn Marino file_label[0] = optarg;
428*86d7f5d3SJohn Marino else if (!file_label[1])
429*86d7f5d3SJohn Marino file_label[1] = optarg;
430*86d7f5d3SJohn Marino else
431*86d7f5d3SJohn Marino fatal ("too many file label options");
432*86d7f5d3SJohn Marino break;
433*86d7f5d3SJohn Marino
434*86d7f5d3SJohn Marino case 'n':
435*86d7f5d3SJohn Marino /* Output RCS-style diffs, like `-f' except that each command
436*86d7f5d3SJohn Marino specifies the number of lines affected. */
437*86d7f5d3SJohn Marino specify_style (OUTPUT_RCS);
438*86d7f5d3SJohn Marino break;
439*86d7f5d3SJohn Marino
440*86d7f5d3SJohn Marino case 'N':
441*86d7f5d3SJohn Marino /* When comparing directories, if a file appears only in one
442*86d7f5d3SJohn Marino directory, treat it as present but empty in the other. */
443*86d7f5d3SJohn Marino entire_new_file_flag = 1;
444*86d7f5d3SJohn Marino break;
445*86d7f5d3SJohn Marino
446*86d7f5d3SJohn Marino case 'p':
447*86d7f5d3SJohn Marino /* Make context-style output and show name of last C function. */
448*86d7f5d3SJohn Marino show_c_function = 1;
449*86d7f5d3SJohn Marino add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
450*86d7f5d3SJohn Marino break;
451*86d7f5d3SJohn Marino
452*86d7f5d3SJohn Marino case 'P':
453*86d7f5d3SJohn Marino /* When comparing directories, if a file appears only in
454*86d7f5d3SJohn Marino the second directory of the two,
455*86d7f5d3SJohn Marino treat it as present but empty in the other. */
456*86d7f5d3SJohn Marino unidirectional_new_file_flag = 1;
457*86d7f5d3SJohn Marino break;
458*86d7f5d3SJohn Marino
459*86d7f5d3SJohn Marino case 'q':
460*86d7f5d3SJohn Marino no_details_flag = 1;
461*86d7f5d3SJohn Marino break;
462*86d7f5d3SJohn Marino
463*86d7f5d3SJohn Marino case 'r':
464*86d7f5d3SJohn Marino /* When comparing directories,
465*86d7f5d3SJohn Marino recursively compare any subdirectories found. */
466*86d7f5d3SJohn Marino recursive = 1;
467*86d7f5d3SJohn Marino break;
468*86d7f5d3SJohn Marino
469*86d7f5d3SJohn Marino case 's':
470*86d7f5d3SJohn Marino /* Print a message if the files are the same. */
471*86d7f5d3SJohn Marino print_file_same_flag = 1;
472*86d7f5d3SJohn Marino break;
473*86d7f5d3SJohn Marino
474*86d7f5d3SJohn Marino case 'S':
475*86d7f5d3SJohn Marino /* When comparing directories, start with the specified
476*86d7f5d3SJohn Marino file name. This is used for resuming an aborted comparison. */
477*86d7f5d3SJohn Marino dir_start_file = optarg;
478*86d7f5d3SJohn Marino break;
479*86d7f5d3SJohn Marino
480*86d7f5d3SJohn Marino case 't':
481*86d7f5d3SJohn Marino /* Expand tabs to spaces in the output so that it preserves
482*86d7f5d3SJohn Marino the alignment of the input files. */
483*86d7f5d3SJohn Marino tab_expand_flag = 1;
484*86d7f5d3SJohn Marino break;
485*86d7f5d3SJohn Marino
486*86d7f5d3SJohn Marino case 'T':
487*86d7f5d3SJohn Marino /* Use a tab in the output, rather than a space, before the
488*86d7f5d3SJohn Marino text of an input line, so as to keep the proper alignment
489*86d7f5d3SJohn Marino in the input line without changing the characters in it. */
490*86d7f5d3SJohn Marino tab_align_flag = 1;
491*86d7f5d3SJohn Marino break;
492*86d7f5d3SJohn Marino
493*86d7f5d3SJohn Marino case 'u':
494*86d7f5d3SJohn Marino /* Output the context diff in unidiff format. */
495*86d7f5d3SJohn Marino specify_style (OUTPUT_UNIFIED);
496*86d7f5d3SJohn Marino break;
497*86d7f5d3SJohn Marino
498*86d7f5d3SJohn Marino case 'v':
499*86d7f5d3SJohn Marino if (callbacks && callbacks->write_stdout)
500*86d7f5d3SJohn Marino {
501*86d7f5d3SJohn Marino (*callbacks->write_stdout) ("diff - GNU diffutils version ");
502*86d7f5d3SJohn Marino (*callbacks->write_stdout) (diff_version_string);
503*86d7f5d3SJohn Marino (*callbacks->write_stdout) ("\n");
504*86d7f5d3SJohn Marino }
505*86d7f5d3SJohn Marino else
506*86d7f5d3SJohn Marino printf ("diff - GNU diffutils version %s\n", diff_version_string);
507*86d7f5d3SJohn Marino return 0;
508*86d7f5d3SJohn Marino
509*86d7f5d3SJohn Marino case 'w':
510*86d7f5d3SJohn Marino /* Ignore horizontal white space when comparing lines. */
511*86d7f5d3SJohn Marino ignore_all_space_flag = 1;
512*86d7f5d3SJohn Marino ignore_some_changes = 1;
513*86d7f5d3SJohn Marino ignore_some_line_changes = 1;
514*86d7f5d3SJohn Marino break;
515*86d7f5d3SJohn Marino
516*86d7f5d3SJohn Marino case 'x':
517*86d7f5d3SJohn Marino add_exclude (optarg);
518*86d7f5d3SJohn Marino break;
519*86d7f5d3SJohn Marino
520*86d7f5d3SJohn Marino case 'X':
521*86d7f5d3SJohn Marino if (add_exclude_file (optarg) != 0)
522*86d7f5d3SJohn Marino pfatal_with_name (optarg);
523*86d7f5d3SJohn Marino break;
524*86d7f5d3SJohn Marino
525*86d7f5d3SJohn Marino case 'y':
526*86d7f5d3SJohn Marino /* Use side-by-side (sdiff-style) columnar output. */
527*86d7f5d3SJohn Marino specify_style (OUTPUT_SDIFF);
528*86d7f5d3SJohn Marino break;
529*86d7f5d3SJohn Marino
530*86d7f5d3SJohn Marino case 'W':
531*86d7f5d3SJohn Marino /* Set the line width for OUTPUT_SDIFF. */
532*86d7f5d3SJohn Marino if (ck_atoi (optarg, &width) || width <= 0)
533*86d7f5d3SJohn Marino fatal ("column width must be a positive integer");
534*86d7f5d3SJohn Marino break;
535*86d7f5d3SJohn Marino
536*86d7f5d3SJohn Marino case 129:
537*86d7f5d3SJohn Marino sdiff_left_only = 1;
538*86d7f5d3SJohn Marino break;
539*86d7f5d3SJohn Marino
540*86d7f5d3SJohn Marino case 130:
541*86d7f5d3SJohn Marino sdiff_skip_common_lines = 1;
542*86d7f5d3SJohn Marino break;
543*86d7f5d3SJohn Marino
544*86d7f5d3SJohn Marino case 131:
545*86d7f5d3SJohn Marino /* sdiff-style columns output. */
546*86d7f5d3SJohn Marino specify_style (OUTPUT_SDIFF);
547*86d7f5d3SJohn Marino sdiff_help_sdiff = 1;
548*86d7f5d3SJohn Marino break;
549*86d7f5d3SJohn Marino
550*86d7f5d3SJohn Marino case 132:
551*86d7f5d3SJohn Marino case 133:
552*86d7f5d3SJohn Marino case 134:
553*86d7f5d3SJohn Marino specify_style (OUTPUT_IFDEF);
554*86d7f5d3SJohn Marino if (specify_format (&line_format[c - 132], optarg) != 0)
555*86d7f5d3SJohn Marino diff_error ("conflicting line format", 0, 0);
556*86d7f5d3SJohn Marino break;
557*86d7f5d3SJohn Marino
558*86d7f5d3SJohn Marino case 135:
559*86d7f5d3SJohn Marino specify_style (OUTPUT_IFDEF);
560*86d7f5d3SJohn Marino {
561*86d7f5d3SJohn Marino int i, err = 0;
562*86d7f5d3SJohn Marino for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
563*86d7f5d3SJohn Marino err |= specify_format (&line_format[i], optarg);
564*86d7f5d3SJohn Marino if (err)
565*86d7f5d3SJohn Marino diff_error ("conflicting line format", 0, 0);
566*86d7f5d3SJohn Marino }
567*86d7f5d3SJohn Marino break;
568*86d7f5d3SJohn Marino
569*86d7f5d3SJohn Marino case 136:
570*86d7f5d3SJohn Marino case 137:
571*86d7f5d3SJohn Marino case 138:
572*86d7f5d3SJohn Marino case 139:
573*86d7f5d3SJohn Marino specify_style (OUTPUT_IFDEF);
574*86d7f5d3SJohn Marino if (specify_format (&group_format[c - 136], optarg) != 0)
575*86d7f5d3SJohn Marino diff_error ("conflicting group format", 0, 0);
576*86d7f5d3SJohn Marino break;
577*86d7f5d3SJohn Marino
578*86d7f5d3SJohn Marino case 140:
579*86d7f5d3SJohn Marino if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0)
580*86d7f5d3SJohn Marino fatal ("horizon must be a nonnegative integer");
581*86d7f5d3SJohn Marino break;
582*86d7f5d3SJohn Marino
583*86d7f5d3SJohn Marino case 141:
584*86d7f5d3SJohn Marino usage ();
585*86d7f5d3SJohn Marino if (! callbacks || ! callbacks->write_stdout)
586*86d7f5d3SJohn Marino check_output (stdout);
587*86d7f5d3SJohn Marino return 0;
588*86d7f5d3SJohn Marino
589*86d7f5d3SJohn Marino case 142:
590*86d7f5d3SJohn Marino /* Use binary I/O when reading and writing data.
591*86d7f5d3SJohn Marino On Posix hosts, this has no effect. */
592*86d7f5d3SJohn Marino #if HAVE_SETMODE
593*86d7f5d3SJohn Marino binary_I_O = 1;
594*86d7f5d3SJohn Marino # if 0
595*86d7f5d3SJohn Marino /* Because this code is leftover from pre-library days,
596*86d7f5d3SJohn Marino there is no way to set stdout back to the default mode
597*86d7f5d3SJohn Marino when we are done. As it turns out, I think the only
598*86d7f5d3SJohn Marino parts of CVS that pass out == NULL, and thus cause diff
599*86d7f5d3SJohn Marino to write to stdout, are "cvs diff" and "cvs rdiff". So
600*86d7f5d3SJohn Marino I'm not going to worry about this too much yet. */
601*86d7f5d3SJohn Marino setmode (STDOUT_FILENO, O_BINARY);
602*86d7f5d3SJohn Marino # else
603*86d7f5d3SJohn Marino if (out == NULL)
604*86d7f5d3SJohn Marino error (0, 0, "warning: did not set stdout to binary mode");
605*86d7f5d3SJohn Marino # endif
606*86d7f5d3SJohn Marino #endif
607*86d7f5d3SJohn Marino break;
608*86d7f5d3SJohn Marino
609*86d7f5d3SJohn Marino default:
610*86d7f5d3SJohn Marino return try_help (0);
611*86d7f5d3SJohn Marino }
612*86d7f5d3SJohn Marino prev = c;
613*86d7f5d3SJohn Marino }
614*86d7f5d3SJohn Marino
615*86d7f5d3SJohn Marino if (argc - optind != 2)
616*86d7f5d3SJohn Marino return try_help (argc - optind < 2 ? "missing operand" : "extra operand");
617*86d7f5d3SJohn Marino
618*86d7f5d3SJohn Marino {
619*86d7f5d3SJohn Marino /*
620*86d7f5d3SJohn Marino * We maximize first the half line width, and then the gutter width,
621*86d7f5d3SJohn Marino * according to the following constraints:
622*86d7f5d3SJohn Marino * 1. Two half lines plus a gutter must fit in a line.
623*86d7f5d3SJohn Marino * 2. If the half line width is nonzero:
624*86d7f5d3SJohn Marino * a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
625*86d7f5d3SJohn Marino * b. If tabs are not expanded to spaces,
626*86d7f5d3SJohn Marino * a half line plus a gutter is an integral number of tabs,
627*86d7f5d3SJohn Marino * so that tabs in the right column line up.
628*86d7f5d3SJohn Marino */
629*86d7f5d3SJohn Marino int t = tab_expand_flag ? 1 : TAB_WIDTH;
630*86d7f5d3SJohn Marino int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t;
631*86d7f5d3SJohn Marino sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)),
632*86d7f5d3SJohn Marino sdiff_column2_offset = sdiff_half_width ? off : width;
633*86d7f5d3SJohn Marino }
634*86d7f5d3SJohn Marino
635*86d7f5d3SJohn Marino if (show_c_function && output_style != OUTPUT_UNIFIED)
636*86d7f5d3SJohn Marino specify_style (OUTPUT_CONTEXT);
637*86d7f5d3SJohn Marino
638*86d7f5d3SJohn Marino if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
639*86d7f5d3SJohn Marino context = 0;
640*86d7f5d3SJohn Marino else if (context == -1)
641*86d7f5d3SJohn Marino /* Default amount of context for -c. */
642*86d7f5d3SJohn Marino context = 3;
643*86d7f5d3SJohn Marino
644*86d7f5d3SJohn Marino if (output_style == OUTPUT_IFDEF)
645*86d7f5d3SJohn Marino {
646*86d7f5d3SJohn Marino /* Format arrays are char *, not char const *,
647*86d7f5d3SJohn Marino because integer formats are temporarily modified.
648*86d7f5d3SJohn Marino But it is safe to assign a constant like "%=" to a format array,
649*86d7f5d3SJohn Marino since "%=" does not format any integers. */
650*86d7f5d3SJohn Marino int i;
651*86d7f5d3SJohn Marino for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
652*86d7f5d3SJohn Marino if (!line_format[i])
653*86d7f5d3SJohn Marino line_format[i] = "%l\n";
654*86d7f5d3SJohn Marino if (!group_format[OLD])
655*86d7f5d3SJohn Marino group_format[OLD]
656*86d7f5d3SJohn Marino = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<";
657*86d7f5d3SJohn Marino if (!group_format[NEW])
658*86d7f5d3SJohn Marino group_format[NEW]
659*86d7f5d3SJohn Marino = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>";
660*86d7f5d3SJohn Marino if (!group_format[UNCHANGED])
661*86d7f5d3SJohn Marino group_format[UNCHANGED] = "%=";
662*86d7f5d3SJohn Marino if (!group_format[CHANGED])
663*86d7f5d3SJohn Marino group_format[CHANGED] = concat (group_format[OLD],
664*86d7f5d3SJohn Marino group_format[NEW], "");
665*86d7f5d3SJohn Marino }
666*86d7f5d3SJohn Marino
667*86d7f5d3SJohn Marino no_diff_means_no_output =
668*86d7f5d3SJohn Marino (output_style == OUTPUT_IFDEF ?
669*86d7f5d3SJohn Marino (!*group_format[UNCHANGED]
670*86d7f5d3SJohn Marino || (strcmp (group_format[UNCHANGED], "%=") == 0
671*86d7f5d3SJohn Marino && !*line_format[UNCHANGED]))
672*86d7f5d3SJohn Marino : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1);
673*86d7f5d3SJohn Marino
674*86d7f5d3SJohn Marino switch_string = option_list (argv + 1, optind - 1);
675*86d7f5d3SJohn Marino
676*86d7f5d3SJohn Marino if (callbacks && callbacks->write_output)
677*86d7f5d3SJohn Marino {
678*86d7f5d3SJohn Marino if (out != NULL)
679*86d7f5d3SJohn Marino {
680*86d7f5d3SJohn Marino diff_error ("write callback with output file", 0, 0);
681*86d7f5d3SJohn Marino return 2;
682*86d7f5d3SJohn Marino }
683*86d7f5d3SJohn Marino }
684*86d7f5d3SJohn Marino else
685*86d7f5d3SJohn Marino {
686*86d7f5d3SJohn Marino if (out == NULL)
687*86d7f5d3SJohn Marino outfile = stdout;
688*86d7f5d3SJohn Marino else
689*86d7f5d3SJohn Marino {
690*86d7f5d3SJohn Marino #if HAVE_SETMODE
691*86d7f5d3SJohn Marino /* A diff which is full of ^Z and such isn't going to work
692*86d7f5d3SJohn Marino very well in text mode. */
693*86d7f5d3SJohn Marino if (binary_I_O)
694*86d7f5d3SJohn Marino outfile = fopen (out, "wb");
695*86d7f5d3SJohn Marino else
696*86d7f5d3SJohn Marino #endif
697*86d7f5d3SJohn Marino outfile = fopen (out, "w");
698*86d7f5d3SJohn Marino if (outfile == NULL)
699*86d7f5d3SJohn Marino {
700*86d7f5d3SJohn Marino perror_with_name ("could not open output file");
701*86d7f5d3SJohn Marino return 2;
702*86d7f5d3SJohn Marino }
703*86d7f5d3SJohn Marino opened_file = 1;
704*86d7f5d3SJohn Marino }
705*86d7f5d3SJohn Marino }
706*86d7f5d3SJohn Marino
707*86d7f5d3SJohn Marino val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
708*86d7f5d3SJohn Marino
709*86d7f5d3SJohn Marino /* Print any messages that were saved up for last. */
710*86d7f5d3SJohn Marino print_message_queue ();
711*86d7f5d3SJohn Marino
712*86d7f5d3SJohn Marino free (switch_string);
713*86d7f5d3SJohn Marino
714*86d7f5d3SJohn Marino optind = optind_old;
715*86d7f5d3SJohn Marino
716*86d7f5d3SJohn Marino if (! callbacks || ! callbacks->write_output)
717*86d7f5d3SJohn Marino check_output (outfile);
718*86d7f5d3SJohn Marino
719*86d7f5d3SJohn Marino if (opened_file)
720*86d7f5d3SJohn Marino if (fclose (outfile) != 0)
721*86d7f5d3SJohn Marino perror_with_name ("close error on output file");
722*86d7f5d3SJohn Marino
723*86d7f5d3SJohn Marino return val;
724*86d7f5d3SJohn Marino }
725*86d7f5d3SJohn Marino
726*86d7f5d3SJohn Marino /* Add the compiled form of regexp PATTERN to REGLIST. */
727*86d7f5d3SJohn Marino
728*86d7f5d3SJohn Marino static void
add_regexp(reglist,pattern)729*86d7f5d3SJohn Marino add_regexp (reglist, pattern)
730*86d7f5d3SJohn Marino struct regexp_list **reglist;
731*86d7f5d3SJohn Marino char const *pattern;
732*86d7f5d3SJohn Marino {
733*86d7f5d3SJohn Marino struct regexp_list *r;
734*86d7f5d3SJohn Marino char const *m;
735*86d7f5d3SJohn Marino
736*86d7f5d3SJohn Marino r = (struct regexp_list *) xmalloc (sizeof (*r));
737*86d7f5d3SJohn Marino bzero (r, sizeof (*r));
738*86d7f5d3SJohn Marino r->buf.fastmap = xmalloc (256);
739*86d7f5d3SJohn Marino m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
740*86d7f5d3SJohn Marino if (m != 0)
741*86d7f5d3SJohn Marino diff_error ("%s: %s", pattern, m);
742*86d7f5d3SJohn Marino
743*86d7f5d3SJohn Marino /* Add to the start of the list, since it's easier than the end. */
744*86d7f5d3SJohn Marino r->next = *reglist;
745*86d7f5d3SJohn Marino *reglist = r;
746*86d7f5d3SJohn Marino }
747*86d7f5d3SJohn Marino
748*86d7f5d3SJohn Marino static int
try_help(reason)749*86d7f5d3SJohn Marino try_help (reason)
750*86d7f5d3SJohn Marino char const *reason;
751*86d7f5d3SJohn Marino {
752*86d7f5d3SJohn Marino if (reason)
753*86d7f5d3SJohn Marino diff_error ("%s", reason, 0);
754*86d7f5d3SJohn Marino diff_error ("Try `%s --help' for more information.", diff_program_name, 0);
755*86d7f5d3SJohn Marino return 2;
756*86d7f5d3SJohn Marino }
757*86d7f5d3SJohn Marino
758*86d7f5d3SJohn Marino static void
check_output(file)759*86d7f5d3SJohn Marino check_output (file)
760*86d7f5d3SJohn Marino FILE *file;
761*86d7f5d3SJohn Marino {
762*86d7f5d3SJohn Marino if (ferror (file) || fflush (file) != 0)
763*86d7f5d3SJohn Marino fatal ("write error");
764*86d7f5d3SJohn Marino }
765*86d7f5d3SJohn Marino
766*86d7f5d3SJohn Marino static char const * const option_help[] = {
767*86d7f5d3SJohn Marino "-i --ignore-case Consider upper- and lower-case to be the same.",
768*86d7f5d3SJohn Marino "-w --ignore-all-space Ignore all white space.",
769*86d7f5d3SJohn Marino "-b --ignore-space-change Ignore changes in the amount of white space.",
770*86d7f5d3SJohn Marino "-B --ignore-blank-lines Ignore changes whose lines are all blank.",
771*86d7f5d3SJohn Marino "-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.",
772*86d7f5d3SJohn Marino #if HAVE_SETMODE
773*86d7f5d3SJohn Marino "--binary Read and write data in binary mode.",
774*86d7f5d3SJohn Marino #endif
775*86d7f5d3SJohn Marino "-a --text Treat all files as text.\n",
776*86d7f5d3SJohn Marino "-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.",
777*86d7f5d3SJohn Marino "-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.",
778*86d7f5d3SJohn Marino " -NUM Use NUM context lines.",
779*86d7f5d3SJohn Marino " -L LABEL --label LABEL Use LABEL instead of file name.",
780*86d7f5d3SJohn Marino " -p --show-c-function Show which C function each change is in.",
781*86d7f5d3SJohn Marino " -F RE --show-function-line=RE Show the most recent line matching RE.",
782*86d7f5d3SJohn Marino "-q --brief Output only whether files differ.",
783*86d7f5d3SJohn Marino "-e --ed Output an ed script.",
784*86d7f5d3SJohn Marino "-n --rcs Output an RCS format diff.",
785*86d7f5d3SJohn Marino "-y --side-by-side Output in two columns.",
786*86d7f5d3SJohn Marino " -W NUM --width=NUM Output at most NUM (default 130) characters per line.",
787*86d7f5d3SJohn Marino " --left-column Output only the left column of common lines.",
788*86d7f5d3SJohn Marino " --suppress-common-lines Do not output common lines.",
789*86d7f5d3SJohn Marino "-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.",
790*86d7f5d3SJohn Marino "--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.",
791*86d7f5d3SJohn Marino "--line-format=LFMT Similar, but format all input lines with LFMT.",
792*86d7f5d3SJohn Marino "--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.",
793*86d7f5d3SJohn Marino " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.",
794*86d7f5d3SJohn Marino " GFMT may contain:",
795*86d7f5d3SJohn Marino " %< lines from FILE1",
796*86d7f5d3SJohn Marino " %> lines from FILE2",
797*86d7f5d3SJohn Marino " %= lines common to FILE1 and FILE2",
798*86d7f5d3SJohn Marino " %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER",
799*86d7f5d3SJohn Marino " LETTERs are as follows for new group, lower case for old group:",
800*86d7f5d3SJohn Marino " F first line number",
801*86d7f5d3SJohn Marino " L last line number",
802*86d7f5d3SJohn Marino " N number of lines = L-F+1",
803*86d7f5d3SJohn Marino " E F-1",
804*86d7f5d3SJohn Marino " M L+1",
805*86d7f5d3SJohn Marino " LFMT may contain:",
806*86d7f5d3SJohn Marino " %L contents of line",
807*86d7f5d3SJohn Marino " %l contents of line, excluding any trailing newline",
808*86d7f5d3SJohn Marino " %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number",
809*86d7f5d3SJohn Marino " Either GFMT or LFMT may contain:",
810*86d7f5d3SJohn Marino " %% %",
811*86d7f5d3SJohn Marino " %c'C' the single character C",
812*86d7f5d3SJohn Marino " %c'\\OOO' the character with octal code OOO\n",
813*86d7f5d3SJohn Marino "-l --paginate Pass the output through `pr' to paginate it.",
814*86d7f5d3SJohn Marino "-t --expand-tabs Expand tabs to spaces in output.",
815*86d7f5d3SJohn Marino "-T --initial-tab Make tabs line up by prepending a tab.\n",
816*86d7f5d3SJohn Marino "-r --recursive Recursively compare any subdirectories found.",
817*86d7f5d3SJohn Marino "-N --new-file Treat absent files as empty.",
818*86d7f5d3SJohn Marino "-P --unidirectional-new-file Treat absent first files as empty.",
819*86d7f5d3SJohn Marino "-s --report-identical-files Report when two files are the same.",
820*86d7f5d3SJohn Marino "-x PAT --exclude=PAT Exclude files that match PAT.",
821*86d7f5d3SJohn Marino "-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.",
822*86d7f5d3SJohn Marino "-S FILE --starting-file=FILE Start with FILE when comparing directories.\n",
823*86d7f5d3SJohn Marino "--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.",
824*86d7f5d3SJohn Marino "-d --minimal Try hard to find a smaller set of changes.",
825*86d7f5d3SJohn Marino "-H --speed-large-files Assume large files and many scattered small changes.\n",
826*86d7f5d3SJohn Marino "-v --version Output version info.",
827*86d7f5d3SJohn Marino "--help Output this help.",
828*86d7f5d3SJohn Marino 0
829*86d7f5d3SJohn Marino };
830*86d7f5d3SJohn Marino
831*86d7f5d3SJohn Marino static void
usage()832*86d7f5d3SJohn Marino usage ()
833*86d7f5d3SJohn Marino {
834*86d7f5d3SJohn Marino char const * const *p;
835*86d7f5d3SJohn Marino
836*86d7f5d3SJohn Marino if (callbacks && callbacks->write_stdout)
837*86d7f5d3SJohn Marino {
838*86d7f5d3SJohn Marino (*callbacks->write_stdout) ("Usage: ");
839*86d7f5d3SJohn Marino (*callbacks->write_stdout) (diff_program_name);
840*86d7f5d3SJohn Marino (*callbacks->write_stdout) (" [OPTION]... FILE1 FILE2\n\n");
841*86d7f5d3SJohn Marino for (p = option_help; *p; p++)
842*86d7f5d3SJohn Marino {
843*86d7f5d3SJohn Marino (*callbacks->write_stdout) (" ");
844*86d7f5d3SJohn Marino (*callbacks->write_stdout) (*p);
845*86d7f5d3SJohn Marino (*callbacks->write_stdout) ("\n");
846*86d7f5d3SJohn Marino }
847*86d7f5d3SJohn Marino (*callbacks->write_stdout)
848*86d7f5d3SJohn Marino ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
849*86d7f5d3SJohn Marino }
850*86d7f5d3SJohn Marino else
851*86d7f5d3SJohn Marino {
852*86d7f5d3SJohn Marino printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", diff_program_name);
853*86d7f5d3SJohn Marino for (p = option_help; *p; p++)
854*86d7f5d3SJohn Marino printf (" %s\n", *p);
855*86d7f5d3SJohn Marino printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
856*86d7f5d3SJohn Marino }
857*86d7f5d3SJohn Marino }
858*86d7f5d3SJohn Marino
859*86d7f5d3SJohn Marino static int
specify_format(var,value)860*86d7f5d3SJohn Marino specify_format (var, value)
861*86d7f5d3SJohn Marino char **var;
862*86d7f5d3SJohn Marino char *value;
863*86d7f5d3SJohn Marino {
864*86d7f5d3SJohn Marino int err = *var ? strcmp (*var, value) : 0;
865*86d7f5d3SJohn Marino *var = value;
866*86d7f5d3SJohn Marino return err;
867*86d7f5d3SJohn Marino }
868*86d7f5d3SJohn Marino
869*86d7f5d3SJohn Marino static void
specify_style(style)870*86d7f5d3SJohn Marino specify_style (style)
871*86d7f5d3SJohn Marino enum output_style style;
872*86d7f5d3SJohn Marino {
873*86d7f5d3SJohn Marino if (output_style != OUTPUT_NORMAL
874*86d7f5d3SJohn Marino && output_style != style)
875*86d7f5d3SJohn Marino diff_error ("conflicting specifications of output style", 0, 0);
876*86d7f5d3SJohn Marino output_style = style;
877*86d7f5d3SJohn Marino }
878*86d7f5d3SJohn Marino
879*86d7f5d3SJohn Marino static char const *
filetype(st)880*86d7f5d3SJohn Marino filetype (st)
881*86d7f5d3SJohn Marino struct stat const *st;
882*86d7f5d3SJohn Marino {
883*86d7f5d3SJohn Marino /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats.
884*86d7f5d3SJohn Marino To keep diagnostics grammatical, the returned string must start
885*86d7f5d3SJohn Marino with a consonant. */
886*86d7f5d3SJohn Marino
887*86d7f5d3SJohn Marino if (S_ISREG (st->st_mode))
888*86d7f5d3SJohn Marino {
889*86d7f5d3SJohn Marino if (st->st_size == 0)
890*86d7f5d3SJohn Marino return "regular empty file";
891*86d7f5d3SJohn Marino /* Posix.2 section 5.14.2 seems to suggest that we must read the file
892*86d7f5d3SJohn Marino and guess whether it's C, Fortran, etc., but this is somewhat useless
893*86d7f5d3SJohn Marino and doesn't reflect historical practice. We're allowed to guess
894*86d7f5d3SJohn Marino wrong, so we don't bother to read the file. */
895*86d7f5d3SJohn Marino return "regular file";
896*86d7f5d3SJohn Marino }
897*86d7f5d3SJohn Marino if (S_ISDIR (st->st_mode)) return "directory";
898*86d7f5d3SJohn Marino
899*86d7f5d3SJohn Marino /* other Posix.1 file types */
900*86d7f5d3SJohn Marino #ifdef S_ISBLK
901*86d7f5d3SJohn Marino if (S_ISBLK (st->st_mode)) return "block special file";
902*86d7f5d3SJohn Marino #endif
903*86d7f5d3SJohn Marino #ifdef S_ISCHR
904*86d7f5d3SJohn Marino if (S_ISCHR (st->st_mode)) return "character special file";
905*86d7f5d3SJohn Marino #endif
906*86d7f5d3SJohn Marino #ifdef S_ISFIFO
907*86d7f5d3SJohn Marino if (S_ISFIFO (st->st_mode)) return "fifo";
908*86d7f5d3SJohn Marino #endif
909*86d7f5d3SJohn Marino
910*86d7f5d3SJohn Marino /* other Posix.1b file types */
911*86d7f5d3SJohn Marino #ifdef S_TYPEISMQ
912*86d7f5d3SJohn Marino if (S_TYPEISMQ (st)) return "message queue";
913*86d7f5d3SJohn Marino #endif
914*86d7f5d3SJohn Marino #ifdef S_TYPEISSEM
915*86d7f5d3SJohn Marino if (S_TYPEISSEM (st)) return "semaphore";
916*86d7f5d3SJohn Marino #endif
917*86d7f5d3SJohn Marino #ifdef S_TYPEISSHM
918*86d7f5d3SJohn Marino if (S_TYPEISSHM (st)) return "shared memory object";
919*86d7f5d3SJohn Marino #endif
920*86d7f5d3SJohn Marino
921*86d7f5d3SJohn Marino /* other popular file types */
922*86d7f5d3SJohn Marino /* S_ISLNK is impossible with `fstat' and `stat'. */
923*86d7f5d3SJohn Marino #ifdef S_ISSOCK
924*86d7f5d3SJohn Marino if (S_ISSOCK (st->st_mode)) return "socket";
925*86d7f5d3SJohn Marino #endif
926*86d7f5d3SJohn Marino
927*86d7f5d3SJohn Marino return "weird file";
928*86d7f5d3SJohn Marino }
929*86d7f5d3SJohn Marino
930*86d7f5d3SJohn Marino /* Compare two files (or dirs) with specified names
931*86d7f5d3SJohn Marino DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
932*86d7f5d3SJohn Marino (if DIR0 is 0, then the name is just NAME0, etc.)
933*86d7f5d3SJohn Marino This is self-contained; it opens the files and closes them.
934*86d7f5d3SJohn Marino
935*86d7f5d3SJohn Marino Value is 0 if files are the same, 1 if different,
936*86d7f5d3SJohn Marino 2 if there is a problem opening them. */
937*86d7f5d3SJohn Marino
938*86d7f5d3SJohn Marino static int
compare_files(dir0,name0,dir1,name1,depth)939*86d7f5d3SJohn Marino compare_files (dir0, name0, dir1, name1, depth)
940*86d7f5d3SJohn Marino char const *dir0, *dir1;
941*86d7f5d3SJohn Marino char const *name0, *name1;
942*86d7f5d3SJohn Marino int depth;
943*86d7f5d3SJohn Marino {
944*86d7f5d3SJohn Marino struct file_data inf[2];
945*86d7f5d3SJohn Marino register int i;
946*86d7f5d3SJohn Marino int val;
947*86d7f5d3SJohn Marino int same_files;
948*86d7f5d3SJohn Marino int failed = 0;
949*86d7f5d3SJohn Marino char *free0 = 0, *free1 = 0;
950*86d7f5d3SJohn Marino
951*86d7f5d3SJohn Marino /* If this is directory comparison, perhaps we have a file
952*86d7f5d3SJohn Marino that exists only in one of the directories.
953*86d7f5d3SJohn Marino If so, just print a message to that effect. */
954*86d7f5d3SJohn Marino
955*86d7f5d3SJohn Marino if (! ((name0 != 0 && name1 != 0)
956*86d7f5d3SJohn Marino || (unidirectional_new_file_flag && name1 != 0)
957*86d7f5d3SJohn Marino || entire_new_file_flag))
958*86d7f5d3SJohn Marino {
959*86d7f5d3SJohn Marino char const *name = name0 == 0 ? name1 : name0;
960*86d7f5d3SJohn Marino char const *dir = name0 == 0 ? dir1 : dir0;
961*86d7f5d3SJohn Marino message ("Only in %s: %s\n", dir, name);
962*86d7f5d3SJohn Marino /* Return 1 so that diff_dirs will return 1 ("some files differ"). */
963*86d7f5d3SJohn Marino return 1;
964*86d7f5d3SJohn Marino }
965*86d7f5d3SJohn Marino
966*86d7f5d3SJohn Marino bzero (inf, sizeof (inf));
967*86d7f5d3SJohn Marino
968*86d7f5d3SJohn Marino /* Mark any nonexistent file with -1 in the desc field. */
969*86d7f5d3SJohn Marino /* Mark unopened files (e.g. directories) with -2. */
970*86d7f5d3SJohn Marino
971*86d7f5d3SJohn Marino inf[0].desc = name0 == 0 ? -1 : -2;
972*86d7f5d3SJohn Marino inf[1].desc = name1 == 0 ? -1 : -2;
973*86d7f5d3SJohn Marino
974*86d7f5d3SJohn Marino /* Now record the full name of each file, including nonexistent ones. */
975*86d7f5d3SJohn Marino
976*86d7f5d3SJohn Marino if (name0 == 0)
977*86d7f5d3SJohn Marino name0 = name1;
978*86d7f5d3SJohn Marino if (name1 == 0)
979*86d7f5d3SJohn Marino name1 = name0;
980*86d7f5d3SJohn Marino
981*86d7f5d3SJohn Marino inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
982*86d7f5d3SJohn Marino inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
983*86d7f5d3SJohn Marino
984*86d7f5d3SJohn Marino /* Stat the files. Record whether they are directories. */
985*86d7f5d3SJohn Marino
986*86d7f5d3SJohn Marino for (i = 0; i <= 1; i++)
987*86d7f5d3SJohn Marino {
988*86d7f5d3SJohn Marino if (inf[i].desc != -1)
989*86d7f5d3SJohn Marino {
990*86d7f5d3SJohn Marino int stat_result;
991*86d7f5d3SJohn Marino
992*86d7f5d3SJohn Marino if (i && filename_cmp (inf[i].name, inf[0].name) == 0)
993*86d7f5d3SJohn Marino {
994*86d7f5d3SJohn Marino inf[i].stat = inf[0].stat;
995*86d7f5d3SJohn Marino stat_result = 0;
996*86d7f5d3SJohn Marino }
997*86d7f5d3SJohn Marino else if (strcmp (inf[i].name, "-") == 0)
998*86d7f5d3SJohn Marino {
999*86d7f5d3SJohn Marino inf[i].desc = STDIN_FILENO;
1000*86d7f5d3SJohn Marino stat_result = fstat (STDIN_FILENO, &inf[i].stat);
1001*86d7f5d3SJohn Marino if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode))
1002*86d7f5d3SJohn Marino {
1003*86d7f5d3SJohn Marino off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
1004*86d7f5d3SJohn Marino if (pos == -1)
1005*86d7f5d3SJohn Marino stat_result = -1;
1006*86d7f5d3SJohn Marino else
1007*86d7f5d3SJohn Marino {
1008*86d7f5d3SJohn Marino if (pos <= inf[i].stat.st_size)
1009*86d7f5d3SJohn Marino inf[i].stat.st_size -= pos;
1010*86d7f5d3SJohn Marino else
1011*86d7f5d3SJohn Marino inf[i].stat.st_size = 0;
1012*86d7f5d3SJohn Marino /* Posix.2 4.17.6.1.4 requires current time for stdin. */
1013*86d7f5d3SJohn Marino time (&inf[i].stat.st_mtime);
1014*86d7f5d3SJohn Marino }
1015*86d7f5d3SJohn Marino }
1016*86d7f5d3SJohn Marino }
1017*86d7f5d3SJohn Marino else
1018*86d7f5d3SJohn Marino stat_result = stat (inf[i].name, &inf[i].stat);
1019*86d7f5d3SJohn Marino
1020*86d7f5d3SJohn Marino if (stat_result != 0)
1021*86d7f5d3SJohn Marino {
1022*86d7f5d3SJohn Marino perror_with_name (inf[i].name);
1023*86d7f5d3SJohn Marino failed = 1;
1024*86d7f5d3SJohn Marino }
1025*86d7f5d3SJohn Marino else
1026*86d7f5d3SJohn Marino {
1027*86d7f5d3SJohn Marino inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
1028*86d7f5d3SJohn Marino if (inf[1 - i].desc == -1)
1029*86d7f5d3SJohn Marino {
1030*86d7f5d3SJohn Marino inf[1 - i].dir_p = inf[i].dir_p;
1031*86d7f5d3SJohn Marino inf[1 - i].stat.st_mode = inf[i].stat.st_mode;
1032*86d7f5d3SJohn Marino }
1033*86d7f5d3SJohn Marino }
1034*86d7f5d3SJohn Marino }
1035*86d7f5d3SJohn Marino }
1036*86d7f5d3SJohn Marino
1037*86d7f5d3SJohn Marino if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p)
1038*86d7f5d3SJohn Marino {
1039*86d7f5d3SJohn Marino /* If one is a directory, and it was specified in the command line,
1040*86d7f5d3SJohn Marino use the file in that dir with the other file's basename. */
1041*86d7f5d3SJohn Marino
1042*86d7f5d3SJohn Marino int fnm_arg = inf[0].dir_p;
1043*86d7f5d3SJohn Marino int dir_arg = 1 - fnm_arg;
1044*86d7f5d3SJohn Marino char const *fnm = inf[fnm_arg].name;
1045*86d7f5d3SJohn Marino char const *dir = inf[dir_arg].name;
1046*86d7f5d3SJohn Marino char const *p = filename_lastdirchar (fnm);
1047*86d7f5d3SJohn Marino char const *filename = inf[dir_arg].name
1048*86d7f5d3SJohn Marino = dir_file_pathname (dir, p ? p + 1 : fnm);
1049*86d7f5d3SJohn Marino
1050*86d7f5d3SJohn Marino if (strcmp (fnm, "-") == 0)
1051*86d7f5d3SJohn Marino fatal ("can't compare - to a directory");
1052*86d7f5d3SJohn Marino
1053*86d7f5d3SJohn Marino if (stat (filename, &inf[dir_arg].stat) != 0)
1054*86d7f5d3SJohn Marino {
1055*86d7f5d3SJohn Marino perror_with_name (filename);
1056*86d7f5d3SJohn Marino failed = 1;
1057*86d7f5d3SJohn Marino }
1058*86d7f5d3SJohn Marino else
1059*86d7f5d3SJohn Marino inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
1060*86d7f5d3SJohn Marino }
1061*86d7f5d3SJohn Marino
1062*86d7f5d3SJohn Marino if (failed)
1063*86d7f5d3SJohn Marino {
1064*86d7f5d3SJohn Marino
1065*86d7f5d3SJohn Marino /* If either file should exist but does not, return 2. */
1066*86d7f5d3SJohn Marino
1067*86d7f5d3SJohn Marino val = 2;
1068*86d7f5d3SJohn Marino
1069*86d7f5d3SJohn Marino }
1070*86d7f5d3SJohn Marino else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1
1071*86d7f5d3SJohn Marino && 0 < same_file (&inf[0].stat, &inf[1].stat))
1072*86d7f5d3SJohn Marino && no_diff_means_no_output)
1073*86d7f5d3SJohn Marino {
1074*86d7f5d3SJohn Marino /* The two named files are actually the same physical file.
1075*86d7f5d3SJohn Marino We know they are identical without actually reading them. */
1076*86d7f5d3SJohn Marino
1077*86d7f5d3SJohn Marino val = 0;
1078*86d7f5d3SJohn Marino }
1079*86d7f5d3SJohn Marino else if (inf[0].dir_p & inf[1].dir_p)
1080*86d7f5d3SJohn Marino {
1081*86d7f5d3SJohn Marino if (output_style == OUTPUT_IFDEF)
1082*86d7f5d3SJohn Marino fatal ("-D option not supported with directories");
1083*86d7f5d3SJohn Marino
1084*86d7f5d3SJohn Marino /* If both are directories, compare the files in them. */
1085*86d7f5d3SJohn Marino
1086*86d7f5d3SJohn Marino if (depth > 0 && !recursive)
1087*86d7f5d3SJohn Marino {
1088*86d7f5d3SJohn Marino /* But don't compare dir contents one level down
1089*86d7f5d3SJohn Marino unless -r was specified. */
1090*86d7f5d3SJohn Marino message ("Common subdirectories: %s and %s\n",
1091*86d7f5d3SJohn Marino inf[0].name, inf[1].name);
1092*86d7f5d3SJohn Marino val = 0;
1093*86d7f5d3SJohn Marino }
1094*86d7f5d3SJohn Marino else
1095*86d7f5d3SJohn Marino {
1096*86d7f5d3SJohn Marino val = diff_dirs (inf, compare_files, depth);
1097*86d7f5d3SJohn Marino }
1098*86d7f5d3SJohn Marino
1099*86d7f5d3SJohn Marino }
1100*86d7f5d3SJohn Marino else if ((inf[0].dir_p | inf[1].dir_p)
1101*86d7f5d3SJohn Marino || (depth > 0
1102*86d7f5d3SJohn Marino && (! S_ISREG (inf[0].stat.st_mode)
1103*86d7f5d3SJohn Marino || ! S_ISREG (inf[1].stat.st_mode))))
1104*86d7f5d3SJohn Marino {
1105*86d7f5d3SJohn Marino /* Perhaps we have a subdirectory that exists only in one directory.
1106*86d7f5d3SJohn Marino If so, just print a message to that effect. */
1107*86d7f5d3SJohn Marino
1108*86d7f5d3SJohn Marino if (inf[0].desc == -1 || inf[1].desc == -1)
1109*86d7f5d3SJohn Marino {
1110*86d7f5d3SJohn Marino if ((inf[0].dir_p | inf[1].dir_p)
1111*86d7f5d3SJohn Marino && recursive
1112*86d7f5d3SJohn Marino && (entire_new_file_flag
1113*86d7f5d3SJohn Marino || (unidirectional_new_file_flag && inf[0].desc == -1)))
1114*86d7f5d3SJohn Marino val = diff_dirs (inf, compare_files, depth);
1115*86d7f5d3SJohn Marino else
1116*86d7f5d3SJohn Marino {
1117*86d7f5d3SJohn Marino char const *dir = (inf[0].desc == -1) ? dir1 : dir0;
1118*86d7f5d3SJohn Marino /* See Posix.2 section 4.17.6.1.1 for this format. */
1119*86d7f5d3SJohn Marino message ("Only in %s: %s\n", dir, name0);
1120*86d7f5d3SJohn Marino val = 1;
1121*86d7f5d3SJohn Marino }
1122*86d7f5d3SJohn Marino }
1123*86d7f5d3SJohn Marino else
1124*86d7f5d3SJohn Marino {
1125*86d7f5d3SJohn Marino /* We have two files that are not to be compared. */
1126*86d7f5d3SJohn Marino
1127*86d7f5d3SJohn Marino /* See Posix.2 section 4.17.6.1.1 for this format. */
1128*86d7f5d3SJohn Marino message5 ("File %s is a %s while file %s is a %s\n",
1129*86d7f5d3SJohn Marino inf[0].name, filetype (&inf[0].stat),
1130*86d7f5d3SJohn Marino inf[1].name, filetype (&inf[1].stat));
1131*86d7f5d3SJohn Marino
1132*86d7f5d3SJohn Marino /* This is a difference. */
1133*86d7f5d3SJohn Marino val = 1;
1134*86d7f5d3SJohn Marino }
1135*86d7f5d3SJohn Marino }
1136*86d7f5d3SJohn Marino else if ((no_details_flag & ~ignore_some_changes)
1137*86d7f5d3SJohn Marino && inf[0].stat.st_size != inf[1].stat.st_size
1138*86d7f5d3SJohn Marino && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode))
1139*86d7f5d3SJohn Marino && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode)))
1140*86d7f5d3SJohn Marino {
1141*86d7f5d3SJohn Marino message ("Files %s and %s differ\n", inf[0].name, inf[1].name);
1142*86d7f5d3SJohn Marino val = 1;
1143*86d7f5d3SJohn Marino }
1144*86d7f5d3SJohn Marino else
1145*86d7f5d3SJohn Marino {
1146*86d7f5d3SJohn Marino /* Both exist and neither is a directory. */
1147*86d7f5d3SJohn Marino
1148*86d7f5d3SJohn Marino /* Open the files and record their descriptors. */
1149*86d7f5d3SJohn Marino
1150*86d7f5d3SJohn Marino if (inf[0].desc == -2)
1151*86d7f5d3SJohn Marino if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
1152*86d7f5d3SJohn Marino {
1153*86d7f5d3SJohn Marino perror_with_name (inf[0].name);
1154*86d7f5d3SJohn Marino failed = 1;
1155*86d7f5d3SJohn Marino }
1156*86d7f5d3SJohn Marino if (inf[1].desc == -2)
1157*86d7f5d3SJohn Marino {
1158*86d7f5d3SJohn Marino if (same_files)
1159*86d7f5d3SJohn Marino inf[1].desc = inf[0].desc;
1160*86d7f5d3SJohn Marino else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
1161*86d7f5d3SJohn Marino {
1162*86d7f5d3SJohn Marino perror_with_name (inf[1].name);
1163*86d7f5d3SJohn Marino failed = 1;
1164*86d7f5d3SJohn Marino }
1165*86d7f5d3SJohn Marino }
1166*86d7f5d3SJohn Marino
1167*86d7f5d3SJohn Marino #if HAVE_SETMODE
1168*86d7f5d3SJohn Marino if (binary_I_O)
1169*86d7f5d3SJohn Marino for (i = 0; i <= 1; i++)
1170*86d7f5d3SJohn Marino if (0 <= inf[i].desc)
1171*86d7f5d3SJohn Marino setmode (inf[i].desc, O_BINARY);
1172*86d7f5d3SJohn Marino #endif
1173*86d7f5d3SJohn Marino
1174*86d7f5d3SJohn Marino /* Compare the files, if no error was found. */
1175*86d7f5d3SJohn Marino
1176*86d7f5d3SJohn Marino val = failed ? 2 : diff_2_files (inf, depth);
1177*86d7f5d3SJohn Marino
1178*86d7f5d3SJohn Marino /* Close the file descriptors. */
1179*86d7f5d3SJohn Marino
1180*86d7f5d3SJohn Marino if (inf[0].desc >= 0 && close (inf[0].desc) != 0)
1181*86d7f5d3SJohn Marino {
1182*86d7f5d3SJohn Marino perror_with_name (inf[0].name);
1183*86d7f5d3SJohn Marino val = 2;
1184*86d7f5d3SJohn Marino }
1185*86d7f5d3SJohn Marino if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc
1186*86d7f5d3SJohn Marino && close (inf[1].desc) != 0)
1187*86d7f5d3SJohn Marino {
1188*86d7f5d3SJohn Marino perror_with_name (inf[1].name);
1189*86d7f5d3SJohn Marino val = 2;
1190*86d7f5d3SJohn Marino }
1191*86d7f5d3SJohn Marino }
1192*86d7f5d3SJohn Marino
1193*86d7f5d3SJohn Marino /* Now the comparison has been done, if no error prevented it,
1194*86d7f5d3SJohn Marino and VAL is the value this function will return. */
1195*86d7f5d3SJohn Marino
1196*86d7f5d3SJohn Marino if (val == 0 && !inf[0].dir_p)
1197*86d7f5d3SJohn Marino {
1198*86d7f5d3SJohn Marino if (print_file_same_flag)
1199*86d7f5d3SJohn Marino message ("Files %s and %s are identical\n",
1200*86d7f5d3SJohn Marino inf[0].name, inf[1].name);
1201*86d7f5d3SJohn Marino }
1202*86d7f5d3SJohn Marino else
1203*86d7f5d3SJohn Marino flush_output ();
1204*86d7f5d3SJohn Marino
1205*86d7f5d3SJohn Marino if (free0)
1206*86d7f5d3SJohn Marino free (free0);
1207*86d7f5d3SJohn Marino if (free1)
1208*86d7f5d3SJohn Marino free (free1);
1209*86d7f5d3SJohn Marino
1210*86d7f5d3SJohn Marino return val;
1211*86d7f5d3SJohn Marino }
1212*86d7f5d3SJohn Marino
1213*86d7f5d3SJohn Marino /* Initialize status variables and flag variables used in libdiff,
1214*86d7f5d3SJohn Marino to permit repeated calls to diff_run. */
1215*86d7f5d3SJohn Marino
1216*86d7f5d3SJohn Marino static void
initialize_main(argcp,argvp)1217*86d7f5d3SJohn Marino initialize_main (argcp, argvp)
1218*86d7f5d3SJohn Marino int *argcp;
1219*86d7f5d3SJohn Marino char ***argvp;
1220*86d7f5d3SJohn Marino {
1221*86d7f5d3SJohn Marino /* These variables really must be reset each time diff_run is called. */
1222*86d7f5d3SJohn Marino output_style = OUTPUT_NORMAL;
1223*86d7f5d3SJohn Marino context = -1;
1224*86d7f5d3SJohn Marino file_label[0] = NULL;
1225*86d7f5d3SJohn Marino file_label[1] = NULL;
1226*86d7f5d3SJohn Marino diff_program_name = (*argvp)[0];
1227*86d7f5d3SJohn Marino outfile = NULL;
1228*86d7f5d3SJohn Marino
1229*86d7f5d3SJohn Marino /* Reset these also, just for safety's sake. (If one invocation turns
1230*86d7f5d3SJohn Marino on ignore_case_flag, it must be turned off before diff_run is called
1231*86d7f5d3SJohn Marino again. But it is possible to make many diffs before encountering
1232*86d7f5d3SJohn Marino such a problem. */
1233*86d7f5d3SJohn Marino recursive = 0;
1234*86d7f5d3SJohn Marino no_discards = 0;
1235*86d7f5d3SJohn Marino #if HAVE_SETMODE
1236*86d7f5d3SJohn Marino binary_I_O = 0;
1237*86d7f5d3SJohn Marino #endif
1238*86d7f5d3SJohn Marino no_diff_means_no_output = 0;
1239*86d7f5d3SJohn Marino always_text_flag = 0;
1240*86d7f5d3SJohn Marino horizon_lines = 0;
1241*86d7f5d3SJohn Marino ignore_space_change_flag = 0;
1242*86d7f5d3SJohn Marino ignore_all_space_flag = 0;
1243*86d7f5d3SJohn Marino ignore_blank_lines_flag = 0;
1244*86d7f5d3SJohn Marino ignore_some_line_changes = 0;
1245*86d7f5d3SJohn Marino ignore_some_changes = 0;
1246*86d7f5d3SJohn Marino ignore_case_flag = 0;
1247*86d7f5d3SJohn Marino function_regexp_list = NULL;
1248*86d7f5d3SJohn Marino ignore_regexp_list = NULL;
1249*86d7f5d3SJohn Marino no_details_flag = 0;
1250*86d7f5d3SJohn Marino print_file_same_flag = 0;
1251*86d7f5d3SJohn Marino tab_align_flag = 0;
1252*86d7f5d3SJohn Marino tab_expand_flag = 0;
1253*86d7f5d3SJohn Marino dir_start_file = NULL;
1254*86d7f5d3SJohn Marino entire_new_file_flag = 0;
1255*86d7f5d3SJohn Marino unidirectional_new_file_flag = 0;
1256*86d7f5d3SJohn Marino paginate_flag = 0;
1257*86d7f5d3SJohn Marino bzero (group_format, sizeof (group_format));
1258*86d7f5d3SJohn Marino bzero (line_format, sizeof (line_format));
1259*86d7f5d3SJohn Marino sdiff_help_sdiff = 0;
1260*86d7f5d3SJohn Marino sdiff_left_only = 0;
1261*86d7f5d3SJohn Marino sdiff_skip_common_lines = 0;
1262*86d7f5d3SJohn Marino sdiff_half_width = 0;
1263*86d7f5d3SJohn Marino sdiff_column2_offset = 0;
1264*86d7f5d3SJohn Marino switch_string = NULL;
1265*86d7f5d3SJohn Marino heuristic = 0;
1266*86d7f5d3SJohn Marino bzero (files, sizeof (files));
1267*86d7f5d3SJohn Marino }
1268