Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.72
1.72 ! xsa 1: /* $OpenBSD: rcsdiff.c,v 1.71 2006/10/12 17:20:12 niallo 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>
! 28:
! 29: #include <err.h>
! 30: #include <fcntl.h>
! 31: #include <stdio.h>
! 32: #include <stdlib.h>
! 33: #include <string.h>
! 34: #include <unistd.h>
1.1 joris 35:
1.30 xsa 36: #include "rcsprog.h"
1.2 niallo 37: #include "diff.h"
1.1 joris 38:
1.69 millert 39: static int rcsdiff_file(RCSFILE *, RCSNUM *, const char *, int);
40: static int rcsdiff_rev(RCSFILE *, RCSNUM *, RCSNUM *, int);
41: static void push_ignore_pats(char *);
1.1 joris 42:
1.69 millert 43: static int quiet;
1.18 xsa 44: static int kflag = RCS_KWEXP_ERR;
1.69 millert 45: static char *diff_ignore_pats;
1.18 xsa 46:
1.1 joris 47: int
48: rcsdiff_main(int argc, char **argv)
49: {
1.69 millert 50: int fd, i, ch, dflags, status;
1.46 ray 51: RCSNUM *rev1, *rev2;
1.1 joris 52: RCSFILE *file;
1.46 ray 53: char fpath[MAXPATHLEN], *rev_str1, *rev_str2;
1.69 millert 54: const char *errstr;
1.1 joris 55:
1.46 ray 56: rev1 = rev2 = NULL;
57: rev_str1 = rev_str2 = NULL;
1.61 xsa 58: status = D_SAME;
1.69 millert 59: dflags = 0;
1.1 joris 60:
1.59 ray 61: if (strlcpy(diffargs, "diff", sizeof(diffargs)) >= sizeof(diffargs))
1.63 xsa 62: errx(D_ERROR, "diffargs too long");
1.16 xsa 63:
1.69 millert 64: while ((ch = rcs_getopt(argc, argv, "abC:cdI:ik:npqr:TtU:uVwx::z::")) != -1) {
1.1 joris 65: switch (ch) {
1.69 millert 66: case 'a':
67: if (strlcat(diffargs, " -a", sizeof(diffargs)) >=
68: sizeof(diffargs))
69: errx(D_ERROR, "diffargs too long");
70: dflags |= D_FORCEASCII;
71: break;
72: case 'b':
73: if (strlcat(diffargs, " -b", sizeof(diffargs)) >=
74: sizeof(diffargs))
75: errx(D_ERROR, "diffargs too long");
76: dflags |= D_FOLDBLANKS;
77: break;
78: case 'C':
79: (void)strlcat(diffargs, " -C", sizeof(diffargs));
80: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
81: sizeof(diffargs))
82: errx(D_ERROR, "diffargs too long");
83: diff_context = strtonum(rcs_optarg, 0, INT_MAX, &errstr);
84: if (errstr)
85: errx(D_ERROR, "context is %s: %s",
86: errstr, rcs_optarg);
87: diff_format = D_CONTEXT;
88: break;
1.3 joris 89: case 'c':
1.59 ray 90: if (strlcat(diffargs, " -c", sizeof(diffargs)) >=
91: sizeof(diffargs))
1.63 xsa 92: errx(D_ERROR, "diffargs too long");
1.3 joris 93: diff_format = D_CONTEXT;
94: break;
1.69 millert 95: case 'd':
96: if (strlcat(diffargs, " -d", sizeof(diffargs)) >=
97: sizeof(diffargs))
98: errx(D_ERROR, "diffargs too long");
99: dflags |= D_MINIMAL;
100: break;
101: case 'i':
102: if (strlcat(diffargs, " -i", sizeof(diffargs)) >=
103: sizeof(diffargs))
104: errx(D_ERROR, "diffargs too long");
105: dflags |= D_IGNORECASE;
106: break;
107: case 'I':
108: (void)strlcat(diffargs, " -I", sizeof(diffargs));
109: if (strlcat(diffargs, rcs_optarg, sizeof(diffargs)) >=
110: sizeof(diffargs))
111: errx(D_ERROR, "diffargs too long");
112: push_ignore_pats(rcs_optarg);
113: break;
1.18 xsa 114: case 'k':
115: kflag = rcs_kflag_get(rcs_optarg);
116: if (RCS_KWEXP_INVAL(kflag)) {
1.54 xsa 117: warnx("invalid RCS keyword substitution mode");
1.18 xsa 118: (usage)();
1.62 xsa 119: exit(D_ERROR);
1.18 xsa 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.68 ray 186: exit(D_ERROR);
1.1 joris 187: }
188: }
189:
1.11 joris 190: argc -= rcs_optind;
191: argv += rcs_optind;
1.1 joris 192:
193: if (argc == 0) {
1.52 xsa 194: warnx("no input file");
1.1 joris 195: (usage)();
1.62 xsa 196: exit(D_ERROR);
1.1 joris 197: }
198:
1.69 millert 199: if (diff_ignore_pats != NULL) {
200: char buf[BUFSIZ];
201: int error;
202:
203: diff_ignore_re = xmalloc(sizeof(*diff_ignore_re));
204: if ((error = regcomp(diff_ignore_re, diff_ignore_pats,
205: REG_NEWLINE | REG_EXTENDED)) != 0) {
206: regerror(error, diff_ignore_re, buf, sizeof(buf));
207: if (*diff_ignore_pats != '\0')
208: errx(D_ERROR, "%s: %s", diff_ignore_pats, buf);
209: else
210: errx(D_ERROR, "%s", buf);
211: }
212: }
213:
1.1 joris 214: for (i = 0; i < argc; i++) {
1.65 ray 215: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
216: if (fd < 0) {
1.71 niallo 217: warn("%s", fpath);
1.1 joris 218: continue;
1.65 ray 219: }
1.1 joris 220:
1.58 joris 221: if ((file = rcs_open(fpath, fd,
222: RCS_READ|RCS_PARSE_FULLY)) == NULL)
1.1 joris 223: continue;
1.20 xsa 224:
1.44 xsa 225: rcs_kwexp_set(file, kflag);
1.1 joris 226:
1.46 ray 227: if (rev_str1 != NULL) {
1.47 ray 228: if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL)
1.63 xsa 229: errx(D_ERROR, "bad revision number");
1.46 ray 230: }
231: if (rev_str2 != NULL) {
1.47 ray 232: if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL)
1.63 xsa 233: errx(D_ERROR, "bad revision number");
1.46 ray 234: }
1.14 xsa 235:
1.69 millert 236: if (!quiet) {
1.18 xsa 237: fprintf(stderr, "%s\n", RCS_DIFF_DIV);
238: fprintf(stderr, "RCS file: %s\n", fpath);
1.14 xsa 239: }
1.8 joris 240:
241: diff_file = argv[i];
1.1 joris 242:
1.46 ray 243: /* No revisions given. */
1.61 xsa 244: if (rev_str1 == NULL)
1.69 millert 245: status = rcsdiff_file(file, file->rf_head, argv[i],
246: dflags);
1.46 ray 247: /* One revision given. */
1.61 xsa 248: else if (rev_str2 == NULL)
1.69 millert 249: status = rcsdiff_file(file, rev1, argv[i], dflags);
1.46 ray 250: /* Two revisions given. */
1.61 xsa 251: else
1.69 millert 252: status = rcsdiff_rev(file, rev1, rev2, dflags);
1.1 joris 253:
254: rcs_close(file);
1.46 ray 255:
256: if (rev1 != NULL) {
257: rcsnum_free(rev1);
258: rev1 = NULL;
259: }
260: if (rev2 != NULL) {
261: rcsnum_free(rev2);
262: rev2 = NULL;
263: }
1.1 joris 264: }
265:
1.26 niallo 266: return (status);
1.1 joris 267: }
268:
269: void
270: rcsdiff_usage(void)
271: {
1.9 deraadt 272: fprintf(stderr,
1.55 jmc 273: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] "
1.70 jmc 274: "[-xsuffixes] [-ztz] [diff_options] file ...\n");
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;
285: char rbuf[64];
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.24 niallo 316: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev);
317: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 318:
1.57 joris 319: if ((b2 = rcs_buf_load(filename, BUF_AUTOEXT)) == 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.67 ray 335: rcs_buf_write_stmp(b1, path1);
1.37 joris 336:
1.57 joris 337: rcs_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.67 ray 344: rcs_buf_write_stmp(b2, path2);
1.37 joris 345:
1.57 joris 346: rcs_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.69 millert 352: ret = rcs_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.57 joris 358: rcs_buf_free(b1);
1.37 joris 359: if (b2 != NULL)
1.57 joris 360: rcs_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.68 ray 375: char *path1, *path2, rbuf1[64], rbuf2[64];
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.24 niallo 396: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev1);
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.24 niallo 409: tv2[0].tv_sec = (long)rcs_rev_getdate(file, rev2);
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.67 ray 416: rcs_buf_write_stmp(b1, path1);
1.37 joris 417:
1.57 joris 418: rcs_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.67 ray 425: rcs_buf_write_stmp(b2, path2);
1.37 joris 426:
1.57 joris 427: rcs_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.69 millert 433: ret = rcs_diffreg(path1, path2, NULL, dflags);
1.37 joris 434:
435: out:
436: if (b1 != NULL)
1.57 joris 437: rcs_buf_free(b1);
1.37 joris 438: if (b2 != NULL)
1.57 joris 439: rcs_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;
460: diff_ignore_pats = xrealloc(diff_ignore_pats, len, 1);
461: strlcat(diff_ignore_pats, "|", len);
462: strlcat(diff_ignore_pats, pattern, len);
463: }
1.1 joris 464: }