GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: rcsdiff.c,v 1.84 2015/11/02 16:45:21 nicm Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2005 Joris Vink <joris@openbsd.org> |
||
4 |
* All rights reserved. |
||
5 |
* |
||
6 |
* Redistribution and use in source and binary forms, with or without |
||
7 |
* modification, are permitted provided that the following conditions |
||
8 |
* are met: |
||
9 |
* |
||
10 |
* 1. Redistributions of source code must retain the above copyright |
||
11 |
* notice, this list of conditions and the following disclaimer. |
||
12 |
* 2. The name of the author may not be used to endorse or promote products |
||
13 |
* derived from this software without specific prior written permission. |
||
14 |
* |
||
15 |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||
16 |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
||
17 |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
||
18 |
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
19 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||
20 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
||
21 |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
||
22 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
||
23 |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||
24 |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
25 |
*/ |
||
26 |
|||
27 |
#include <sys/stat.h> |
||
28 |
#include <sys/time.h> |
||
29 |
|||
30 |
#include <err.h> |
||
31 |
#include <fcntl.h> |
||
32 |
#include <stdio.h> |
||
33 |
#include <stdlib.h> |
||
34 |
#include <string.h> |
||
35 |
#include <unistd.h> |
||
36 |
|||
37 |
#include "rcsprog.h" |
||
38 |
#include "diff.h" |
||
39 |
|||
40 |
static int rcsdiff_file(RCSFILE *, RCSNUM *, const char *, int); |
||
41 |
static int rcsdiff_rev(RCSFILE *, RCSNUM *, RCSNUM *, int); |
||
42 |
static void push_ignore_pats(char *); |
||
43 |
|||
44 |
static int quiet; |
||
45 |
static int kflag = RCS_KWEXP_ERR; |
||
46 |
static char *diff_ignore_pats; |
||
47 |
|||
48 |
int |
||
49 |
rcsdiff_main(int argc, char **argv) |
||
50 |
{ |
||
51 |
int fd, i, ch, dflags, status; |
||
52 |
RCSNUM *rev1, *rev2; |
||
53 |
RCSFILE *file; |
||
54 |
8 |
char fpath[PATH_MAX], *rev_str1, *rev_str2; |
|
55 |
4 |
const char *errstr; |
|
56 |
|||
57 |
rev1 = rev2 = NULL; |
||
58 |
4 |
rev_str1 = rev_str2 = NULL; |
|
59 |
status = D_SAME; |
||
60 |
dflags = 0; |
||
61 |
|||
62 |
✗✓ | 4 |
if (strlcpy(diffargs, "diff", sizeof(diffargs)) >= sizeof(diffargs)) |
63 |
errx(D_ERROR, "diffargs too long"); |
||
64 |
|||
65 |
✓✓ | 17 |
while ((ch = rcs_getopt(argc, argv, "abC:cdI:ik:npqr:TtU:uVwx::z::")) != -1) { |
66 |
✓✗✗✗ ✗✗✗✗ ✗✗✗✓ ✓✗✗✓ ✗✗✗✗ ✗ |
26 |
switch (ch) { |
67 |
case 'a': |
||
68 |
if (strlcat(diffargs, " -a", sizeof(diffargs)) >= |
||
69 |
sizeof(diffargs)) |
||
70 |
errx(D_ERROR, "diffargs too long"); |
||
71 |
dflags |= D_FORCEASCII; |
||
72 |
break; |
||
73 |
case 'b': |
||
74 |
if (strlcat(diffargs, " -b", sizeof(diffargs)) >= |
||
75 |
sizeof(diffargs)) |
||
76 |
errx(D_ERROR, "diffargs too long"); |
||
77 |
dflags |= D_FOLDBLANKS; |
||
78 |
break; |
||
79 |
case 'C': |
||
80 |
(void)strlcat(diffargs, " -C", sizeof(diffargs)); |
||
81 |
if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >= |
||
82 |
sizeof(diffargs)) |
||
83 |
errx(D_ERROR, "diffargs too long"); |
||
84 |
diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr); |
||
85 |
if (errstr) |
||
86 |
errx(D_ERROR, "context is %s: %s", |
||
87 |
errstr, rcs_optarg); |
||
88 |
diff_format = D_CONTEXT; |
||
89 |
break; |
||
90 |
case 'c': |
||
91 |
if (strlcat(diffargs, " -c", sizeof(diffargs)) >= |
||
92 |
sizeof(diffargs)) |
||
93 |
errx(D_ERROR, "diffargs too long"); |
||
94 |
diff_format = D_CONTEXT; |
||
95 |
break; |
||
96 |
case 'd': |
||
97 |
if (strlcat(diffargs, " -d", sizeof(diffargs)) >= |
||
98 |
sizeof(diffargs)) |
||
99 |
errx(D_ERROR, "diffargs too long"); |
||
100 |
dflags |= D_MINIMAL; |
||
101 |
break; |
||
102 |
case 'i': |
||
103 |
if (strlcat(diffargs, " -i", sizeof(diffargs)) >= |
||
104 |
sizeof(diffargs)) |
||
105 |
errx(D_ERROR, "diffargs too long"); |
||
106 |
dflags |= D_IGNORECASE; |
||
107 |
break; |
||
108 |
case 'I': |
||
109 |
(void)strlcat(diffargs, " -I", sizeof(diffargs)); |
||
110 |
if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >= |
||
111 |
sizeof(diffargs)) |
||
112 |
errx(D_ERROR, "diffargs too long"); |
||
113 |
push_ignore_pats(rcs_optarg); |
||
114 |
break; |
||
115 |
case 'k': |
||
116 |
kflag = rcs_kflag_get(rcs_optarg); |
||
117 |
if (RCS_KWEXP_INVAL(kflag)) { |
||
118 |
warnx("invalid RCS keyword substitution mode"); |
||
119 |
(usage)(); |
||
120 |
} |
||
121 |
break; |
||
122 |
case 'n': |
||
123 |
if (strlcat(diffargs, " -n", sizeof(diffargs)) >= |
||
124 |
sizeof(diffargs)) |
||
125 |
errx(D_ERROR, "diffargs too long"); |
||
126 |
diff_format = D_RCSDIFF; |
||
127 |
break; |
||
128 |
case 'p': |
||
129 |
if (strlcat(diffargs, " -p", sizeof(diffargs)) >= |
||
130 |
sizeof(diffargs)) |
||
131 |
errx(D_ERROR, "diffargs too long"); |
||
132 |
dflags |= D_PROTOTYPE; |
||
133 |
break; |
||
134 |
case 'q': |
||
135 |
4 |
quiet = 1; |
|
136 |
4 |
break; |
|
137 |
case 'r': |
||
138 |
5 |
rcs_setrevstr2(&rev_str1, &rev_str2, rcs_optarg); |
|
139 |
5 |
break; |
|
140 |
case 'T': |
||
141 |
/* |
||
142 |
* kept for compatibility |
||
143 |
*/ |
||
144 |
break; |
||
145 |
case 't': |
||
146 |
if (strlcat(diffargs, " -t", sizeof(diffargs)) >= |
||
147 |
sizeof(diffargs)) |
||
148 |
errx(D_ERROR, "diffargs too long"); |
||
149 |
dflags |= D_EXPANDTABS; |
||
150 |
break; |
||
151 |
case 'U': |
||
152 |
(void)strlcat(diffargs, " -U", sizeof(diffargs)); |
||
153 |
if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >= |
||
154 |
sizeof(diffargs)) |
||
155 |
errx(D_ERROR, "diffargs too long"); |
||
156 |
diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr); |
||
157 |
if (errstr) |
||
158 |
errx(D_ERROR, "context is %s: %s", |
||
159 |
errstr, rcs_optarg); |
||
160 |
diff_format = D_UNIFIED; |
||
161 |
break; |
||
162 |
case 'u': |
||
163 |
✗✓ | 4 |
if (strlcat(diffargs, " -u", sizeof(diffargs)) >= |
164 |
sizeof(diffargs)) |
||
165 |
errx(D_ERROR, "diffargs too long"); |
||
166 |
4 |
diff_format = D_UNIFIED; |
|
167 |
4 |
break; |
|
168 |
case 'V': |
||
169 |
printf("%s\n", rcs_version); |
||
170 |
exit(0); |
||
171 |
case 'w': |
||
172 |
if (strlcat(diffargs, " -w", sizeof(diffargs)) >= |
||
173 |
sizeof(diffargs)) |
||
174 |
errx(D_ERROR, "diffargs too long"); |
||
175 |
dflags |= D_IGNOREBLANKS; |
||
176 |
break; |
||
177 |
case 'x': |
||
178 |
/* Use blank extension if none given. */ |
||
179 |
rcs_suffixes = rcs_optarg ? rcs_optarg : ""; |
||
180 |
break; |
||
181 |
case 'z': |
||
182 |
timezone_flag = rcs_optarg; |
||
183 |
break; |
||
184 |
default: |
||
185 |
(usage)(); |
||
186 |
} |
||
187 |
} |
||
188 |
|||
189 |
4 |
argc -= rcs_optind; |
|
190 |
4 |
argv += rcs_optind; |
|
191 |
|||
192 |
✗✓ | 4 |
if (argc == 0) { |
193 |
warnx("no input file"); |
||
194 |
(usage)(); |
||
195 |
} |
||
196 |
|||
197 |
✗✓ | 4 |
if (diff_ignore_pats != NULL) { |
198 |
char buf[BUFSIZ]; |
||
199 |
int error; |
||
200 |
|||
201 |
diff_ignore_re = xmalloc(sizeof(*diff_ignore_re)); |
||
202 |
if ((error = regcomp(diff_ignore_re, diff_ignore_pats, |
||
203 |
REG_NEWLINE | REG_EXTENDED)) != 0) { |
||
204 |
regerror(error, diff_ignore_re, buf, sizeof(buf)); |
||
205 |
if (*diff_ignore_pats != '\0') |
||
206 |
errx(D_ERROR, "%s: %s", diff_ignore_pats, buf); |
||
207 |
else |
||
208 |
errx(D_ERROR, "%s", buf); |
||
209 |
} |
||
210 |
} |
||
211 |
|||
212 |
✓✓ | 16 |
for (i = 0; i < argc; i++) { |
213 |
4 |
fd = rcs_choosefile(argv[i], fpath, sizeof(fpath)); |
|
214 |
✗✓ | 4 |
if (fd < 0) { |
215 |
warn("%s", fpath); |
||
216 |
continue; |
||
217 |
} |
||
218 |
|||
219 |
✓✗ | 8 |
if ((file = rcs_open(fpath, fd, |
220 |
4 |
RCS_READ|RCS_PARSE_FULLY)) == NULL) |
|
221 |
continue; |
||
222 |
|||
223 |
4 |
rcs_kwexp_set(file, kflag); |
|
224 |
|||
225 |
✓✓ | 4 |
if (rev_str1 != NULL) { |
226 |
✗✓ | 3 |
if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL) |
227 |
errx(D_ERROR, "bad revision number"); |
||
228 |
} |
||
229 |
✓✓ | 4 |
if (rev_str2 != NULL) { |
230 |
✗✓ | 2 |
if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL) |
231 |
errx(D_ERROR, "bad revision number"); |
||
232 |
} |
||
233 |
|||
234 |
✗✓ | 4 |
if (!quiet) { |
235 |
fprintf(stderr, "%s\n", RCS_DIFF_DIV); |
||
236 |
fprintf(stderr, "RCS file: %s\n", fpath); |
||
237 |
} |
||
238 |
|||
239 |
4 |
diff_file = argv[i]; |
|
240 |
|||
241 |
/* No revisions given. */ |
||
242 |
✓✓ | 4 |
if (rev_str1 == NULL) |
243 |
1 |
status = rcsdiff_file(file, file->rf_head, argv[i], |
|
244 |
dflags); |
||
245 |
/* One revision given. */ |
||
246 |
✓✓ | 3 |
else if (rev_str2 == NULL) |
247 |
1 |
status = rcsdiff_file(file, rev1, argv[i], dflags); |
|
248 |
/* Two revisions given. */ |
||
249 |
else |
||
250 |
2 |
status = rcsdiff_rev(file, rev1, rev2, dflags); |
|
251 |
|||
252 |
4 |
rcs_close(file); |
|
253 |
4 |
rcsnum_free(rev1); |
|
254 |
4 |
rcsnum_free(rev2); |
|
255 |
rev1 = rev2 = NULL; |
||
256 |
4 |
} |
|
257 |
|||
258 |
4 |
return (status); |
|
259 |
4 |
} |
|
260 |
|||
261 |
__dead void |
||
262 |
rcsdiff_usage(void) |
||
263 |
{ |
||
264 |
fprintf(stderr, |
||
265 |
"usage: rcsdiff [-cnquV] [-kmode] [-rrev] [-xsuffixes] [-ztz]\n" |
||
266 |
" [diff_options] file ...\n"); |
||
267 |
|||
268 |
exit(D_ERROR); |
||
269 |
} |
||
270 |
|||
271 |
static int |
||
272 |
rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags) |
||
273 |
{ |
||
274 |
int ret, fd; |
||
275 |
time_t t; |
||
276 |
4 |
struct stat st; |
|
277 |
2 |
char *path1, *path2; |
|
278 |
BUF *b1, *b2; |
||
279 |
2 |
char rbuf[RCS_REV_BUFSZ]; |
|
280 |
struct tm *tb; |
||
281 |
2 |
struct timeval tv[2], tv2[2]; |
|
282 |
|||
283 |
2 |
memset(&tv, 0, sizeof(tv)); |
|
284 |
2 |
memset(&tv2, 0, sizeof(tv2)); |
|
285 |
|||
286 |
ret = D_ERROR; |
||
287 |
b1 = b2 = NULL; |
||
288 |
|||
289 |
2 |
diff_rev1 = rev; |
|
290 |
2 |
diff_rev2 = NULL; |
|
291 |
2 |
path1 = path2 = NULL; |
|
292 |
|||
293 |
✗✓ | 2 |
if ((fd = open(filename, O_RDONLY)) == -1) { |
294 |
warn("%s", filename); |
||
295 |
goto out; |
||
296 |
} |
||
297 |
|||
298 |
2 |
rcsnum_tostr(rev, rbuf, sizeof(rbuf)); |
|
299 |
✗✓ | 2 |
if (!quiet) { |
300 |
fprintf(stderr, "retrieving revision %s\n", rbuf); |
||
301 |
fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename); |
||
302 |
} |
||
303 |
|||
304 |
✗✓ | 2 |
if ((b1 = rcs_getrev(file, rev)) == NULL) { |
305 |
warnx("failed to retrieve revision %s", rbuf); |
||
306 |
goto out; |
||
307 |
} |
||
308 |
|||
309 |
2 |
b1 = rcs_kwexp_buf(b1, file, rev); |
|
310 |
2 |
tv[0].tv_sec = rcs_rev_getdate(file, rev); |
|
311 |
2 |
tv[1].tv_sec = tv[0].tv_sec; |
|
312 |
|||
313 |
✗✓ | 2 |
if ((b2 = buf_load(filename)) == NULL) { |
314 |
warnx("failed to load file: `%s'", filename); |
||
315 |
goto out; |
||
316 |
} |
||
317 |
|||
318 |
/* XXX - GNU uses GMT */ |
||
319 |
✗✓ | 2 |
if (fstat(fd, &st) == -1) |
320 |
err(D_ERROR, "%s", filename); |
||
321 |
|||
322 |
2 |
tb = gmtime(&st.st_mtime); |
|
323 |
2 |
t = mktime(tb); |
|
324 |
|||
325 |
2 |
tv2[0].tv_sec = t; |
|
326 |
2 |
tv2[1].tv_sec = t; |
|
327 |
|||
328 |
2 |
(void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); |
|
329 |
2 |
buf_write_stmp(b1, path1); |
|
330 |
|||
331 |
2 |
buf_free(b1); |
|
332 |
b1 = NULL; |
||
333 |
|||
334 |
✗✓ | 2 |
if (utimes(path1, (const struct timeval *)&tv) < 0) |
335 |
warn("utimes"); |
||
336 |
|||
337 |
2 |
(void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); |
|
338 |
2 |
buf_write_stmp(b2, path2); |
|
339 |
|||
340 |
2 |
buf_free(b2); |
|
341 |
b2 = NULL; |
||
342 |
|||
343 |
✗✓ | 2 |
if (utimes(path2, (const struct timeval *)&tv2) < 0) |
344 |
warn("utimes"); |
||
345 |
|||
346 |
2 |
ret = diffreg(path1, path2, NULL, dflags); |
|
347 |
|||
348 |
out: |
||
349 |
✓✗ | 2 |
if (fd != -1) |
350 |
2 |
(void)close(fd); |
|
351 |
2 |
buf_free(b1); |
|
352 |
2 |
buf_free(b2); |
|
353 |
2 |
free(path1); |
|
354 |
2 |
free(path2); |
|
355 |
|||
356 |
2 |
return (ret); |
|
357 |
2 |
} |
|
358 |
|||
359 |
static int |
||
360 |
rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags) |
||
361 |
{ |
||
362 |
4 |
struct timeval tv[2], tv2[2]; |
|
363 |
BUF *b1, *b2; |
||
364 |
int ret; |
||
365 |
2 |
char *path1, *path2, rbuf1[RCS_REV_BUFSZ], rbuf2[RCS_REV_BUFSZ]; |
|
366 |
|||
367 |
ret = D_ERROR; |
||
368 |
b1 = b2 = NULL; |
||
369 |
2 |
memset(&tv, 0, sizeof(tv)); |
|
370 |
2 |
memset(&tv2, 0, sizeof(tv2)); |
|
371 |
|||
372 |
2 |
diff_rev1 = rev1; |
|
373 |
2 |
diff_rev2 = rev2; |
|
374 |
2 |
path1 = path2 = NULL; |
|
375 |
|||
376 |
2 |
rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1)); |
|
377 |
✗✓ | 2 |
if (!quiet) |
378 |
fprintf(stderr, "retrieving revision %s\n", rbuf1); |
||
379 |
|||
380 |
✗✓ | 2 |
if ((b1 = rcs_getrev(file, rev1)) == NULL) { |
381 |
warnx("failed to retrieve revision %s", rbuf1); |
||
382 |
goto out; |
||
383 |
} |
||
384 |
|||
385 |
2 |
b1 = rcs_kwexp_buf(b1, file, rev1); |
|
386 |
2 |
tv[0].tv_sec = rcs_rev_getdate(file, rev1); |
|
387 |
2 |
tv[1].tv_sec = tv[0].tv_sec; |
|
388 |
|||
389 |
2 |
rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2)); |
|
390 |
✗✓ | 2 |
if (!quiet) |
391 |
fprintf(stderr, "retrieving revision %s\n", rbuf2); |
||
392 |
|||
393 |
✗✓ | 2 |
if ((b2 = rcs_getrev(file, rev2)) == NULL) { |
394 |
warnx("failed to retrieve revision %s", rbuf2); |
||
395 |
goto out; |
||
396 |
} |
||
397 |
|||
398 |
2 |
b2 = rcs_kwexp_buf(b2, file, rev2); |
|
399 |
2 |
tv2[0].tv_sec = rcs_rev_getdate(file, rev2); |
|
400 |
2 |
tv2[1].tv_sec = tv2[0].tv_sec; |
|
401 |
|||
402 |
✗✓ | 2 |
if (!quiet) |
403 |
fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2); |
||
404 |
|||
405 |
2 |
(void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); |
|
406 |
2 |
buf_write_stmp(b1, path1); |
|
407 |
|||
408 |
2 |
buf_free(b1); |
|
409 |
b1 = NULL; |
||
410 |
|||
411 |
✗✓ | 2 |
if (utimes(path1, (const struct timeval *)&tv) < 0) |
412 |
warn("utimes"); |
||
413 |
|||
414 |
2 |
(void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); |
|
415 |
2 |
buf_write_stmp(b2, path2); |
|
416 |
|||
417 |
2 |
buf_free(b2); |
|
418 |
b2 = NULL; |
||
419 |
|||
420 |
✗✓ | 2 |
if (utimes(path2, (const struct timeval *)&tv2) < 0) |
421 |
warn("utimes"); |
||
422 |
|||
423 |
2 |
ret = diffreg(path1, path2, NULL, dflags); |
|
424 |
|||
425 |
out: |
||
426 |
2 |
buf_free(b1); |
|
427 |
2 |
buf_free(b2); |
|
428 |
2 |
free(path1); |
|
429 |
2 |
free(path2); |
|
430 |
|||
431 |
2 |
return (ret); |
|
432 |
2 |
} |
|
433 |
|||
434 |
static void |
||
435 |
push_ignore_pats(char *pattern) |
||
436 |
{ |
||
437 |
size_t len; |
||
438 |
|||
439 |
if (diff_ignore_pats == NULL) { |
||
440 |
len = strlen(pattern) + 1; |
||
441 |
diff_ignore_pats = xmalloc(len); |
||
442 |
strlcpy(diff_ignore_pats, pattern, len); |
||
443 |
} else { |
||
444 |
/* old + "|" + new + NUL */ |
||
445 |
len = strlen(diff_ignore_pats) + strlen(pattern) + 2; |
||
446 |
diff_ignore_pats = xreallocarray(diff_ignore_pats, len, 1); |
||
447 |
strlcat(diff_ignore_pats, "|", len); |
||
448 |
strlcat(diff_ignore_pats, pattern, len); |
||
449 |
} |
||
450 |
} |
Generated by: GCOVR (Version 3.3) |