1 /*
2 * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * Sponsored in part by the Defense Advanced Research Projects
17 * Agency (DARPA) and Air Force Research Laboratory, Air Force
18 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
19 */
20
21 #if 0
22 #ifndef lint
23 static char sccsid[] = "@(#)diff.c 8.1 (Berkeley) 6/6/93";
24 #endif
25 #endif /* not lint */
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <sys/stat.h>
31
32 #include <ctype.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "diff.h"
44 #include "pathnames.h"
45
46 int aflag, bflag, cflag, dflag, Eflag, iflag, lflag, Nflag, Pflag, pflag, rflag;
47 int sflag, tflag, Tflag, wflag, Toflag, Fromflag;
48 int Bflag, yflag;
49 int strip_cr, suppress_cl, tabsize = 8;
50 char ignore_file_case = 0;
51 int format, context, status;
52 char *start, *ifdefname, *diffargs, *label[2], *ignore_pats, *line_format, *group_format;
53 struct stat stb1, stb2;
54 struct excludes *excludes_list;
55 regex_t ignore_re;
56
57 int flag_opts = 0;
58
59 #define OPTIONS "0123456789aBbC:cdD:EefhI:iL:lnNPpqrS:sTtU:uvwXy:x"
60
61
62 /* Options which exceed manageable alphanumeric assignments */
63 enum
64 {
65 OPT_IGN_FN_CASE = CHAR_MAX + 1,
66 OPT_NIGN_FN_CASE,
67 OPT_STRIPCR,
68 OPT_NORMAL,
69 OPT_LEFTC,
70 OPT_SUPCL,
71 OPT_CHGD_GF,
72 OPT_NEW_GF,
73 OPT_OLD_GF,
74 OPT_UNCHGD_GF,
75 OPT_LF,
76 OPT_LLF,
77 OPT_TSIZE,
78 OPT_FFILE,
79 OPT_TOFILE,
80 OPT_HLINES,
81 OPT_LFILES,
82 OPT_HELP,
83 OPT_NEW_LF,
84 OPT_OLD_LF,
85 OPT_UNCHGD_LF,
86 };
87
88
89 static struct option longopts[] = {
90
91 /*
92 * Commented-out options are unimplemented.
93 */
94
95 { "brief", no_argument, NULL, 'q' },
96 { "changed-group-format", required_argument, NULL, OPT_CHGD_GF},
97 { "context", optional_argument, NULL, 'C' },
98 { "ed", no_argument, NULL, 'e' },
99 { "exclude", required_argument, NULL, 'x' },
100 { "exclude-from", required_argument, NULL, 'X' },
101 { "expand-tabs", no_argument, NULL, 't' },
102 { "from-file", required_argument, NULL, OPT_FFILE },
103 { "forward-ed", no_argument, NULL, 'f' },
104 { "help", no_argument, NULL, OPT_HELP },
105 /*{ "horizon-lines", required_argument, NULL, OPT_HLINES },*/
106 { "ifdef", required_argument, NULL, 'D' },
107 { "ignore-all-space", no_argument, NULL, 'w' },
108 { "ignore-blank-lines", no_argument, NULL, 'B' },
109 { "ignore-case", no_argument, NULL, 'i' },
110 { "ignore-file-name-case", no_argument, NULL, OPT_IGN_FN_CASE },
111 { "ignore-matching-lines", required_argument, NULL, 'I' },
112 { "ignore-space-change", no_argument, NULL, 'b' },
113 { "ignore-tab-expansion", no_argument, NULL, 'E' },
114 { "initial-tab", no_argument, NULL, 'T' },
115 { "label", required_argument, NULL, 'L' },
116 { "left-column", no_argument, NULL, OPT_LEFTC },
117 { "line-format", required_argument, NULL, OPT_LF },
118 { "minimal", no_argument, NULL, 'd' },
119 { "new-file", no_argument, NULL, 'N' },
120 { "new-line-format", required_argument, NULL, OPT_NEW_LF},
121 { "new-group-format", required_argument, NULL, OPT_NEW_GF},
122 { "no-ignore-file-name-case", no_argument, NULL, OPT_NIGN_FN_CASE },
123 { "normal", no_argument, NULL, OPT_NORMAL },
124 { "old-line-format", required_argument, NULL, OPT_OLD_LF},
125 { "old-group-format", required_argument, NULL, OPT_OLD_GF},
126 { "paginate", no_argument, NULL, 'l' },
127 { "recursive", no_argument, NULL, 'r' },
128 { "report-identical-files", no_argument, NULL, 's' },
129 { "rcs", no_argument, NULL, 'n' },
130 { "show-c-function", no_argument, NULL, 'p' },
131 { "show-function-line", required_argument, NULL, 'F' },
132 { "side-by-side", no_argument, NULL, 'y' },
133 /*{ "speed-large-files", no_argument, NULL, OPT_LFILES }, */
134 { "starting-file", required_argument, NULL, 'S' },
135 { "strip-trailing-cr", no_argument, NULL, OPT_STRIPCR },
136 { "suppress-common-lines", no_argument, NULL, OPT_SUPCL },
137 { "tabsize", optional_argument, NULL, OPT_TSIZE },
138 { "text", no_argument, NULL, 'a' },
139 { "to-file", required_argument, NULL, OPT_TOFILE },
140 { "unchanged-group-format", required_argument, NULL, OPT_UNCHGD_GF},
141 { "unchanged-line-format", required_argument, NULL, OPT_UNCHGD_LF},
142 { "unidirectional-new-file", no_argument, NULL, 'P' },
143 { "unified", optional_argument, NULL, 'U' },
144 { "version", no_argument, NULL, 'v' },
145 /*{ "width", optional_argument, NULL, 'W' }, */
146 { NULL, 0, NULL, '\0'}
147 };
148
149 static const char *help_msg[] = {
150 "\t-a --text treat files as ASCII text",
151 "\t-B --ignore-blank-lines Ignore blank newlines in the comparison",
152 "\t-b --ignore-space-change Ignore all changes due to whitespace",
153 "\t-C -c NUM --context=NUM Show NUM lines before and after change (default 3)",
154 "\t-D --ifdef=NAME Output merged file with `#ifdef NAME' diffs",
155 "\t-E --ignore-tab-expansion Ignore tab expansion in the comparison",
156 "\t-e --ed Output an ed script",
157 "\t-F --show-function-line=RE Show the most recent line matching RE",
158 "\t-f --forward-ed Output a forward ed script",
159 "\t-I --ignore-matching-lines=RE Ignore changes whose lines all match RE",
160 "\t-i --ignore-case Ignore case differences in file contents",
161 "\t-L --label=NAME Label file header",
162 "\t-l --paginate Paginates output through pr",
163 "\t-N --new-file Treat new files as empty",
164 "\t-n --rcs Output an RCS format diff",
165 "\t-P --unidirectional-new-file Treat absent-first files as empty",
166 "\t-p --show-c-function Show which C function each change is in",
167 "\t-q --brief report only when files differ",
168 "\t-r --recursive Recursively compare any sub-directories found",
169 "\t-S --starting-file=FILE Start with FILE when comparing directories",
170 "\t-s --report-identical-files Report when two files are the same",
171 "\t-T --initial-tab Make tabs line up by prepending a tab",
172 "\t-t --expand-tabs Expand tabs to spaces in output",
173 "\t-U -u NUM --unified=NUM Show NUM lines of unified context",
174 "\t-v --version Show diff version",
175 "\t-w --width=NUM Output at most NUM (default 130) print columns",
176 "\t-X --exclude-from=FILE Start with FILE when comparing directories",
177 "\t-x --exclude=PAT Exclude files that match PAT",
178 "\t-y --side-by-side Output difference in two columns",
179 "\t--GTYPE-group-format=GFMT Format GTYPE input groups with GFMT",
180 "\t--LTYPE-line-format=LFMT Format LTYPE input lines with LFMT",
181 "\t--from-file=FILE Compare FILE to all operands",
182 "\t--to-file=FILE Compare all operands to FILE",
183 "\t--ignore-file-name-case Ignore file name case",
184 "\t--left-column Output the only the left column of common lines",
185 "\t--line-format=LFMT Format all input lines with LFMT",
186 "\t--no-ignore-file-name-case Do not ignore file name case",
187 "\t--normal Output a normal diff (default output)",
188 "\t--strip-trailing-cr Strip trailing carriage return",
189 "\t--suppress-common-lines Do not output common lines",
190 "\t--tabsize=NUM Tab stops every NUM (default 8) print columns",
191 "\t--help Output this help message",
192 NULL,
193 };
194 char **help_strs = (char **)help_msg;
195
196 void set_argstr(char **, char **);
197
198
199 void usage(void);
200 void push_excludes(char *);
201 void push_ignore_pats(char *);
202 void read_excludes_file(char *);
203
204 int
main(int argc,char ** argv)205 main(int argc, char **argv)
206 {
207 char *ep, **oargv, *optfile;
208 long l;
209 int ch, lastch, gotstdin, prevoptind, newarg;
210 int oargc;
211
212 oargv = argv;
213 oargc = argc;
214 gotstdin = 0;
215 optfile = "\0";
216
217 lastch = '\0';
218 prevoptind = 1;
219 newarg = 1;
220 while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
221 switch (ch) {
222 case '0': case '1': case '2': case '3': case '4':
223 case '5': case '6': case '7': case '8': case '9':
224 if (newarg)
225 usage(); /* disallow -[0-9]+ */
226 else if (lastch == 'c' || lastch == 'u')
227 context = 0;
228 else if (!isdigit(lastch) || context > INT_MAX / 10)
229 usage();
230 context = (context * 10) + (ch - '0');
231 break;
232 case 'a':
233 aflag = 1;
234 break;
235 case 'b':
236 bflag = 1;
237 break;
238 case 'B':
239 Bflag = 1;
240 break;
241 case 'C':
242 case 'c':
243 cflag = 1;
244 format = D_CONTEXT;
245 if (optarg != NULL) {
246 l = strtol(optarg, &ep, 10);
247 if (*ep != '\0' || l < 0 || l >= INT_MAX)
248 usage();
249 context = (int)l;
250 } else
251 context = 3;
252 break;
253 case 'D':
254 format = D_IFDEF;
255 ifdefname = optarg;
256 break;
257 case 'd':
258 dflag = 1;
259 break;
260 case 'E':
261 Eflag = 1;
262 break;
263 case 'e':
264 format = D_EDIT;
265 break;
266 case 'f':
267 format = D_REVERSE;
268 break;
269 case 'h':
270 /* silently ignore for backwards compatibility */
271 break;
272 case 'I':
273 push_ignore_pats(optarg);
274 break;
275 case 'i':
276 iflag = 1;
277 break;
278 case 'L':
279 if (label[0] == NULL)
280 label[0] = optarg;
281 else if (label[1] == NULL)
282 label[1] = optarg;
283 else
284 usage();
285 break;
286 case 'l':
287 lflag = 1;
288 signal(SIGPIPE, SIG_IGN);
289 break;
290 case 'N':
291 Nflag = 1;
292 break;
293 case 'n':
294 format = D_NREVERSE;
295 break;
296 case 'P':
297 Pflag = 1;
298 break;
299 case 'p':
300 pflag = 1;
301 break;
302 case 'r':
303 rflag = 1;
304 break;
305 case 'q':
306 format = D_BRIEF;
307 break;
308 case 'S':
309 start = optarg;
310 break;
311 case 's':
312 sflag = 1;
313 break;
314 case 'T':
315 Tflag = 1;
316 break;
317 case 't':
318 tflag = 1;
319 break;
320 case 'U':
321 case 'u':
322 format = D_UNIFIED;
323 if (optarg != NULL) {
324 l = strtol(optarg, &ep, 10);
325 if (*ep != '\0' || l < 0 || l >= INT_MAX)
326 usage();
327 context = (int)l;
328 } else
329 context = 3;
330 break;
331 case 'v':
332 printf("FreeBSD diff 2.8.7\n");
333 exit(0);
334 case 'w':
335 wflag = 1;
336 break;
337 case 'X':
338 read_excludes_file(optarg);
339 break;
340 case 'x':
341 push_excludes(optarg);
342 break;
343 case 'y':
344 yflag = 1;
345 break;
346 case OPT_FFILE:
347 Toflag = 1;
348 optfile = optarg;
349 break;
350 case OPT_TOFILE:
351 Fromflag = 1;
352 optfile = optarg;
353 break;
354 case OPT_CHGD_GF:
355 case OPT_NEW_GF:
356 case OPT_OLD_GF:
357 case OPT_UNCHGD_GF:
358 /* XXX To do: Complete --GTYPE-group-format. */
359 format = D_GF;
360 group_format = optarg;
361 break;
362 case OPT_NEW_LF:
363 case OPT_OLD_LF:
364 case OPT_UNCHGD_LF:
365 case OPT_LF:
366 /* XXX To do: Complete --line-format. */
367 format = D_LF;
368 line_format = optarg;
369 break;
370 case OPT_NORMAL:
371 format = D_NORMAL;
372 break;
373 case OPT_LEFTC:
374 /* Do nothing, passes option to sdiff. */
375 break;
376 case OPT_SUPCL:
377 /* Do nothing, passes option to sdiff. */
378 break;
379 case OPT_TSIZE:
380 if (optarg != NULL) {
381 l = strtol(optarg, &ep, 10);
382 if (*ep != '\0' || l < 1 || l >= INT_MAX)
383 usage();
384 tabsize = (int)l;
385 } else
386 tabsize = 8;
387 break;
388 case OPT_STRIPCR:
389 strip_cr=1;
390 break;
391 case OPT_IGN_FN_CASE:
392 ignore_file_case = 1;
393 break;
394 case OPT_NIGN_FN_CASE:
395 ignore_file_case = 0;
396 break;
397 case OPT_HELP:
398 for(;*help_strs;help_strs++)
399 {
400 printf("%s\n", *help_strs);
401 }
402 exit(2);
403 break;
404 default:
405 usage();
406 break;
407 }
408 lastch = ch;
409 newarg = optind != prevoptind;
410 prevoptind = optind;
411
412 }
413 argc -= optind;
414 argv += optind;
415 if (yflag) {
416 /* remove y flag from args and call sdiff */
417 for (argv = oargv; argv && strcmp(*argv, "-y") != 0 &&
418 strcmp(*argv, "--side-by-side") != 0; argv++);
419 while(argv != &oargv[oargc]){
420 *argv= *(argv+1);
421 argv++;
422 }
423 oargv[0] = _PATH_SDIFF;
424 *argv= "\0";
425 execv(_PATH_SDIFF, oargv);
426 _exit(127);
427 }
428
429 /*
430 * Do sanity checks, fill in stb1 and stb2 and call the appropriate
431 * driver routine. Both drivers use the contents of stb1 and stb2.
432 */
433 if (argc != 2)
434 usage();
435 if (ignore_pats != NULL) {
436 char buf[BUFSIZ];
437 int error;
438
439 if ((error = regcomp(&ignore_re, ignore_pats,
440 REG_NEWLINE | REG_EXTENDED)) != 0) {
441 regerror(error, &ignore_re, buf, sizeof(buf));
442 if (*ignore_pats != '\0')
443 errx(2, "%s: %s", ignore_pats, buf);
444 else
445 errx(2, "%s", buf);
446 }
447 }
448 if (strcmp(argv[0], "-") == 0) {
449 fstat(STDIN_FILENO, &stb1);
450 gotstdin = 1;
451 } else if (stat(argv[0], &stb1) != 0)
452 err(2, "%s", argv[0]);
453 if (strcmp(argv[1], "-") == 0) {
454 fstat(STDIN_FILENO, &stb2);
455 gotstdin = 1;
456 } else if (stat(argv[1], &stb2) != 0)
457 err(2, "%s", argv[1]);
458 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
459 errx(2, "can't compare - to a directory");
460 set_argstr(oargv, argv);
461 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
462 if (format == D_IFDEF)
463 if (ch == 'D')
464 errx(2, "-D option not supported with directories");
465 if (ch == OPT_LF)
466 errx(2, "--line-format option not supported with directories");
467 diffdir(argv[0], argv[1]);
468 } else {
469 if (S_ISDIR(stb1.st_mode)) {
470 argv[0] = splice(argv[0], argv[1]);
471 if (stat(argv[0], &stb1) < 0)
472 err(2, "%s", argv[0]);
473 }
474 if (S_ISDIR(stb2.st_mode)) {
475 argv[1] = splice(argv[1], argv[0]);
476 if (stat(argv[1], &stb2) < 0)
477 err(2, "%s", argv[1]);
478 }
479 /* Checks if --to-file or --from-file are specified */
480 if (Toflag && Fromflag) {
481 (void)fprintf(stderr, "--from-file and --to-file both specified.\n");
482 exit(2);
483 }
484 if (Toflag) {
485 print_status(diffreg(optfile, argv[0], 0), optfile, argv[0],
486 NULL);
487 print_status(diffreg(optfile, argv[1], 0), optfile, argv[1],
488 NULL);
489 }
490 if (Fromflag) {
491 print_status(diffreg(argv[0], optfile, 0), argv[0], optfile,
492 NULL);
493 print_status(diffreg(argv[1], optfile, 0), argv[1], optfile,
494 NULL);
495 }
496 if (!Toflag && !Fromflag)
497 print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1],
498 NULL);
499 }
500 exit(status);
501 }
502
503 void *
emalloc(size_t n)504 emalloc(size_t n)
505 {
506 void *p;
507
508 if (n == 0)
509 errx(2, NULL);
510 if ((p = malloc(n)) == NULL)
511 errx(2, NULL);
512 return (p);
513 }
514
515 void *
erealloc(void * p,size_t n)516 erealloc(void *p, size_t n)
517 {
518 void *q;
519
520 if (n == 0)
521 errx(2, NULL);
522 if (p == NULL)
523 q = malloc(n);
524 else
525 q = realloc(p, n);
526 if (q == NULL)
527 errx(2, NULL);
528 return (q);
529 }
530
531 int
easprintf(char ** ret,const char * fmt,...)532 easprintf(char **ret, const char *fmt, ...)
533 {
534 int len;
535 va_list ap;
536
537 va_start(ap, fmt);
538 len = vasprintf(ret, fmt, ap);
539 va_end(ap);
540 if (len < 0 || *ret == NULL)
541 errx(2, NULL);
542 return (len);
543 }
544
545 char *
estrdup(const char * str)546 estrdup(const char *str)
547 {
548 size_t len;
549 char *cp;
550
551 len = strlen(str) + 1;
552 cp = emalloc(len);
553
554 strlcpy(cp, str, len);
555 return (cp);
556 }
557
558 void
set_argstr(char ** av,char ** ave)559 set_argstr(char **av, char **ave)
560 {
561 size_t argsize;
562 char **ap;
563
564 argsize = 4 + *ave - *av + 1;
565 diffargs = emalloc(argsize);
566 strlcpy(diffargs, "diff", argsize);
567 for (ap = av + 1; ap < ave; ap++) {
568 if (strcmp(*ap, "--") != 0) {
569 strlcat(diffargs, " ", argsize);
570 strlcat(diffargs, *ap, argsize);
571 }
572 }
573 }
574
575 /*
576 * Read in an excludes file and push each line.
577 */
578 void
read_excludes_file(char * file)579 read_excludes_file(char *file)
580 {
581 FILE *fp;
582 char *buf, *pattern;
583 size_t len;
584
585 if (strcmp(file, "-") == 0)
586 fp = stdin;
587 else if ((fp = fopen(file, "r")) == NULL)
588 err(2, "%s", file);
589 while ((buf = fgetln(fp, &len)) != NULL) {
590 if (buf[len - 1] == '\n')
591 len--;
592 pattern = emalloc(len + 1);
593 memcpy(pattern, buf, len);
594 pattern[len] = '\0';
595 push_excludes(pattern);
596 }
597 if (strcmp(file, "-") != 0)
598 fclose(fp);
599 }
600
601 /*
602 * Push a pattern onto the excludes list.
603 */
604 void
push_excludes(char * pattern)605 push_excludes(char *pattern)
606 {
607 struct excludes *entry;
608
609 entry = emalloc(sizeof(*entry));
610 entry->pattern = pattern;
611 entry->next = excludes_list;
612 excludes_list = entry;
613 }
614
615 void
push_ignore_pats(char * pattern)616 push_ignore_pats(char *pattern)
617 {
618 size_t len;
619
620 if (ignore_pats == NULL)
621 ignore_pats = estrdup(pattern);
622 else {
623 /* old + "|" + new + NUL */
624 len = strlen(ignore_pats) + strlen(pattern) + 2;
625 ignore_pats = erealloc(ignore_pats, len);
626 strlcat(ignore_pats, "|", len);
627 strlcat(ignore_pats, pattern, len);
628 }
629 }
630
631 void
print_only(const char * path,size_t dirlen,const char * entry)632 print_only(const char *path, size_t dirlen, const char *entry)
633 {
634
635 if (dirlen > 1)
636 dirlen--;
637 printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
638 }
639
640 void
print_status(int val,char * path1,char * path2,char * entry)641 print_status(int val, char *path1, char *path2, char *entry)
642 {
643
644 switch (val) {
645 case D_ONLY:
646 print_only(path1, strlen(path1), entry);
647 break;
648 case D_COMMON:
649 printf("Common subdirectories: %s%s and %s%s\n",
650 path1, entry ? entry : "", path2, entry ? entry : "");
651 break;
652 case D_BINARY:
653 printf("Files %s%s and %s%s differ\n",
654 path1, entry ? entry : "", path2, entry ? entry : "");
655 break;
656 case D_DIFFER:
657 if (format == D_BRIEF)
658 printf("Files %s%s and %s%s differ\n",
659 path1, entry ? entry : "",
660 path2, entry ? entry : "");
661 break;
662 case D_SAME:
663 if (sflag)
664 printf("Files %s%s and %s%s are identical\n",
665 path1, entry ? entry : "",
666 path2, entry ? entry : "");
667 break;
668 case D_MISMATCH1:
669 printf("File %s%s is a directory while file %s%s is a regular file\n",
670 path1, entry ? entry : "", path2, entry ? entry : "");
671 break;
672 case D_MISMATCH2:
673 printf("File %s%s is a regular file while file %s%s is a directory\n",
674 path1, entry ? entry : "", path2, entry ? entry : "");
675 break;
676 case D_SKIPPED1:
677 printf("File %s%s is not a regular file or directory and was skipped\n",
678 path1, entry ? entry : "");
679 break;
680 case D_SKIPPED2:
681 printf("File %s%s is not a regular file or directory and was skipped\n",
682 path2, entry ? entry : "");
683 break;
684 }
685 }
686
687 void
usage(void)688 usage(void)
689 {
690
691 (void)fprintf(stderr,
692 "usage: diff [-abdilpqTtw] [-I pattern] [-c | -e | -f | -n | -u]\n"
693 " [-L label] file1 file2\n"
694 " diff [-abdilpqTtw] [-I pattern] [-L label] -C number file1 file2\n"
695 " diff [-abdilqtw] [-I pattern] -D string file1 file2\n"
696 " diff [-abdilpqTtw] [-I pattern] [-L label] -U number file1 file2\n"
697 " diff [-abdilNPpqrsTtw] [-I pattern] [-c | -e | -f | -n | -u]\n"
698 " [-L label] [-S name] [-X file] [-x pattern] dir1 dir2\n"
699 " diff [-v]\n");
700
701 exit(2);
702 }
703