Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.82
1.82 ! deraadt 1: /* $OpenBSD: rcsdiff.c,v 1.81 2014/12/01 21:58:46 deraadt Exp $ */
1.1 joris 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:
1.72 xsa 27: #include <sys/stat.h>
1.78 chl 28: #include <sys/time.h>
1.72 xsa 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>
1.1 joris 36:
1.30 xsa 37: #include "rcsprog.h"
1.2 niallo 38: #include "diff.h"
1.1 joris 39:
1.69 millert 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 *);
1.1 joris 43:
1.69 millert 44: static int quiet;
1.18 xsa 45: static int kflag = RCS_KWEXP_ERR;
1.69 millert 46: static char *diff_ignore_pats;
1.18 xsa 47:
1.1 joris 48: int
49: rcsdiff_main(int argc, char **argv)
50: {
1.69 millert 51: int fd, i, ch, dflags, status;
1.46 ray 52: RCSNUM *rev1, *rev2;
1.1 joris 53: RCSFILE *file;
1.82 ! deraadt 54: char fpath[PATH_MAX], *rev_str1, *rev_str2;
1.69 millert 55: const char *errstr;
1.1 joris 56:
1.46 ray 57: rev1 = rev2 = NULL;
58: rev_str1 = rev_str2 = NULL;
1.61 xsa 59: status = D_SAME;
1.69 millert 60: dflags = 0;
1.1 joris 61:
1.59 ray 62: if (strlcpy(diffargs, "diff", sizeof(diffargs)) >= sizeof(diffargs))
1.63 xsa 63: errx(D_ERROR, "diffargs too long");
1.16 xsa 64:
1.69 millert 65: while ((ch = rcs_getopt(argc, argv, "abC:cdI:ik:npqr:TtU:uVwx::z::")) != -1) {
1.1 joris 66: switch (ch) {
1.69 millert 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;
1.3 joris 90: case 'c':
1.59 ray 91: if (strlcat(diffargs, " -c", sizeof(diffargs)) >=
92: sizeof(diffargs))
1.63 xsa 93: errx(D_ERROR, "diffargs too long");
1.3 joris 94: diff_format = D_CONTEXT;
95: break;
1.69 millert 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;
1.18 xsa 115: case 'k':
116: kflag = rcs_kflag_get(rcs_optarg);
117: if (RCS_KWEXP_INVAL(kflag)) {
1.54 xsa 118: warnx("invalid RCS keyword substitution mode");
1.18 xsa 119: (usage)();
120: }
121: break;
1.3 joris 122: case 'n':
1.59 ray 123: if (strlcat(diffargs, " -n", sizeof(diffargs)) >=
124: sizeof(diffargs))
1.63 xsa 125: errx(D_ERROR, "diffargs too long");
1.3 joris 126: diff_format = D_RCSDIFF;
127: break;
1.69 millert 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;
1.1 joris 134: case 'q':
1.69 millert 135: quiet = 1;
1.3 joris 136: break;
1.4 niallo 137: case 'r':
1.46 ray 138: rcs_setrevstr2(&rev_str1, &rev_str2, rcs_optarg);
1.19 xsa 139: break;
140: case 'T':
1.21 xsa 141: /*
142: * kept for compatibility
143: */
1.17 xsa 144: break;
1.69 millert 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;
1.59 ray 162: case 'u':
163: if (strlcat(diffargs, " -u", sizeof(diffargs)) >=
164: sizeof(diffargs))
1.63 xsa 165: errx(D_ERROR, "diffargs too long");
1.59 ray 166: diff_format = D_UNIFIED;
167: break;
1.17 xsa 168: case 'V':
169: printf("%s\n", rcs_version);
170: exit(0);
1.69 millert 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;
1.17 xsa 177: case 'x':
1.41 ray 178: /* Use blank extension if none given. */
179: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.36 joris 180: break;
181: case 'z':
182: timezone_flag = rcs_optarg;
1.4 niallo 183: break;
1.1 joris 184: default:
1.11 joris 185: (usage)();
1.1 joris 186: }
187: }
188:
1.11 joris 189: argc -= rcs_optind;
190: argv += rcs_optind;
1.1 joris 191:
192: if (argc == 0) {
1.52 xsa 193: warnx("no input file");
1.1 joris 194: (usage)();
195: }
196:
1.69 millert 197: 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:
1.1 joris 212: for (i = 0; i < argc; i++) {
1.65 ray 213: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
214: if (fd < 0) {
1.71 niallo 215: warn("%s", fpath);
1.1 joris 216: continue;
1.65 ray 217: }
1.1 joris 218:
1.58 joris 219: if ((file = rcs_open(fpath, fd,
220: RCS_READ|RCS_PARSE_FULLY)) == NULL)
1.1 joris 221: continue;
1.20 xsa 222:
1.44 xsa 223: rcs_kwexp_set(file, kflag);
1.1 joris 224:
1.46 ray 225: if (rev_str1 != NULL) {
1.47 ray 226: if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL)
1.63 xsa 227: errx(D_ERROR, "bad revision number");
1.46 ray 228: }
229: if (rev_str2 != NULL) {
1.47 ray 230: if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL)
1.63 xsa 231: errx(D_ERROR, "bad revision number");
1.46 ray 232: }
1.14 xsa 233:
1.69 millert 234: if (!quiet) {
1.18 xsa 235: fprintf(stderr, "%s\n", RCS_DIFF_DIV);
236: fprintf(stderr, "RCS file: %s\n", fpath);
1.14 xsa 237: }
1.8 joris 238:
239: diff_file = argv[i];
1.1 joris 240:
1.46 ray 241: /* No revisions given. */
1.61 xsa 242: if (rev_str1 == NULL)
1.69 millert 243: status = rcsdiff_file(file, file->rf_head, argv[i],
244: dflags);
1.46 ray 245: /* One revision given. */
1.61 xsa 246: else if (rev_str2 == NULL)
1.69 millert 247: status = rcsdiff_file(file, rev1, argv[i], dflags);
1.46 ray 248: /* Two revisions given. */
1.61 xsa 249: else
1.69 millert 250: status = rcsdiff_rev(file, rev1, rev2, dflags);
1.1 joris 251:
252: rcs_close(file);
1.46 ray 253:
254: if (rev1 != NULL) {
255: rcsnum_free(rev1);
256: rev1 = NULL;
257: }
258: if (rev2 != NULL) {
259: rcsnum_free(rev2);
260: rev2 = NULL;
261: }
1.1 joris 262: }
263:
1.26 niallo 264: return (status);
1.1 joris 265: }
266:
1.80 otto 267: __dead void
1.1 joris 268: rcsdiff_usage(void)
269: {
1.9 deraadt 270: fprintf(stderr,
1.73 sobrado 271: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] [-xsuffixes] [-ztz]\n"
272: " [diff_options] file ...\n");
1.80 otto 273:
274: exit(D_ERROR);
1.1 joris 275: }
276:
277: static int
1.69 millert 278: rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags)
1.1 joris 279: {
1.58 joris 280: int ret, fd;
1.49 joris 281: time_t t;
1.58 joris 282: struct stat st;
1.60 xsa 283: char *path1, *path2;
1.1 joris 284: BUF *b1, *b2;
1.74 xsa 285: char rbuf[RCS_REV_BUFSZ];
1.49 joris 286: struct tm *tb;
1.24 niallo 287: struct timeval tv[2], tv2[2];
1.49 joris 288:
1.24 niallo 289: memset(&tv, 0, sizeof(tv));
290: memset(&tv2, 0, sizeof(tv2));
1.15 xsa 291:
1.61 xsa 292: ret = D_ERROR;
1.37 joris 293: b1 = b2 = NULL;
294:
1.49 joris 295: diff_rev1 = rev;
296: diff_rev2 = NULL;
1.66 niallo 297: path1 = path2 = NULL;
1.49 joris 298:
1.58 joris 299: if ((fd = open(filename, O_RDONLY)) == -1) {
1.52 xsa 300: warn("%s", filename);
1.37 joris 301: goto out;
1.15 xsa 302: }
1.1 joris 303:
304: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
1.69 millert 305: if (!quiet) {
1.18 xsa 306: fprintf(stderr, "retrieving revision %s\n", rbuf);
307: fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename);
1.16 xsa 308: }
1.1 joris 309:
1.23 xsa 310: if ((b1 = rcs_getrev(file, rev)) == NULL) {
1.52 xsa 311: warnx("failed to retrieve revision %s", rbuf);
1.37 joris 312: goto out;
1.1 joris 313: }
1.37 joris 314:
1.31 niallo 315: b1 = rcs_kwexp_buf(b1, file, rev);
1.79 deraadt 316: tv[0].tv_sec = rcs_rev_getdate(file, rev);
1.24 niallo 317: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 318:
1.77 ray 319: if ((b2 = buf_load(filename)) == NULL) {
1.52 xsa 320: warnx("failed to load file: `%s'", filename);
1.37 joris 321: goto out;
1.4 niallo 322: }
1.37 joris 323:
1.49 joris 324: /* XXX - GNU uses GMT */
1.58 joris 325: if (fstat(fd, &st) == -1)
1.63 xsa 326: err(D_ERROR, "%s", filename);
1.58 joris 327:
1.49 joris 328: tb = gmtime(&st.st_mtime);
329: t = mktime(tb);
330:
331: tv2[0].tv_sec = t;
1.50 deraadt 332: tv2[1].tv_sec = t;
1.4 niallo 333:
1.60 xsa 334: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 335: buf_write_stmp(b1, path1);
1.37 joris 336:
1.76 ray 337: buf_free(b1);
1.37 joris 338: b1 = NULL;
339:
1.24 niallo 340: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 341: warn("utimes");
1.4 niallo 342:
1.60 xsa 343: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 344: buf_write_stmp(b2, path2);
1.37 joris 345:
1.76 ray 346: buf_free(b2);
1.37 joris 347: b2 = NULL;
348:
1.24 niallo 349: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 350: warn("utimes");
1.4 niallo 351:
1.75 ray 352: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 353:
354: out:
1.58 joris 355: if (fd != -1)
356: (void)close(fd);
1.37 joris 357: if (b1 != NULL)
1.76 ray 358: buf_free(b1);
1.37 joris 359: if (b2 != NULL)
1.76 ray 360: buf_free(b2);
1.60 xsa 361: if (path1 != NULL)
362: xfree(path1);
363: if (path2 != NULL)
364: xfree(path2);
1.37 joris 365:
366: return (ret);
1.4 niallo 367: }
368:
369: static int
1.69 millert 370: rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags)
1.4 niallo 371: {
1.68 ray 372: struct timeval tv[2], tv2[2];
373: BUF *b1, *b2;
1.37 joris 374: int ret;
1.74 xsa 375: char *path1, *path2, rbuf1[RCS_REV_BUFSZ], rbuf2[RCS_REV_BUFSZ];
1.24 niallo 376:
1.61 xsa 377: ret = D_ERROR;
1.37 joris 378: b1 = b2 = NULL;
1.24 niallo 379: memset(&tv, 0, sizeof(tv));
380: memset(&tv2, 0, sizeof(tv2));
1.4 niallo 381:
1.49 joris 382: diff_rev1 = rev1;
383: diff_rev2 = rev2;
1.66 niallo 384: path1 = path2 = NULL;
1.49 joris 385:
1.16 xsa 386: rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1));
1.69 millert 387: if (!quiet)
1.49 joris 388: fprintf(stderr, "retrieving revision %s\n", rbuf1);
1.4 niallo 389:
1.23 xsa 390: if ((b1 = rcs_getrev(file, rev1)) == NULL) {
1.52 xsa 391: warnx("failed to retrieve revision %s", rbuf1);
1.37 joris 392: goto out;
1.4 niallo 393: }
1.37 joris 394:
1.31 niallo 395: b1 = rcs_kwexp_buf(b1, file, rev1);
1.79 deraadt 396: tv[0].tv_sec = rcs_rev_getdate(file, rev1);
1.24 niallo 397: tv[1].tv_sec = tv[0].tv_sec;
1.4 niallo 398:
1.16 xsa 399: rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2));
1.69 millert 400: if (!quiet)
1.18 xsa 401: fprintf(stderr, "retrieving revision %s\n", rbuf2);
1.4 niallo 402:
1.23 xsa 403: if ((b2 = rcs_getrev(file, rev2)) == NULL) {
1.52 xsa 404: warnx("failed to retrieve revision %s", rbuf2);
1.37 joris 405: goto out;
1.1 joris 406: }
1.37 joris 407:
1.31 niallo 408: b2 = rcs_kwexp_buf(b2, file, rev2);
1.79 deraadt 409: tv2[0].tv_sec = rcs_rev_getdate(file, rev2);
1.24 niallo 410: tv2[1].tv_sec = tv2[0].tv_sec;
1.16 xsa 411:
1.69 millert 412: if (!quiet)
1.33 xsa 413: fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2);
1.1 joris 414:
1.60 xsa 415: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 416: buf_write_stmp(b1, path1);
1.37 joris 417:
1.76 ray 418: buf_free(b1);
1.37 joris 419: b1 = NULL;
420:
1.24 niallo 421: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 422: warn("utimes");
1.1 joris 423:
1.60 xsa 424: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 425: buf_write_stmp(b2, path2);
1.37 joris 426:
1.76 ray 427: buf_free(b2);
1.37 joris 428: b2 = NULL;
1.1 joris 429:
1.24 niallo 430: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 431: warn("utimes");
1.32 deraadt 432:
1.75 ray 433: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 434:
435: out:
436: if (b1 != NULL)
1.76 ray 437: buf_free(b1);
1.37 joris 438: if (b2 != NULL)
1.76 ray 439: buf_free(b2);
1.60 xsa 440: if (path1 != NULL)
441: xfree(path1);
442: if (path2 != NULL)
443: xfree(path2);
1.1 joris 444:
1.37 joris 445: return (ret);
1.69 millert 446: }
447:
448: static void
449: push_ignore_pats(char *pattern)
450: {
451: size_t len;
452:
453: if (diff_ignore_pats == NULL) {
454: len = strlen(pattern) + 1;
455: diff_ignore_pats = xmalloc(len);
456: strlcpy(diff_ignore_pats, pattern, len);
457: } else {
458: /* old + "|" + new + NUL */
459: len = strlen(diff_ignore_pats) + strlen(pattern) + 2;
1.81 deraadt 460: diff_ignore_pats = xreallocarray(diff_ignore_pats, len, 1);
1.69 millert 461: strlcat(diff_ignore_pats, "|", len);
462: strlcat(diff_ignore_pats, pattern, len);
463: }
1.1 joris 464: }