Annotation of src/usr.bin/diff/diff.c, Revision 1.34
1.34 ! millert 1: /* $OpenBSD: diff.c,v 1.33 2003/07/22 01:16:01 millert Exp $ */
1.2 deraadt 2:
3: /*
1.23 millert 4: * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
1.2 deraadt 5: *
1.23 millert 6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
1.2 deraadt 9: *
1.23 millert 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: *
18: * Sponsored in part by the Defense Advanced Research Projects
19: * Agency (DARPA) and Air Force Research Laboratory, Air Force
20: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.2 deraadt 21: */
22:
1.23 millert 23: #ifndef lint
1.34 ! millert 24: static const char rcsid[] = "$OpenBSD: diff.c,v 1.33 2003/07/22 01:16:01 millert Exp $";
1.23 millert 25: #endif /* not lint */
26:
27: #include <sys/param.h>
28: #include <sys/stat.h>
29:
30: #include <err.h>
1.15 millert 31: #include <errno.h>
1.23 millert 32: #include <getopt.h>
1.28 millert 33: #include <signal.h>
1.3 tedu 34: #include <stdlib.h>
1.23 millert 35: #include <stdio.h>
1.15 millert 36: #include <stdarg.h>
1.18 david 37: #include <string.h>
1.3 tedu 38: #include <unistd.h>
1.1 deraadt 39:
40: #include "diff.h"
1.12 tedu 41:
1.33 millert 42: int aflag, bflag, iflag, lflag, Nflag, Pflag, rflag, sflag, tflag, Tflag,
43: wflag;
1.23 millert 44: int format, context, status;
1.33 millert 45: char *start, *ifdefname, *diffargs, *label;
1.23 millert 46: struct stat stb1, stb2;
47: struct excludes *excludes_list;
48:
1.33 millert 49: #define OPTIONS "abC:cD:efhiL:lnNPqrS:sTtU:uwX:x:"
1.23 millert 50: static struct option longopts[] = {
51: { "text", no_argument, 0, 'a' },
52: { "ignore-space-change", no_argument, 0, 'b' },
53: { "context", optional_argument, 0, 'C' },
54: { "ifdef", required_argument, 0, 'D' },
55: { "ed", no_argument, 0, 'e' },
56: { "forward-ed", no_argument, 0, 'f' },
57: { "ignore-case", no_argument, 0, 'i' },
1.27 millert 58: { "paginate", no_argument, 0, 'l' },
1.33 millert 59: { "label", required_argument, 0, 'L' },
1.23 millert 60: { "new-file", no_argument, 0, 'N' },
61: { "rcs", no_argument, 0, 'n' },
1.24 millert 62: { "unidirectional-new-file", no_argument, 0, 'P' },
1.25 millert 63: { "brief", no_argument, 0, 'q' },
1.23 millert 64: { "recursive", no_argument, 0, 'r' },
65: { "report-identical-files", no_argument, 0, 's' },
66: { "starting-file", required_argument, 0, 'S' },
67: { "expand-tabs", no_argument, 0, 't' },
1.33 millert 68: { "intial-tab", no_argument, 0, 'T' },
1.23 millert 69: { "unified", optional_argument, 0, 'U' },
70: { "ignore-all-space", no_argument, 0, 'w' },
71: { "exclude", required_argument, 0, 'x' },
72: { "exclude-from", required_argument, 0, 'X' },
1.34 ! millert 73: { NULL, 0, 0, '\0'}
1.23 millert 74: };
1.1 deraadt 75:
1.6 millert 76: __dead void usage(void);
1.23 millert 77: void push_excludes(char *);
78: void read_excludes_file(char *file);
79: void set_argstr(char **, char **);
1.3 tedu 80:
81: int
82: main(int argc, char **argv)
1.1 deraadt 83: {
1.23 millert 84: char *ep, **oargv;
85: long l;
86: int ch, gotstdin;
1.1 deraadt 87:
1.23 millert 88: oargv = argv;
89: gotstdin = 0;
1.6 millert 90:
1.23 millert 91: while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
1.6 millert 92: switch (ch) {
1.12 tedu 93: case 'a':
1.23 millert 94: aflag = 1;
1.12 tedu 95: break;
1.6 millert 96: case 'b':
1.23 millert 97: bflag = 1;
1.6 millert 98: break;
99: case 'C':
100: case 'c':
1.23 millert 101: format = D_CONTEXT;
102: if (optarg != NULL) {
103: l = strtol(optarg, &ep, 10);
104: if (*ep != '\0' || l < 0 || l >= INT_MAX)
105: usage();
106: context = (int)l;
107: } else
108: context = 3;
1.6 millert 109: break;
110: case 'D':
1.23 millert 111: format = D_IFDEF;
1.17 millert 112: ifdefname = optarg;
1.6 millert 113: break;
114: case 'e':
1.23 millert 115: format = D_EDIT;
1.6 millert 116: break;
117: case 'f':
1.23 millert 118: format = D_REVERSE;
1.6 millert 119: break;
1.22 millert 120: case 'h':
121: /* silently ignore for backwards compatibility */
122: break;
1.6 millert 123: case 'i':
1.23 millert 124: iflag = 1;
125: break;
1.33 millert 126: case 'L':
127: label = optarg;
128: break;
1.27 millert 129: case 'l':
130: lflag = 1;
1.28 millert 131: signal(SIGPIPE, SIG_IGN);
1.27 millert 132: break;
1.23 millert 133: case 'N':
134: Nflag = 1;
1.6 millert 135: break;
136: case 'n':
1.23 millert 137: format = D_NREVERSE;
1.6 millert 138: break;
1.24 millert 139: case 'P':
140: Pflag = 1;
141: break;
1.6 millert 142: case 'r':
1.23 millert 143: rflag = 1;
1.6 millert 144: break;
1.25 millert 145: case 'q':
146: format = D_BRIEF;
147: break;
1.6 millert 148: case 'S':
149: start = optarg;
150: break;
151: case 's':
1.23 millert 152: sflag = 1;
1.6 millert 153: break;
1.33 millert 154: case 'T':
155: Tflag = 1;
156: break;
1.6 millert 157: case 't':
1.23 millert 158: tflag = 1;
1.6 millert 159: break;
1.9 millert 160: case 'U':
161: case 'u':
1.23 millert 162: format = D_UNIFIED;
163: if (optarg != NULL) {
164: l = strtol(optarg, &ep, 10);
165: if (*ep != '\0' || l < 0 || l >= INT_MAX)
166: usage();
167: context = (int)l;
168: } else
169: context = 3;
1.9 millert 170: break;
1.6 millert 171: case 'w':
1.23 millert 172: wflag = 1;
173: break;
174: case 'X':
175: read_excludes_file(optarg);
176: break;
177: case 'x':
178: push_excludes(optarg);
1.6 millert 179: break;
180: default:
181: usage();
182: break;
183: }
1.1 deraadt 184: }
1.6 millert 185: argc -= optind;
186: argv += optind;
187:
1.23 millert 188: /*
189: * Do sanity checks, fill in stb1 and stb2 and call the appropriate
190: * driver routine. Both drivers use the contents of stb1 and stb2.
191: */
1.6 millert 192: if (argc != 2)
1.23 millert 193: usage();
194: if (strcmp(argv[0], "-") == 0) {
1.26 millert 195: fstat(STDIN_FILENO, &stb1);
1.23 millert 196: gotstdin = 1;
197: } else if (stat(argv[0], &stb1) != 0)
1.28 millert 198: err(2, "%s", argv[0]);
1.23 millert 199: if (strcmp(argv[1], "-") == 0) {
1.26 millert 200: fstat(STDIN_FILENO, &stb2);
1.23 millert 201: gotstdin = 1;
202: } else if (stat(argv[1], &stb2) != 0)
1.28 millert 203: err(2, "%s", argv[1]);
1.23 millert 204: if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
1.28 millert 205: errx(2, "can't compare - to a directory");
1.27 millert 206: set_argstr(oargv, argv);
1.23 millert 207: if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
208: if (format == D_IFDEF)
1.28 millert 209: errx(2, "-D option not supported with directories");
1.23 millert 210: diffdir(argv[0], argv[1]);
1.27 millert 211: } else {
1.28 millert 212: if (S_ISDIR(stb1.st_mode)) {
213: argv[0] = splice(argv[0], argv[1]);
214: if (stat(argv[0], &stb1) < 0)
215: err(2, "%s", argv[0]);
216: }
217: if (S_ISDIR(stb2.st_mode)) {
218: argv[1] = splice(argv[1], argv[0]);
219: if (stat(argv[1], &stb2) < 0)
220: err(2, "%s", argv[1]);
221: }
1.27 millert 222: print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1],
223: NULL);
224: }
1.23 millert 225: exit(status);
1.1 deraadt 226: }
227:
1.3 tedu 228: void *
1.8 millert 229: emalloc(size_t n)
1.3 tedu 230: {
231: void *p;
232:
233: if ((p = malloc(n)) == NULL)
1.28 millert 234: err(2, NULL);
1.3 tedu 235: return (p);
1.1 deraadt 236: }
237:
1.3 tedu 238: void *
1.8 millert 239: erealloc(void *p, size_t n)
1.1 deraadt 240: {
1.3 tedu 241: void *q;
1.1 deraadt 242:
1.3 tedu 243: if ((q = realloc(p, n)) == NULL)
1.28 millert 244: err(2, NULL);
1.3 tedu 245: return (q);
1.1 deraadt 246: }
247:
1.27 millert 248: int
249: easprintf(char **ret, const char *fmt, ...)
250: {
251: int len;
252: va_list ap;
253:
254: va_start(ap, fmt);
255: len = vasprintf(ret, fmt, ap);
256: va_end(ap);
257:
258: if (len == -1)
1.28 millert 259: err(2, NULL);
1.30 henning 260: return (len);
1.27 millert 261: }
262:
1.23 millert 263: void
264: set_argstr(char **av, char **ave)
265: {
266: size_t argsize;
267: char **ap;
268:
269: argsize = 4 + (char *)ave - (char *)av + 1;
270: diffargs = emalloc(argsize);
271: strlcpy(diffargs, "diff", argsize);
272: for (ap = av + 1; ap < ave; ap++) {
273: if (strcmp(*ap, "--") != 0) {
274: strlcat(diffargs, " ", argsize);
275: strlcat(diffargs, *ap, argsize);
276: }
277: }
278: }
279:
280: /*
281: * Read in an excludes file and push each line.
282: */
283: void
284: read_excludes_file(char *file)
285: {
286: FILE *fp;
287: char *buf, *pattern;
288: size_t len;
289:
290: if (strcmp(file, "-") == 0)
291: fp = stdin;
292: else if ((fp = fopen(file, "r")) == NULL)
1.28 millert 293: err(2, "%s", file);
1.23 millert 294: while ((buf = fgetln(fp, &len)) != NULL) {
295: if (buf[len - 1] == '\n')
296: len--;
297: pattern = emalloc(len + 1);
298: memcpy(pattern, buf, len);
299: pattern[len] = '\0';
300: push_excludes(pattern);
301: }
302: if (strcmp(file, "-") != 0)
303: fclose(fp);
304: }
305:
306: /*
307: * Push a pattern onto the excludes list.
308: */
309: void
310: push_excludes(char *pattern)
311: {
312: struct excludes *entry;
313:
314: entry = emalloc(sizeof(*entry));
315: entry->pattern = pattern;
316: entry->next = excludes_list;
317: excludes_list = entry;
1.27 millert 318: }
319:
320: void
321: print_status(int val, char *path1, char *path2, char *entry)
322: {
323: switch (val) {
324: case D_ONLY:
1.32 millert 325: /* must strip off the trailing '/' */
326: printf("Only in %.*s: %s\n", (int)(strlen(path1) - 1),
327: path1, entry);
1.27 millert 328: break;
329: case D_COMMON:
330: printf("Common subdirectories: %s%s and %s%s\n",
331: path1, entry ? entry : "", path2, entry ? entry : "");
332: break;
333: case D_BINARY:
334: printf("Binary files %s%s and %s%s differ\n",
335: path1, entry ? entry : "", path2, entry ? entry : "");
336: break;
337: case D_DIFFER:
338: if (format == D_BRIEF)
339: printf("Files %s%s and %s%s differ\n",
340: path1, entry ? entry : "",
341: path2, entry ? entry : "");
342: break;
343: case D_SAME:
344: if (sflag)
345: printf("Files %s%s and %s%s are identical\n",
346: path1, entry ? entry : "",
347: path2, entry ? entry : "");
1.28 millert 348: break;
1.29 millert 349: case D_MISMATCH1:
1.31 millert 350: printf("File %s%s is a directory while file %s%s is a regular file\n",
1.29 millert 351: path1, entry ? entry : "", path2, entry ? entry : "");
352: break;
353: case D_MISMATCH2:
1.31 millert 354: printf("File %s%s is a regular file while file %s%s is a directory\n",
1.28 millert 355: path1, entry ? entry : "", path2, entry ? entry : "");
1.27 millert 356: break;
357: }
1.23 millert 358: }
359:
1.6 millert 360: __dead void
361: usage(void)
362: {
1.14 deraadt 363: (void)fprintf(stderr,
1.33 millert 364: "usage: diff [-bilqtTw] [-c | -e | -f | -n | -u] [-L label] file1 file2\n"
365: " diff [-bilqtTw] [-L label] -C number file1 file2\n"
366: " diff [-bilqtw] -D string file1 file2\n"
367: " diff [-bilqtTw] [-L label] -U number file1 file2\n"
368: " diff [-bilNPqwtT] [-c | -e | -f | -n | -u ] [-L label] [-r] [-s] [-S name]\n"
369: " [-X file] [-x pattern] dir1 dir2\n");
1.6 millert 370:
1.15 millert 371: exit(2);
1.1 deraadt 372: }