Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.79
1.79 ! deraadt 1: /* $OpenBSD: rcsdiff.c,v 1.78 2010/12/06 22:50:34 chl 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.46 ray 54: char fpath[MAXPATHLEN], *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)();
1.62 xsa 120: exit(D_ERROR);
1.18 xsa 121: }
122: break;
1.3 joris 123: case 'n':
1.59 ray 124: if (strlcat(diffargs, " -n", sizeof(diffargs)) >=
125: sizeof(diffargs))
1.63 xsa 126: errx(D_ERROR, "diffargs too long");
1.3 joris 127: diff_format = D_RCSDIFF;
128: break;
1.69 millert 129: case 'p':
130: if (strlcat(diffargs, " -p", sizeof(diffargs)) >=
131: sizeof(diffargs))
132: errx(D_ERROR, "diffargs too long");
133: dflags |= D_PROTOTYPE;
134: break;
1.1 joris 135: case 'q':
1.69 millert 136: quiet = 1;
1.3 joris 137: break;
1.4 niallo 138: case 'r':
1.46 ray 139: rcs_setrevstr2(&rev_str1, &rev_str2, rcs_optarg);
1.19 xsa 140: break;
141: case 'T':
1.21 xsa 142: /*
143: * kept for compatibility
144: */
1.17 xsa 145: break;
1.69 millert 146: case 't':
147: if (strlcat(diffargs, " -t", sizeof(diffargs)) >=
148: sizeof(diffargs))
149: errx(D_ERROR, "diffargs too long");
150: dflags |= D_EXPANDTABS;
151: break;
152: case 'U':
153: (void)strlcat(diffargs, " -U", sizeof(diffargs));
154: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
155: sizeof(diffargs))
156: errx(D_ERROR, "diffargs too long");
157: diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr);
158: if (errstr)
159: errx(D_ERROR, "context is %s: %s",
160: errstr, rcs_optarg);
161: diff_format = D_UNIFIED;
162: break;
1.59 ray 163: case 'u':
164: if (strlcat(diffargs, " -u", sizeof(diffargs)) >=
165: sizeof(diffargs))
1.63 xsa 166: errx(D_ERROR, "diffargs too long");
1.59 ray 167: diff_format = D_UNIFIED;
168: break;
1.17 xsa 169: case 'V':
170: printf("%s\n", rcs_version);
171: exit(0);
1.69 millert 172: case 'w':
173: if (strlcat(diffargs, " -w", sizeof(diffargs)) >=
174: sizeof(diffargs))
175: errx(D_ERROR, "diffargs too long");
176: dflags |= D_IGNOREBLANKS;
177: break;
1.17 xsa 178: case 'x':
1.41 ray 179: /* Use blank extension if none given. */
180: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.36 joris 181: break;
182: case 'z':
183: timezone_flag = rcs_optarg;
1.4 niallo 184: break;
1.1 joris 185: default:
1.11 joris 186: (usage)();
1.68 ray 187: exit(D_ERROR);
1.1 joris 188: }
189: }
190:
1.11 joris 191: argc -= rcs_optind;
192: argv += rcs_optind;
1.1 joris 193:
194: if (argc == 0) {
1.52 xsa 195: warnx("no input file");
1.1 joris 196: (usage)();
1.62 xsa 197: exit(D_ERROR);
1.1 joris 198: }
199:
1.69 millert 200: if (diff_ignore_pats != NULL) {
201: char buf[BUFSIZ];
202: int error;
203:
204: diff_ignore_re = xmalloc(sizeof(*diff_ignore_re));
205: if ((error = regcomp(diff_ignore_re, diff_ignore_pats,
206: REG_NEWLINE | REG_EXTENDED)) != 0) {
207: regerror(error, diff_ignore_re, buf, sizeof(buf));
208: if (*diff_ignore_pats != '\0')
209: errx(D_ERROR, "%s: %s", diff_ignore_pats, buf);
210: else
211: errx(D_ERROR, "%s", buf);
212: }
213: }
214:
1.1 joris 215: for (i = 0; i < argc; i++) {
1.65 ray 216: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
217: if (fd < 0) {
1.71 niallo 218: warn("%s", fpath);
1.1 joris 219: continue;
1.65 ray 220: }
1.1 joris 221:
1.58 joris 222: if ((file = rcs_open(fpath, fd,
223: RCS_READ|RCS_PARSE_FULLY)) == NULL)
1.1 joris 224: continue;
1.20 xsa 225:
1.44 xsa 226: rcs_kwexp_set(file, kflag);
1.1 joris 227:
1.46 ray 228: if (rev_str1 != NULL) {
1.47 ray 229: if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL)
1.63 xsa 230: errx(D_ERROR, "bad revision number");
1.46 ray 231: }
232: if (rev_str2 != NULL) {
1.47 ray 233: if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL)
1.63 xsa 234: errx(D_ERROR, "bad revision number");
1.46 ray 235: }
1.14 xsa 236:
1.69 millert 237: if (!quiet) {
1.18 xsa 238: fprintf(stderr, "%s\n", RCS_DIFF_DIV);
239: fprintf(stderr, "RCS file: %s\n", fpath);
1.14 xsa 240: }
1.8 joris 241:
242: diff_file = argv[i];
1.1 joris 243:
1.46 ray 244: /* No revisions given. */
1.61 xsa 245: if (rev_str1 == NULL)
1.69 millert 246: status = rcsdiff_file(file, file->rf_head, argv[i],
247: dflags);
1.46 ray 248: /* One revision given. */
1.61 xsa 249: else if (rev_str2 == NULL)
1.69 millert 250: status = rcsdiff_file(file, rev1, argv[i], dflags);
1.46 ray 251: /* Two revisions given. */
1.61 xsa 252: else
1.69 millert 253: status = rcsdiff_rev(file, rev1, rev2, dflags);
1.1 joris 254:
255: rcs_close(file);
1.46 ray 256:
257: if (rev1 != NULL) {
258: rcsnum_free(rev1);
259: rev1 = NULL;
260: }
261: if (rev2 != NULL) {
262: rcsnum_free(rev2);
263: rev2 = NULL;
264: }
1.1 joris 265: }
266:
1.26 niallo 267: return (status);
1.1 joris 268: }
269:
270: void
271: rcsdiff_usage(void)
272: {
1.9 deraadt 273: fprintf(stderr,
1.73 sobrado 274: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] [-xsuffixes] [-ztz]\n"
275: " [diff_options] file ...\n");
1.1 joris 276: }
277:
278: static int
1.69 millert 279: rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags)
1.1 joris 280: {
1.58 joris 281: int ret, fd;
1.49 joris 282: time_t t;
1.58 joris 283: struct stat st;
1.60 xsa 284: char *path1, *path2;
1.1 joris 285: BUF *b1, *b2;
1.74 xsa 286: char rbuf[RCS_REV_BUFSZ];
1.49 joris 287: struct tm *tb;
1.24 niallo 288: struct timeval tv[2], tv2[2];
1.49 joris 289:
1.24 niallo 290: memset(&tv, 0, sizeof(tv));
291: memset(&tv2, 0, sizeof(tv2));
1.15 xsa 292:
1.61 xsa 293: ret = D_ERROR;
1.37 joris 294: b1 = b2 = NULL;
295:
1.49 joris 296: diff_rev1 = rev;
297: diff_rev2 = NULL;
1.66 niallo 298: path1 = path2 = NULL;
1.49 joris 299:
1.58 joris 300: if ((fd = open(filename, O_RDONLY)) == -1) {
1.52 xsa 301: warn("%s", filename);
1.37 joris 302: goto out;
1.15 xsa 303: }
1.1 joris 304:
305: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
1.69 millert 306: if (!quiet) {
1.18 xsa 307: fprintf(stderr, "retrieving revision %s\n", rbuf);
308: fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename);
1.16 xsa 309: }
1.1 joris 310:
1.23 xsa 311: if ((b1 = rcs_getrev(file, rev)) == NULL) {
1.52 xsa 312: warnx("failed to retrieve revision %s", rbuf);
1.37 joris 313: goto out;
1.1 joris 314: }
1.37 joris 315:
1.31 niallo 316: b1 = rcs_kwexp_buf(b1, file, rev);
1.79 ! deraadt 317: tv[0].tv_sec = rcs_rev_getdate(file, rev);
1.24 niallo 318: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 319:
1.77 ray 320: if ((b2 = buf_load(filename)) == NULL) {
1.52 xsa 321: warnx("failed to load file: `%s'", filename);
1.37 joris 322: goto out;
1.4 niallo 323: }
1.37 joris 324:
1.49 joris 325: /* XXX - GNU uses GMT */
1.58 joris 326: if (fstat(fd, &st) == -1)
1.63 xsa 327: err(D_ERROR, "%s", filename);
1.58 joris 328:
1.49 joris 329: tb = gmtime(&st.st_mtime);
330: t = mktime(tb);
331:
332: tv2[0].tv_sec = t;
1.50 deraadt 333: tv2[1].tv_sec = t;
1.4 niallo 334:
1.60 xsa 335: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 336: buf_write_stmp(b1, path1);
1.37 joris 337:
1.76 ray 338: buf_free(b1);
1.37 joris 339: b1 = NULL;
340:
1.24 niallo 341: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 342: warn("utimes");
1.4 niallo 343:
1.60 xsa 344: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 345: buf_write_stmp(b2, path2);
1.37 joris 346:
1.76 ray 347: buf_free(b2);
1.37 joris 348: b2 = NULL;
349:
1.24 niallo 350: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 351: warn("utimes");
1.4 niallo 352:
1.75 ray 353: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 354:
355: out:
1.58 joris 356: if (fd != -1)
357: (void)close(fd);
1.37 joris 358: if (b1 != NULL)
1.76 ray 359: buf_free(b1);
1.37 joris 360: if (b2 != NULL)
1.76 ray 361: buf_free(b2);
1.60 xsa 362: if (path1 != NULL)
363: xfree(path1);
364: if (path2 != NULL)
365: xfree(path2);
1.37 joris 366:
367: return (ret);
1.4 niallo 368: }
369:
370: static int
1.69 millert 371: rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags)
1.4 niallo 372: {
1.68 ray 373: struct timeval tv[2], tv2[2];
374: BUF *b1, *b2;
1.37 joris 375: int ret;
1.74 xsa 376: char *path1, *path2, rbuf1[RCS_REV_BUFSZ], rbuf2[RCS_REV_BUFSZ];
1.24 niallo 377:
1.61 xsa 378: ret = D_ERROR;
1.37 joris 379: b1 = b2 = NULL;
1.24 niallo 380: memset(&tv, 0, sizeof(tv));
381: memset(&tv2, 0, sizeof(tv2));
1.4 niallo 382:
1.49 joris 383: diff_rev1 = rev1;
384: diff_rev2 = rev2;
1.66 niallo 385: path1 = path2 = NULL;
1.49 joris 386:
1.16 xsa 387: rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1));
1.69 millert 388: if (!quiet)
1.49 joris 389: fprintf(stderr, "retrieving revision %s\n", rbuf1);
1.4 niallo 390:
1.23 xsa 391: if ((b1 = rcs_getrev(file, rev1)) == NULL) {
1.52 xsa 392: warnx("failed to retrieve revision %s", rbuf1);
1.37 joris 393: goto out;
1.4 niallo 394: }
1.37 joris 395:
1.31 niallo 396: b1 = rcs_kwexp_buf(b1, file, rev1);
1.79 ! deraadt 397: tv[0].tv_sec = rcs_rev_getdate(file, rev1);
1.24 niallo 398: tv[1].tv_sec = tv[0].tv_sec;
1.4 niallo 399:
1.16 xsa 400: rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2));
1.69 millert 401: if (!quiet)
1.18 xsa 402: fprintf(stderr, "retrieving revision %s\n", rbuf2);
1.4 niallo 403:
1.23 xsa 404: if ((b2 = rcs_getrev(file, rev2)) == NULL) {
1.52 xsa 405: warnx("failed to retrieve revision %s", rbuf2);
1.37 joris 406: goto out;
1.1 joris 407: }
1.37 joris 408:
1.31 niallo 409: b2 = rcs_kwexp_buf(b2, file, rev2);
1.79 ! deraadt 410: tv2[0].tv_sec = rcs_rev_getdate(file, rev2);
1.24 niallo 411: tv2[1].tv_sec = tv2[0].tv_sec;
1.16 xsa 412:
1.69 millert 413: if (!quiet)
1.33 xsa 414: fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2);
1.1 joris 415:
1.60 xsa 416: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 417: buf_write_stmp(b1, path1);
1.37 joris 418:
1.76 ray 419: buf_free(b1);
1.37 joris 420: b1 = NULL;
421:
1.24 niallo 422: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 423: warn("utimes");
1.1 joris 424:
1.60 xsa 425: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.76 ray 426: buf_write_stmp(b2, path2);
1.37 joris 427:
1.76 ray 428: buf_free(b2);
1.37 joris 429: b2 = NULL;
1.1 joris 430:
1.24 niallo 431: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 432: warn("utimes");
1.32 deraadt 433:
1.75 ray 434: ret = diffreg(path1, path2, NULL, dflags);
1.37 joris 435:
436: out:
437: if (b1 != NULL)
1.76 ray 438: buf_free(b1);
1.37 joris 439: if (b2 != NULL)
1.76 ray 440: buf_free(b2);
1.60 xsa 441: if (path1 != NULL)
442: xfree(path1);
443: if (path2 != NULL)
444: xfree(path2);
1.1 joris 445:
1.37 joris 446: return (ret);
1.69 millert 447: }
448:
449: static void
450: push_ignore_pats(char *pattern)
451: {
452: size_t len;
453:
454: if (diff_ignore_pats == NULL) {
455: len = strlen(pattern) + 1;
456: diff_ignore_pats = xmalloc(len);
457: strlcpy(diff_ignore_pats, pattern, len);
458: } else {
459: /* old + "|" + new + NUL */
460: len = strlen(diff_ignore_pats) + strlen(pattern) + 2;
461: diff_ignore_pats = xrealloc(diff_ignore_pats, len, 1);
462: strlcat(diff_ignore_pats, "|", len);
463: strlcat(diff_ignore_pats, pattern, len);
464: }
1.1 joris 465: }