GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: diff.c,v 1.65 2015/12/29 19:04:46 gsoares Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> |
||
5 |
* |
||
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. |
||
9 |
* |
||
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. |
||
21 |
*/ |
||
22 |
|||
23 |
#include <sys/stat.h> |
||
24 |
|||
25 |
#include <ctype.h> |
||
26 |
#include <err.h> |
||
27 |
#include <errno.h> |
||
28 |
#include <getopt.h> |
||
29 |
#include <stdlib.h> |
||
30 |
#include <stdio.h> |
||
31 |
#include <stdarg.h> |
||
32 |
#include <string.h> |
||
33 |
#include <unistd.h> |
||
34 |
#include <limits.h> |
||
35 |
|||
36 |
#include "diff.h" |
||
37 |
#include "xmalloc.h" |
||
38 |
|||
39 |
int Nflag, Pflag, rflag, sflag, Tflag; |
||
40 |
int diff_format, diff_context, status; |
||
41 |
char *start, *ifdefname, *diffargs, *label[2], *ignore_pats; |
||
42 |
struct stat stb1, stb2; |
||
43 |
struct excludes *excludes_list; |
||
44 |
regex_t ignore_re; |
||
45 |
|||
46 |
#define OPTIONS "0123456789abC:cdD:efhI:iL:lnNPpqrS:sTtU:uwX:x:" |
||
47 |
static struct option longopts[] = { |
||
48 |
{ "text", no_argument, 0, 'a' }, |
||
49 |
{ "ignore-space-change", no_argument, 0, 'b' }, |
||
50 |
{ "context", optional_argument, 0, 'C' }, |
||
51 |
{ "ifdef", required_argument, 0, 'D' }, |
||
52 |
{ "minimal", no_argument, 0, 'd' }, |
||
53 |
{ "ed", no_argument, 0, 'e' }, |
||
54 |
{ "forward-ed", no_argument, 0, 'f' }, |
||
55 |
{ "ignore-matching-lines", required_argument, 0, 'I' }, |
||
56 |
{ "ignore-case", no_argument, 0, 'i' }, |
||
57 |
{ "label", required_argument, 0, 'L' }, |
||
58 |
{ "new-file", no_argument, 0, 'N' }, |
||
59 |
{ "rcs", no_argument, 0, 'n' }, |
||
60 |
{ "unidirectional-new-file", no_argument, 0, 'P' }, |
||
61 |
{ "show-c-function", no_argument, 0, 'p' }, |
||
62 |
{ "brief", no_argument, 0, 'q' }, |
||
63 |
{ "recursive", no_argument, 0, 'r' }, |
||
64 |
{ "report-identical-files", no_argument, 0, 's' }, |
||
65 |
{ "starting-file", required_argument, 0, 'S' }, |
||
66 |
{ "expand-tabs", no_argument, 0, 't' }, |
||
67 |
{ "initial-tab", no_argument, 0, 'T' }, |
||
68 |
{ "unified", optional_argument, 0, 'U' }, |
||
69 |
{ "ignore-all-space", no_argument, 0, 'w' }, |
||
70 |
{ "exclude", required_argument, 0, 'x' }, |
||
71 |
{ "exclude-from", required_argument, 0, 'X' }, |
||
72 |
{ NULL, 0, 0, '\0'} |
||
73 |
}; |
||
74 |
|||
75 |
__dead void usage(void); |
||
76 |
void push_excludes(char *); |
||
77 |
void push_ignore_pats(char *); |
||
78 |
void read_excludes_file(char *file); |
||
79 |
void set_argstr(char **, char **); |
||
80 |
|||
81 |
int |
||
82 |
main(int argc, char **argv) |
||
83 |
94 |
{ |
|
84 |
char *ep, **oargv; |
||
85 |
long l; |
||
86 |
int ch, dflags, lastch, gotstdin, prevoptind, newarg; |
||
87 |
|||
88 |
94 |
oargv = argv; |
|
89 |
94 |
gotstdin = 0; |
|
90 |
94 |
dflags = 0; |
|
91 |
94 |
lastch = '\0'; |
|
92 |
94 |
prevoptind = 1; |
|
93 |
94 |
newarg = 1; |
|
94 |
✓✓ | 234 |
while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { |
95 |
✗✗✗✓ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗ |
46 |
switch (ch) { |
96 |
case '0': case '1': case '2': case '3': case '4': |
||
97 |
case '5': case '6': case '7': case '8': case '9': |
||
98 |
if (newarg) |
||
99 |
usage(); /* disallow -[0-9]+ */ |
||
100 |
else if (lastch == 'c' || lastch == 'u') |
||
101 |
diff_context = 0; |
||
102 |
else if (!isdigit(lastch) || diff_context > INT_MAX / 10) |
||
103 |
usage(); |
||
104 |
diff_context = (diff_context * 10) + (ch - '0'); |
||
105 |
break; |
||
106 |
case 'a': |
||
107 |
dflags |= D_FORCEASCII; |
||
108 |
break; |
||
109 |
case 'b': |
||
110 |
dflags |= D_FOLDBLANKS; |
||
111 |
break; |
||
112 |
case 'C': |
||
113 |
case 'c': |
||
114 |
15 |
diff_format = D_CONTEXT; |
|
115 |
✗✓ | 15 |
if (optarg != NULL) { |
116 |
l = strtol(optarg, &ep, 10); |
||
117 |
if (*ep != '\0' || l < 0 || l >= INT_MAX) |
||
118 |
usage(); |
||
119 |
diff_context = (int)l; |
||
120 |
} else |
||
121 |
15 |
diff_context = 3; |
|
122 |
break; |
||
123 |
case 'd': |
||
124 |
dflags |= D_MINIMAL; |
||
125 |
break; |
||
126 |
case 'D': |
||
127 |
diff_format = D_IFDEF; |
||
128 |
ifdefname = optarg; |
||
129 |
break; |
||
130 |
case 'e': |
||
131 |
15 |
diff_format = D_EDIT; |
|
132 |
15 |
break; |
|
133 |
case 'f': |
||
134 |
diff_format = D_REVERSE; |
||
135 |
break; |
||
136 |
case 'h': |
||
137 |
/* silently ignore for backwards compatibility */ |
||
138 |
break; |
||
139 |
case 'I': |
||
140 |
push_ignore_pats(optarg); |
||
141 |
break; |
||
142 |
case 'i': |
||
143 |
dflags |= D_IGNORECASE; |
||
144 |
break; |
||
145 |
case 'L': |
||
146 |
if (label[0] == NULL) |
||
147 |
label[0] = optarg; |
||
148 |
else if (label[1] == NULL) |
||
149 |
label[1] = optarg; |
||
150 |
else |
||
151 |
usage(); |
||
152 |
break; |
||
153 |
case 'N': |
||
154 |
Nflag = 1; |
||
155 |
break; |
||
156 |
case 'n': |
||
157 |
diff_format = D_NREVERSE; |
||
158 |
break; |
||
159 |
case 'p': |
||
160 |
dflags |= D_PROTOTYPE; |
||
161 |
break; |
||
162 |
case 'P': |
||
163 |
Pflag = 1; |
||
164 |
break; |
||
165 |
case 'r': |
||
166 |
rflag = 1; |
||
167 |
break; |
||
168 |
case 'q': |
||
169 |
diff_format = D_BRIEF; |
||
170 |
break; |
||
171 |
case 'S': |
||
172 |
start = optarg; |
||
173 |
break; |
||
174 |
case 's': |
||
175 |
sflag = 1; |
||
176 |
break; |
||
177 |
case 'T': |
||
178 |
Tflag = 1; |
||
179 |
break; |
||
180 |
case 't': |
||
181 |
dflags |= D_EXPANDTABS; |
||
182 |
break; |
||
183 |
case 'U': |
||
184 |
case 'u': |
||
185 |
16 |
diff_format = D_UNIFIED; |
|
186 |
✗✓ | 16 |
if (optarg != NULL) { |
187 |
l = strtol(optarg, &ep, 10); |
||
188 |
if (*ep != '\0' || l < 0 || l >= INT_MAX) |
||
189 |
usage(); |
||
190 |
diff_context = (int)l; |
||
191 |
} else |
||
192 |
16 |
diff_context = 3; |
|
193 |
break; |
||
194 |
case 'w': |
||
195 |
dflags |= D_IGNOREBLANKS; |
||
196 |
break; |
||
197 |
case 'X': |
||
198 |
read_excludes_file(optarg); |
||
199 |
break; |
||
200 |
case 'x': |
||
201 |
push_excludes(optarg); |
||
202 |
break; |
||
203 |
default: |
||
204 |
usage(); |
||
205 |
break; |
||
206 |
} |
||
207 |
46 |
lastch = ch; |
|
208 |
46 |
newarg = optind != prevoptind; |
|
209 |
46 |
prevoptind = optind; |
|
210 |
} |
||
211 |
94 |
argc -= optind; |
|
212 |
94 |
argv += optind; |
|
213 |
|||
214 |
✗✓ | 94 |
if (pledge("stdio rpath tmppath wpath cpath", NULL) == -1) |
215 |
err(2, "pledge"); |
||
216 |
|||
217 |
/* |
||
218 |
* Do sanity checks, fill in stb1 and stb2 and call the appropriate |
||
219 |
* driver routine. Both drivers use the contents of stb1 and stb2. |
||
220 |
*/ |
||
221 |
✗✓ | 94 |
if (argc != 2) |
222 |
usage(); |
||
223 |
✗✓ | 94 |
if (ignore_pats != NULL) { |
224 |
char buf[BUFSIZ]; |
||
225 |
int error; |
||
226 |
|||
227 |
if ((error = regcomp(&ignore_re, ignore_pats, |
||
228 |
REG_NEWLINE | REG_EXTENDED)) != 0) { |
||
229 |
regerror(error, &ignore_re, buf, sizeof(buf)); |
||
230 |
if (*ignore_pats != '\0') |
||
231 |
errx(2, "%s: %s", ignore_pats, buf); |
||
232 |
else |
||
233 |
errx(2, "%s", buf); |
||
234 |
} |
||
235 |
} |
||
236 |
✓✓ | 94 |
if (strcmp(argv[0], "-") == 0) { |
237 |
6 |
fstat(STDIN_FILENO, &stb1); |
|
238 |
6 |
gotstdin = 1; |
|
239 |
✓✓ | 88 |
} else if (stat(argv[0], &stb1) != 0) |
240 |
2 |
err(2, "%s", argv[0]); |
|
241 |
✓✓ | 92 |
if (strcmp(argv[1], "-") == 0) { |
242 |
1 |
fstat(STDIN_FILENO, &stb2); |
|
243 |
1 |
gotstdin = 1; |
|
244 |
✗✓ | 91 |
} else if (stat(argv[1], &stb2) != 0) |
245 |
err(2, "%s", argv[1]); |
||
246 |
✓✓✓✗ ✗✓ |
92 |
if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) |
247 |
errx(2, "can't compare - to a directory"); |
||
248 |
92 |
set_argstr(oargv, argv); |
|
249 |
✗✓✗✗ |
92 |
if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { |
250 |
if (diff_format == D_IFDEF) |
||
251 |
errx(2, "-D option not supported with directories"); |
||
252 |
diffdir(argv[0], argv[1], dflags); |
||
253 |
} else { |
||
254 |
✗✓ | 92 |
if (S_ISDIR(stb1.st_mode)) { |
255 |
argv[0] = splice(argv[0], argv[1]); |
||
256 |
if (stat(argv[0], &stb1) < 0) |
||
257 |
err(2, "%s", argv[0]); |
||
258 |
} |
||
259 |
✗✓ | 92 |
if (S_ISDIR(stb2.st_mode)) { |
260 |
argv[1] = splice(argv[1], argv[0]); |
||
261 |
if (stat(argv[1], &stb2) < 0) |
||
262 |
err(2, "%s", argv[1]); |
||
263 |
} |
||
264 |
92 |
print_status(diffreg(argv[0], argv[1], dflags), argv[0], argv[1], |
|
265 |
""); |
||
266 |
} |
||
267 |
92 |
exit(status); |
|
268 |
} |
||
269 |
|||
270 |
void |
||
271 |
set_argstr(char **av, char **ave) |
||
272 |
92 |
{ |
|
273 |
size_t argsize; |
||
274 |
char **ap; |
||
275 |
|||
276 |
92 |
argsize = 4 + *ave - *av + 1; |
|
277 |
92 |
diffargs = xmalloc(argsize); |
|
278 |
92 |
strlcpy(diffargs, "diff", argsize); |
|
279 |
✓✓ | 138 |
for (ap = av + 1; ap < ave; ap++) { |
280 |
✓✗ | 46 |
if (strcmp(*ap, "--") != 0) { |
281 |
46 |
strlcat(diffargs, " ", argsize); |
|
282 |
46 |
strlcat(diffargs, *ap, argsize); |
|
283 |
} |
||
284 |
} |
||
285 |
92 |
} |
|
286 |
|||
287 |
/* |
||
288 |
* Read in an excludes file and push each line. |
||
289 |
*/ |
||
290 |
void |
||
291 |
read_excludes_file(char *file) |
||
292 |
{ |
||
293 |
FILE *fp; |
||
294 |
char *buf, *pattern; |
||
295 |
size_t len; |
||
296 |
|||
297 |
if (strcmp(file, "-") == 0) |
||
298 |
fp = stdin; |
||
299 |
else if ((fp = fopen(file, "r")) == NULL) |
||
300 |
err(2, "%s", file); |
||
301 |
while ((buf = fgetln(fp, &len)) != NULL) { |
||
302 |
if (buf[len - 1] == '\n') |
||
303 |
len--; |
||
304 |
pattern = xmalloc(len + 1); |
||
305 |
memcpy(pattern, buf, len); |
||
306 |
pattern[len] = '\0'; |
||
307 |
push_excludes(pattern); |
||
308 |
} |
||
309 |
if (strcmp(file, "-") != 0) |
||
310 |
fclose(fp); |
||
311 |
} |
||
312 |
|||
313 |
/* |
||
314 |
* Push a pattern onto the excludes list. |
||
315 |
*/ |
||
316 |
void |
||
317 |
push_excludes(char *pattern) |
||
318 |
{ |
||
319 |
struct excludes *entry; |
||
320 |
|||
321 |
entry = xmalloc(sizeof(*entry)); |
||
322 |
entry->pattern = pattern; |
||
323 |
entry->next = excludes_list; |
||
324 |
excludes_list = entry; |
||
325 |
} |
||
326 |
|||
327 |
void |
||
328 |
push_ignore_pats(char *pattern) |
||
329 |
{ |
||
330 |
size_t len; |
||
331 |
|||
332 |
if (ignore_pats == NULL) |
||
333 |
ignore_pats = xstrdup(pattern); |
||
334 |
else { |
||
335 |
/* old + "|" + new + NUL */ |
||
336 |
len = strlen(ignore_pats) + strlen(pattern) + 2; |
||
337 |
ignore_pats = xreallocarray(ignore_pats, 1, len); |
||
338 |
strlcat(ignore_pats, "|", len); |
||
339 |
strlcat(ignore_pats, pattern, len); |
||
340 |
} |
||
341 |
} |
||
342 |
|||
343 |
void |
||
344 |
print_only(const char *path, size_t dirlen, const char *entry) |
||
345 |
{ |
||
346 |
if (dirlen > 1) |
||
347 |
dirlen--; |
||
348 |
printf("Only in %.*s: %s\n", (int)dirlen, path, entry); |
||
349 |
} |
||
350 |
|||
351 |
void |
||
352 |
print_status(int val, char *path1, char *path2, char *entry) |
||
353 |
92 |
{ |
|
354 |
✗✓✓✗ ✗✗✗✗ |
92 |
switch (val) { |
355 |
case D_BINARY: |
||
356 |
printf("Binary files %s%s and %s%s differ\n", |
||
357 |
path1, entry, path2, entry); |
||
358 |
break; |
||
359 |
case D_DIFFER: |
||
360 |
✗✓ | 54 |
if (diff_format == D_BRIEF) |
361 |
printf("Files %s%s and %s%s differ\n", |
||
362 |
path1, entry, path2, entry); |
||
363 |
break; |
||
364 |
case D_SAME: |
||
365 |
✗✓ | 38 |
if (sflag) |
366 |
printf("Files %s%s and %s%s are identical\n", |
||
367 |
path1, entry, path2, entry); |
||
368 |
break; |
||
369 |
case D_MISMATCH1: |
||
370 |
printf("File %s%s is a directory while file %s%s is a regular file\n", |
||
371 |
path1, entry, path2, entry); |
||
372 |
break; |
||
373 |
case D_MISMATCH2: |
||
374 |
printf("File %s%s is a regular file while file %s%s is a directory\n", |
||
375 |
path1, entry, path2, entry); |
||
376 |
break; |
||
377 |
case D_SKIPPED1: |
||
378 |
printf("File %s%s is not a regular file or directory and was skipped\n", |
||
379 |
path1, entry); |
||
380 |
break; |
||
381 |
case D_SKIPPED2: |
||
382 |
printf("File %s%s is not a regular file or directory and was skipped\n", |
||
383 |
path2, entry); |
||
384 |
break; |
||
385 |
} |
||
386 |
92 |
} |
|
387 |
|||
388 |
__dead void |
||
389 |
usage(void) |
||
390 |
{ |
||
391 |
(void)fprintf(stderr, |
||
392 |
"usage: diff [-abdipTtw] [-c | -e | -f | -n | -q | -u] [-I pattern] [-L label]\n" |
||
393 |
" file1 file2\n" |
||
394 |
" diff [-abdipTtw] [-I pattern] [-L label] -C number file1 file2\n" |
||
395 |
" diff [-abditw] [-I pattern] -D string file1 file2\n" |
||
396 |
" diff [-abdipTtw] [-I pattern] [-L label] -U number file1 file2\n" |
||
397 |
" diff [-abdiNPprsTtw] [-c | -e | -f | -n | -q | -u] [-I pattern]\n" |
||
398 |
" [-L label] [-S name] [-X file] [-x pattern] dir1 dir2\n"); |
||
399 |
|||
400 |
exit(2); |
||
401 |
} |
Generated by: GCOVR (Version 3.3) |