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