Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.71
1.71 ! niallo 1: /* $OpenBSD: rcsdiff.c,v 1.70 2006/09/22 13:42:43 jmc 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.29 xsa 27: #include "includes.h"
1.1 joris 28:
1.30 xsa 29: #include "rcsprog.h"
1.2 niallo 30: #include "diff.h"
1.1 joris 31:
1.69 millert 32: static int rcsdiff_file(RCSFILE *, RCSNUM *, const char *, int);
33: static int rcsdiff_rev(RCSFILE *, RCSNUM *, RCSNUM *, int);
34: static void push_ignore_pats(char *);
1.1 joris 35:
1.69 millert 36: static int quiet;
1.18 xsa 37: static int kflag = RCS_KWEXP_ERR;
1.69 millert 38: static char *diff_ignore_pats;
1.18 xsa 39:
1.1 joris 40: int
41: rcsdiff_main(int argc, char **argv)
42: {
1.69 millert 43: int fd, i, ch, dflags, status;
1.46 ray 44: RCSNUM *rev1, *rev2;
1.1 joris 45: RCSFILE *file;
1.46 ray 46: char fpath[MAXPATHLEN], *rev_str1, *rev_str2;
1.69 millert 47: const char *errstr;
1.1 joris 48:
1.46 ray 49: rev1 = rev2 = NULL;
50: rev_str1 = rev_str2 = NULL;
1.61 xsa 51: status = D_SAME;
1.69 millert 52: dflags = 0;
1.1 joris 53:
1.59 ray 54: if (strlcpy(diffargs, "diff", sizeof(diffargs)) >= sizeof(diffargs))
1.63 xsa 55: errx(D_ERROR, "diffargs too long");
1.16 xsa 56:
1.69 millert 57: while ((ch = rcs_getopt(argc, argv, "abC:cdI:ik:npqr:TtU:uVwx::z::")) != -1) {
1.1 joris 58: switch (ch) {
1.69 millert 59: case 'a':
60: if (strlcat(diffargs, " -a", sizeof(diffargs)) >=
61: sizeof(diffargs))
62: errx(D_ERROR, "diffargs too long");
63: dflags |= D_FORCEASCII;
64: break;
65: case 'b':
66: if (strlcat(diffargs, " -b", sizeof(diffargs)) >=
67: sizeof(diffargs))
68: errx(D_ERROR, "diffargs too long");
69: dflags |= D_FOLDBLANKS;
70: break;
71: case 'C':
72: (void)strlcat(diffargs, " -C", sizeof(diffargs));
73: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
74: sizeof(diffargs))
75: errx(D_ERROR, "diffargs too long");
76: diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr);
77: if (errstr)
78: errx(D_ERROR, "context is %s: %s",
79: errstr, rcs_optarg);
80: diff_format = D_CONTEXT;
81: break;
1.3 joris 82: case 'c':
1.59 ray 83: if (strlcat(diffargs, " -c", sizeof(diffargs)) >=
84: sizeof(diffargs))
1.63 xsa 85: errx(D_ERROR, "diffargs too long");
1.3 joris 86: diff_format = D_CONTEXT;
87: break;
1.69 millert 88: case 'd':
89: if (strlcat(diffargs, " -d", sizeof(diffargs)) >=
90: sizeof(diffargs))
91: errx(D_ERROR, "diffargs too long");
92: dflags |= D_MINIMAL;
93: break;
94: case 'i':
95: if (strlcat(diffargs, " -i", sizeof(diffargs)) >=
96: sizeof(diffargs))
97: errx(D_ERROR, "diffargs too long");
98: dflags |= D_IGNORECASE;
99: break;
100: case 'I':
101: (void)strlcat(diffargs, " -I", sizeof(diffargs));
102: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
103: sizeof(diffargs))
104: errx(D_ERROR, "diffargs too long");
105: push_ignore_pats(rcs_optarg);
106: break;
1.18 xsa 107: case 'k':
108: kflag = rcs_kflag_get(rcs_optarg);
109: if (RCS_KWEXP_INVAL(kflag)) {
1.54 xsa 110: warnx("invalid RCS keyword substitution mode");
1.18 xsa 111: (usage)();
1.62 xsa 112: exit(D_ERROR);
1.18 xsa 113: }
114: break;
1.3 joris 115: case 'n':
1.59 ray 116: if (strlcat(diffargs, " -n", sizeof(diffargs)) >=
117: sizeof(diffargs))
1.63 xsa 118: errx(D_ERROR, "diffargs too long");
1.3 joris 119: diff_format = D_RCSDIFF;
120: break;
1.69 millert 121: case 'p':
122: if (strlcat(diffargs, " -p", sizeof(diffargs)) >=
123: sizeof(diffargs))
124: errx(D_ERROR, "diffargs too long");
125: dflags |= D_PROTOTYPE;
126: break;
1.1 joris 127: case 'q':
1.69 millert 128: quiet = 1;
1.3 joris 129: break;
1.4 niallo 130: case 'r':
1.46 ray 131: rcs_setrevstr2(&rev_str1, &rev_str2, rcs_optarg);
1.19 xsa 132: break;
133: case 'T':
1.21 xsa 134: /*
135: * kept for compatibility
136: */
1.17 xsa 137: break;
1.69 millert 138: case 't':
139: if (strlcat(diffargs, " -t", sizeof(diffargs)) >=
140: sizeof(diffargs))
141: errx(D_ERROR, "diffargs too long");
142: dflags |= D_EXPANDTABS;
143: break;
144: case 'U':
145: (void)strlcat(diffargs, " -U", sizeof(diffargs));
146: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
147: sizeof(diffargs))
148: errx(D_ERROR, "diffargs too long");
149: diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr);
150: if (errstr)
151: errx(D_ERROR, "context is %s: %s",
152: errstr, rcs_optarg);
153: diff_format = D_UNIFIED;
154: break;
1.59 ray 155: case 'u':
156: if (strlcat(diffargs, " -u", sizeof(diffargs)) >=
157: sizeof(diffargs))
1.63 xsa 158: errx(D_ERROR, "diffargs too long");
1.59 ray 159: diff_format = D_UNIFIED;
160: break;
1.17 xsa 161: case 'V':
162: printf("%s\n", rcs_version);
163: exit(0);
1.69 millert 164: case 'w':
165: if (strlcat(diffargs, " -w", sizeof(diffargs)) >=
166: sizeof(diffargs))
167: errx(D_ERROR, "diffargs too long");
168: dflags |= D_IGNOREBLANKS;
169: break;
1.17 xsa 170: case 'x':
1.41 ray 171: /* Use blank extension if none given. */
172: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.36 joris 173: break;
174: case 'z':
175: timezone_flag = rcs_optarg;
1.4 niallo 176: break;
1.1 joris 177: default:
1.11 joris 178: (usage)();
1.68 ray 179: exit(D_ERROR);
1.1 joris 180: }
181: }
182:
1.11 joris 183: argc -= rcs_optind;
184: argv += rcs_optind;
1.1 joris 185:
186: if (argc == 0) {
1.52 xsa 187: warnx("no input file");
1.1 joris 188: (usage)();
1.62 xsa 189: exit(D_ERROR);
1.1 joris 190: }
191:
1.69 millert 192: if (diff_ignore_pats != NULL) {
193: char buf[BUFSIZ];
194: int error;
195:
196: diff_ignore_re = xmalloc(sizeof(*diff_ignore_re));
197: if ((error = regcomp(diff_ignore_re, diff_ignore_pats,
198: REG_NEWLINE | REG_EXTENDED)) != 0) {
199: regerror(error, diff_ignore_re, buf, sizeof(buf));
200: if (*diff_ignore_pats != '\0')
201: errx(D_ERROR, "%s: %s", diff_ignore_pats, buf);
202: else
203: errx(D_ERROR, "%s", buf);
204: }
205: }
206:
1.1 joris 207: for (i = 0; i < argc; i++) {
1.65 ray 208: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
209: if (fd < 0) {
1.71 ! niallo 210: warn("%s", fpath);
1.1 joris 211: continue;
1.65 ray 212: }
1.1 joris 213:
1.58 joris 214: if ((file = rcs_open(fpath, fd,
215: RCS_READ|RCS_PARSE_FULLY)) == NULL)
1.1 joris 216: continue;
1.20 xsa 217:
1.44 xsa 218: rcs_kwexp_set(file, kflag);
1.1 joris 219:
1.46 ray 220: if (rev_str1 != NULL) {
1.47 ray 221: if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL)
1.63 xsa 222: errx(D_ERROR, "bad revision number");
1.46 ray 223: }
224: if (rev_str2 != NULL) {
1.47 ray 225: if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL)
1.63 xsa 226: errx(D_ERROR, "bad revision number");
1.46 ray 227: }
1.14 xsa 228:
1.69 millert 229: if (!quiet) {
1.18 xsa 230: fprintf(stderr, "%s\n", RCS_DIFF_DIV);
231: fprintf(stderr, "RCS file: %s\n", fpath);
1.14 xsa 232: }
1.8 joris 233:
234: diff_file = argv[i];
1.1 joris 235:
1.46 ray 236: /* No revisions given. */
1.61 xsa 237: if (rev_str1 == NULL)
1.69 millert 238: status = rcsdiff_file(file, file->rf_head, argv[i],
239: dflags);
1.46 ray 240: /* One revision given. */
1.61 xsa 241: else if (rev_str2 == NULL)
1.69 millert 242: status = rcsdiff_file(file, rev1, argv[i], dflags);
1.46 ray 243: /* Two revisions given. */
1.61 xsa 244: else
1.69 millert 245: status = rcsdiff_rev(file, rev1, rev2, dflags);
1.1 joris 246:
247: rcs_close(file);
1.46 ray 248:
249: if (rev1 != NULL) {
250: rcsnum_free(rev1);
251: rev1 = NULL;
252: }
253: if (rev2 != NULL) {
254: rcsnum_free(rev2);
255: rev2 = NULL;
256: }
1.1 joris 257: }
258:
1.26 niallo 259: return (status);
1.1 joris 260: }
261:
262: void
263: rcsdiff_usage(void)
264: {
1.9 deraadt 265: fprintf(stderr,
1.55 jmc 266: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] "
1.70 jmc 267: "[-xsuffixes] [-ztz] [diff_options] file ...\n");
1.1 joris 268: }
269:
270: static int
1.69 millert 271: rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags)
1.1 joris 272: {
1.58 joris 273: int ret, fd;
1.49 joris 274: time_t t;
1.58 joris 275: struct stat st;
1.60 xsa 276: char *path1, *path2;
1.1 joris 277: BUF *b1, *b2;
278: char rbuf[64];
1.49 joris 279: struct tm *tb;
1.24 niallo 280: struct timeval tv[2], tv2[2];
1.49 joris 281:
1.24 niallo 282: memset(&tv, 0, sizeof(tv));
283: memset(&tv2, 0, sizeof(tv2));
1.15 xsa 284:
1.61 xsa 285: ret = D_ERROR;
1.37 joris 286: b1 = b2 = NULL;
287:
1.49 joris 288: diff_rev1 = rev;
289: diff_rev2 = NULL;
1.66 niallo 290: path1 = path2 = NULL;
1.49 joris 291:
1.58 joris 292: if ((fd = open(filename, O_RDONLY)) == -1) {
1.52 xsa 293: warn("%s", filename);
1.37 joris 294: goto out;
1.15 xsa 295: }
1.1 joris 296:
297: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
1.69 millert 298: if (!quiet) {
1.18 xsa 299: fprintf(stderr, "retrieving revision %s\n", rbuf);
300: fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename);
1.16 xsa 301: }
1.1 joris 302:
1.23 xsa 303: if ((b1 = rcs_getrev(file, rev)) == NULL) {
1.52 xsa 304: warnx("failed to retrieve revision %s", rbuf);
1.37 joris 305: goto out;
1.1 joris 306: }
1.37 joris 307:
1.31 niallo 308: b1 = rcs_kwexp_buf(b1, file, rev);
1.24 niallo 309: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev);
310: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 311:
1.57 joris 312: if ((b2 = rcs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
1.52 xsa 313: warnx("failed to load file: `%s'", filename);
1.37 joris 314: goto out;
1.4 niallo 315: }
1.37 joris 316:
1.49 joris 317: /* XXX - GNU uses GMT */
1.58 joris 318: if (fstat(fd, &st) == -1)
1.63 xsa 319: err(D_ERROR, "%s", filename);
1.58 joris 320:
1.49 joris 321: tb = gmtime(&st.st_mtime);
322: t = mktime(tb);
323:
324: tv2[0].tv_sec = t;
1.50 deraadt 325: tv2[1].tv_sec = t;
1.4 niallo 326:
1.60 xsa 327: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 328: rcs_buf_write_stmp(b1, path1);
1.37 joris 329:
1.57 joris 330: rcs_buf_free(b1);
1.37 joris 331: b1 = NULL;
332:
1.24 niallo 333: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 334: warn("utimes");
1.4 niallo 335:
1.60 xsa 336: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 337: rcs_buf_write_stmp(b2, path2);
1.37 joris 338:
1.57 joris 339: rcs_buf_free(b2);
1.37 joris 340: b2 = NULL;
341:
1.24 niallo 342: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 343: warn("utimes");
1.4 niallo 344:
1.69 millert 345: ret = rcs_diffreg(path1, path2, NULL, dflags);
1.37 joris 346:
347: out:
1.58 joris 348: if (fd != -1)
349: (void)close(fd);
1.37 joris 350: if (b1 != NULL)
1.57 joris 351: rcs_buf_free(b1);
1.37 joris 352: if (b2 != NULL)
1.57 joris 353: rcs_buf_free(b2);
1.60 xsa 354: if (path1 != NULL)
355: xfree(path1);
356: if (path2 != NULL)
357: xfree(path2);
1.37 joris 358:
359: return (ret);
1.4 niallo 360: }
361:
362: static int
1.69 millert 363: rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags)
1.4 niallo 364: {
1.68 ray 365: struct timeval tv[2], tv2[2];
366: BUF *b1, *b2;
1.37 joris 367: int ret;
1.68 ray 368: char *path1, *path2, rbuf1[64], rbuf2[64];
1.24 niallo 369:
1.61 xsa 370: ret = D_ERROR;
1.37 joris 371: b1 = b2 = NULL;
1.24 niallo 372: memset(&tv, 0, sizeof(tv));
373: memset(&tv2, 0, sizeof(tv2));
1.4 niallo 374:
1.49 joris 375: diff_rev1 = rev1;
376: diff_rev2 = rev2;
1.66 niallo 377: path1 = path2 = NULL;
1.49 joris 378:
1.16 xsa 379: rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1));
1.69 millert 380: if (!quiet)
1.49 joris 381: fprintf(stderr, "retrieving revision %s\n", rbuf1);
1.4 niallo 382:
1.23 xsa 383: if ((b1 = rcs_getrev(file, rev1)) == NULL) {
1.52 xsa 384: warnx("failed to retrieve revision %s", rbuf1);
1.37 joris 385: goto out;
1.4 niallo 386: }
1.37 joris 387:
1.31 niallo 388: b1 = rcs_kwexp_buf(b1, file, rev1);
1.24 niallo 389: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev1);
390: tv[1].tv_sec = tv[0].tv_sec;
1.4 niallo 391:
1.16 xsa 392: rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2));
1.69 millert 393: if (!quiet)
1.18 xsa 394: fprintf(stderr, "retrieving revision %s\n", rbuf2);
1.4 niallo 395:
1.23 xsa 396: if ((b2 = rcs_getrev(file, rev2)) == NULL) {
1.52 xsa 397: warnx("failed to retrieve revision %s", rbuf2);
1.37 joris 398: goto out;
1.1 joris 399: }
1.37 joris 400:
1.31 niallo 401: b2 = rcs_kwexp_buf(b2, file, rev2);
1.24 niallo 402: tv2[0].tv_sec = (long)rcs_rev_getdate(file, rev2);
403: tv2[1].tv_sec = tv2[0].tv_sec;
1.16 xsa 404:
1.69 millert 405: if (!quiet)
1.33 xsa 406: fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2);
1.1 joris 407:
1.60 xsa 408: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 409: rcs_buf_write_stmp(b1, path1);
1.37 joris 410:
1.57 joris 411: rcs_buf_free(b1);
1.37 joris 412: b1 = NULL;
413:
1.24 niallo 414: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 415: warn("utimes");
1.1 joris 416:
1.60 xsa 417: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 418: rcs_buf_write_stmp(b2, path2);
1.37 joris 419:
1.57 joris 420: rcs_buf_free(b2);
1.37 joris 421: b2 = NULL;
1.1 joris 422:
1.24 niallo 423: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 424: warn("utimes");
1.32 deraadt 425:
1.69 millert 426: ret = rcs_diffreg(path1, path2, NULL, dflags);
1.37 joris 427:
428: out:
429: if (b1 != NULL)
1.57 joris 430: rcs_buf_free(b1);
1.37 joris 431: if (b2 != NULL)
1.57 joris 432: rcs_buf_free(b2);
1.60 xsa 433: if (path1 != NULL)
434: xfree(path1);
435: if (path2 != NULL)
436: xfree(path2);
1.1 joris 437:
1.37 joris 438: return (ret);
1.69 millert 439: }
440:
441: static void
442: push_ignore_pats(char *pattern)
443: {
444: size_t len;
445:
446: if (diff_ignore_pats == NULL) {
447: len = strlen(pattern) + 1;
448: diff_ignore_pats = xmalloc(len);
449: strlcpy(diff_ignore_pats, pattern, len);
450: } else {
451: /* old + "|" + new + NUL */
452: len = strlen(diff_ignore_pats) + strlen(pattern) + 2;
453: diff_ignore_pats = xrealloc(diff_ignore_pats, len, 1);
454: strlcat(diff_ignore_pats, "|", len);
455: strlcat(diff_ignore_pats, pattern, len);
456: }
1.1 joris 457: }