Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.84
1.84 ! nicm 1: /* $OpenBSD: rcsdiff.c,v 1.83 2015/06/13 20:15:21 nicm 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.84 ! nicm 253: rcsnum_free(rev1);
! 254: rcsnum_free(rev2);
! 255: rev1 = rev2 = NULL;
1.1 joris 256: }
257:
1.26 niallo 258: return (status);
1.1 joris 259: }
260:
1.80 otto 261: __dead void
1.1 joris 262: rcsdiff_usage(void)
263: {
1.9 deraadt 264: fprintf(stderr,
1.73 sobrado 265: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] [-xsuffixes] [-ztz]\n"
266: " [diff_options] file ...\n");
1.80 otto 267:
268: exit(D_ERROR);
1.1 joris 269: }
270:
271: static int
1.69 millert 272: rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags)
1.1 joris 273: {
1.58 joris 274: int ret, fd;
1.49 joris 275: time_t t;
1.58 joris 276: struct stat st;
1.60 xsa 277: char *path1, *path2;
1.1 joris 278: BUF *b1, *b2;
1.74 xsa 279: char rbuf[RCS_REV_BUFSZ];
1.49 joris 280: struct tm *tb;
1.24 niallo 281: struct timeval tv[2], tv2[2];
1.49 joris 282:
1.24 niallo 283: memset(&tv, 0, sizeof(tv));
284: memset(&tv2, 0, sizeof(tv2));
1.15 xsa 285:
1.61 xsa 286: ret = D_ERROR;
1.37 joris 287: b1 = b2 = NULL;
288:
1.49 joris 289: diff_rev1 = rev;
290: diff_rev2 = NULL;
1.66 niallo 291: path1 = path2 = NULL;
1.49 joris 292:
1.58 joris 293: if ((fd = open(filename, O_RDONLY)) == -1) {
1.52 xsa 294: warn("%s", filename);
1.37 joris 295: goto out;
1.15 xsa 296: }
1.1 joris 297:
298: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
1.69 millert 299: if (!quiet) {
1.18 xsa 300: fprintf(stderr, "retrieving revision %s\n", rbuf);
301: fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename);
1.16 xsa 302: }
1.1 joris 303:
1.23 xsa 304: if ((b1 = rcs_getrev(file, rev)) == NULL) {
1.52 xsa 305: warnx("failed to retrieve revision %s", rbuf);
1.37 joris 306: goto out;
1.1 joris 307: }
1.37 joris 308:
1.31 niallo 309: b1 = rcs_kwexp_buf(b1, file, rev);
1.79 deraadt 310: tv[0].tv_sec = rcs_rev_getdate(file, rev);
1.24 niallo 311: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 312:
1.77 ray 313: if ((b2 = buf_load(filename)) == NULL) {
1.52 xsa 314: warnx("failed to load file: `%s'", filename);
1.37 joris 315: goto out;
1.4 niallo 316: }
1.37 joris 317:
1.49 joris 318: /* XXX - GNU uses GMT */
1.58 joris 319: if (fstat(fd, &st) == -1)
1.63 xsa 320: err(D_ERROR, "%s", filename);
1.58 joris 321:
1.49 joris 322: tb = gmtime(&st.st_mtime);
323: t = mktime(tb);
324:
325: tv2[0].tv_sec = t;
1.50 deraadt 326: tv2[1].tv_sec = t;
1.4 niallo 327:
1.60 xsa 328: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 329: buf_write_stmp(b1, path1);
1.37 joris 330:
1.76 ray 331: buf_free(b1);
1.37 joris 332: b1 = NULL;
333:
1.24 niallo 334: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 335: warn("utimes");
1.4 niallo 336:
1.60 xsa 337: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 338: buf_write_stmp(b2, path2);
1.37 joris 339:
1.76 ray 340: buf_free(b2);
1.37 joris 341: b2 = NULL;
342:
1.24 niallo 343: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 344: warn("utimes");
1.4 niallo 345:
1.75 ray 346: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 347:
348: out:
1.58 joris 349: if (fd != -1)
350: (void)close(fd);
1.84 ! nicm 351: buf_free(b1);
! 352: buf_free(b2);
1.83 nicm 353: free(path1);
354: free(path2);
1.37 joris 355:
356: return (ret);
1.4 niallo 357: }
358:
359: static int
1.69 millert 360: rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags)
1.4 niallo 361: {
1.68 ray 362: struct timeval tv[2], tv2[2];
363: BUF *b1, *b2;
1.37 joris 364: int ret;
1.74 xsa 365: char *path1, *path2, rbuf1[RCS_REV_BUFSZ], rbuf2[RCS_REV_BUFSZ];
1.24 niallo 366:
1.61 xsa 367: ret = D_ERROR;
1.37 joris 368: b1 = b2 = NULL;
1.24 niallo 369: memset(&tv, 0, sizeof(tv));
370: memset(&tv2, 0, sizeof(tv2));
1.4 niallo 371:
1.49 joris 372: diff_rev1 = rev1;
373: diff_rev2 = rev2;
1.66 niallo 374: path1 = path2 = NULL;
1.49 joris 375:
1.16 xsa 376: rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1));
1.69 millert 377: if (!quiet)
1.49 joris 378: fprintf(stderr, "retrieving revision %s\n", rbuf1);
1.4 niallo 379:
1.23 xsa 380: if ((b1 = rcs_getrev(file, rev1)) == NULL) {
1.52 xsa 381: warnx("failed to retrieve revision %s", rbuf1);
1.37 joris 382: goto out;
1.4 niallo 383: }
1.37 joris 384:
1.31 niallo 385: b1 = rcs_kwexp_buf(b1, file, rev1);
1.79 deraadt 386: tv[0].tv_sec = rcs_rev_getdate(file, rev1);
1.24 niallo 387: tv[1].tv_sec = tv[0].tv_sec;
1.4 niallo 388:
1.16 xsa 389: rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2));
1.69 millert 390: if (!quiet)
1.18 xsa 391: fprintf(stderr, "retrieving revision %s\n", rbuf2);
1.4 niallo 392:
1.23 xsa 393: if ((b2 = rcs_getrev(file, rev2)) == NULL) {
1.52 xsa 394: warnx("failed to retrieve revision %s", rbuf2);
1.37 joris 395: goto out;
1.1 joris 396: }
1.37 joris 397:
1.31 niallo 398: b2 = rcs_kwexp_buf(b2, file, rev2);
1.79 deraadt 399: tv2[0].tv_sec = rcs_rev_getdate(file, rev2);
1.24 niallo 400: tv2[1].tv_sec = tv2[0].tv_sec;
1.16 xsa 401:
1.69 millert 402: if (!quiet)
1.33 xsa 403: fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2);
1.1 joris 404:
1.60 xsa 405: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 406: buf_write_stmp(b1, path1);
1.37 joris 407:
1.76 ray 408: buf_free(b1);
1.37 joris 409: b1 = NULL;
410:
1.24 niallo 411: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 412: warn("utimes");
1.1 joris 413:
1.60 xsa 414: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 415: buf_write_stmp(b2, path2);
1.37 joris 416:
1.76 ray 417: buf_free(b2);
1.37 joris 418: b2 = NULL;
1.1 joris 419:
1.24 niallo 420: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 421: warn("utimes");
1.32 deraadt 422:
1.75 ray 423: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 424:
425: out:
1.84 ! nicm 426: buf_free(b1);
! 427: buf_free(b2);
1.83 nicm 428: free(path1);
429: free(path2);
1.1 joris 430:
1.37 joris 431: return (ret);
1.69 millert 432: }
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;
1.81 deraadt 446: diff_ignore_pats = xreallocarray(diff_ignore_pats, len, 1);
1.69 millert 447: strlcat(diff_ignore_pats, "|", len);
448: strlcat(diff_ignore_pats, pattern, len);
449: }
1.1 joris 450: }