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