Annotation of src/usr.bin/rcs/rcsdiff.c, Revision 1.68
1.68 ! ray 1: /* $OpenBSD: rcsdiff.c,v 1.67 2006/07/08 09:25:44 ray 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:
32: static int rcsdiff_file(RCSFILE *, RCSNUM *, const char *);
1.38 niallo 33: static int rcsdiff_rev(RCSFILE *, RCSNUM *, RCSNUM *);
1.1 joris 34:
1.51 xsa 35: static int flags = 0;
1.18 xsa 36: static int kflag = RCS_KWEXP_ERR;
37:
1.1 joris 38: int
39: rcsdiff_main(int argc, char **argv)
40: {
1.58 joris 41: int fd, i, ch, status;
1.46 ray 42: RCSNUM *rev1, *rev2;
1.1 joris 43: RCSFILE *file;
1.46 ray 44: char fpath[MAXPATHLEN], *rev_str1, *rev_str2;
1.1 joris 45:
1.46 ray 46: rev1 = rev2 = NULL;
47: rev_str1 = rev_str2 = NULL;
1.61 xsa 48: status = D_SAME;
1.1 joris 49:
1.59 ray 50: if (strlcpy(diffargs, "diff", sizeof(diffargs)) >= sizeof(diffargs))
1.63 xsa 51: errx(D_ERROR, "diffargs too long");
1.16 xsa 52:
1.43 xsa 53: while ((ch = rcs_getopt(argc, argv, "ck:nqr:TuVx::z::")) != -1) {
1.1 joris 54: switch (ch) {
1.3 joris 55: case 'c':
1.59 ray 56: if (strlcat(diffargs, " -c", sizeof(diffargs)) >=
57: sizeof(diffargs))
1.63 xsa 58: errx(D_ERROR, "diffargs too long");
1.3 joris 59: diff_format = D_CONTEXT;
60: break;
1.18 xsa 61: case 'k':
62: kflag = rcs_kflag_get(rcs_optarg);
63: if (RCS_KWEXP_INVAL(kflag)) {
1.54 xsa 64: warnx("invalid RCS keyword substitution mode");
1.18 xsa 65: (usage)();
1.62 xsa 66: exit(D_ERROR);
1.18 xsa 67: }
68: break;
1.3 joris 69: case 'n':
1.59 ray 70: if (strlcat(diffargs, " -n", sizeof(diffargs)) >=
71: sizeof(diffargs))
1.63 xsa 72: errx(D_ERROR, "diffargs too long");
1.3 joris 73: diff_format = D_RCSDIFF;
74: break;
1.1 joris 75: case 'q':
1.51 xsa 76: flags |= QUIET;
1.3 joris 77: break;
1.4 niallo 78: case 'r':
1.46 ray 79: rcs_setrevstr2(&rev_str1, &rev_str2, rcs_optarg);
1.19 xsa 80: break;
81: case 'T':
1.21 xsa 82: /*
83: * kept for compatibility
84: */
1.17 xsa 85: break;
1.59 ray 86: case 'u':
87: if (strlcat(diffargs, " -u", sizeof(diffargs)) >=
88: sizeof(diffargs))
1.63 xsa 89: errx(D_ERROR, "diffargs too long");
1.59 ray 90: diff_format = D_UNIFIED;
91: break;
1.17 xsa 92: case 'V':
93: printf("%s\n", rcs_version);
94: exit(0);
95: case 'x':
1.41 ray 96: /* Use blank extension if none given. */
97: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.36 joris 98: break;
99: case 'z':
100: timezone_flag = rcs_optarg;
1.4 niallo 101: break;
1.1 joris 102: default:
1.11 joris 103: (usage)();
1.68 ! ray 104: exit(D_ERROR);
1.1 joris 105: }
106: }
107:
1.11 joris 108: argc -= rcs_optind;
109: argv += rcs_optind;
1.1 joris 110:
111: if (argc == 0) {
1.52 xsa 112: warnx("no input file");
1.1 joris 113: (usage)();
1.62 xsa 114: exit(D_ERROR);
1.1 joris 115: }
116:
117: for (i = 0; i < argc; i++) {
1.65 ray 118: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
119: if (fd < 0) {
120: warnx("%s", fpath);
1.1 joris 121: continue;
1.65 ray 122: }
1.1 joris 123:
1.58 joris 124: if ((file = rcs_open(fpath, fd,
125: RCS_READ|RCS_PARSE_FULLY)) == NULL)
1.1 joris 126: continue;
1.20 xsa 127:
1.44 xsa 128: rcs_kwexp_set(file, kflag);
1.1 joris 129:
1.46 ray 130: if (rev_str1 != NULL) {
1.47 ray 131: if ((rev1 = rcs_getrevnum(rev_str1, file)) == NULL)
1.63 xsa 132: errx(D_ERROR, "bad revision number");
1.46 ray 133: }
134: if (rev_str2 != NULL) {
1.47 ray 135: if ((rev2 = rcs_getrevnum(rev_str2, file)) == NULL)
1.63 xsa 136: errx(D_ERROR, "bad revision number");
1.46 ray 137: }
1.14 xsa 138:
1.51 xsa 139: if (!(flags & QUIET)) {
1.18 xsa 140: fprintf(stderr, "%s\n", RCS_DIFF_DIV);
141: fprintf(stderr, "RCS file: %s\n", fpath);
1.14 xsa 142: }
1.8 joris 143:
144: diff_file = argv[i];
1.1 joris 145:
1.46 ray 146: /* No revisions given. */
1.61 xsa 147: if (rev_str1 == NULL)
148: status = rcsdiff_file(file, file->rf_head, argv[i]);
1.46 ray 149: /* One revision given. */
1.61 xsa 150: else if (rev_str2 == NULL)
151: status = rcsdiff_file(file, rev1, argv[i]);
1.46 ray 152: /* Two revisions given. */
1.61 xsa 153: else
154: status = rcsdiff_rev(file, rev1, rev2);
1.1 joris 155:
156: rcs_close(file);
1.46 ray 157:
158: if (rev1 != NULL) {
159: rcsnum_free(rev1);
160: rev1 = NULL;
161: }
162: if (rev2 != NULL) {
163: rcsnum_free(rev2);
164: rev2 = NULL;
165: }
1.1 joris 166: }
167:
1.26 niallo 168: return (status);
1.1 joris 169: }
170:
171: void
172: rcsdiff_usage(void)
173: {
1.9 deraadt 174: fprintf(stderr,
1.55 jmc 175: "usage: rcsdiff [-cnquV] [-kmode] [-rrev] "
1.35 jmc 176: "[-xsuffixes] [-ztz] file ...\n");
1.1 joris 177: }
178:
179: static int
1.23 xsa 180: rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename)
1.1 joris 181: {
1.58 joris 182: int ret, fd;
1.49 joris 183: time_t t;
1.58 joris 184: struct stat st;
1.60 xsa 185: char *path1, *path2;
1.1 joris 186: BUF *b1, *b2;
187: char rbuf[64];
1.49 joris 188: struct tm *tb;
1.24 niallo 189: struct timeval tv[2], tv2[2];
1.49 joris 190:
1.24 niallo 191: memset(&tv, 0, sizeof(tv));
192: memset(&tv2, 0, sizeof(tv2));
1.15 xsa 193:
1.61 xsa 194: ret = D_ERROR;
1.37 joris 195: b1 = b2 = NULL;
196:
1.49 joris 197: diff_rev1 = rev;
198: diff_rev2 = NULL;
1.66 niallo 199: path1 = path2 = NULL;
1.49 joris 200:
1.58 joris 201: if ((fd = open(filename, O_RDONLY)) == -1) {
1.52 xsa 202: warn("%s", filename);
1.37 joris 203: goto out;
1.15 xsa 204: }
1.1 joris 205:
206: rcsnum_tostr(rev, rbuf, sizeof(rbuf));
1.51 xsa 207: if (!(flags & QUIET)) {
1.18 xsa 208: fprintf(stderr, "retrieving revision %s\n", rbuf);
209: fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename);
1.16 xsa 210: }
1.1 joris 211:
1.23 xsa 212: if ((b1 = rcs_getrev(file, rev)) == NULL) {
1.52 xsa 213: warnx("failed to retrieve revision %s", rbuf);
1.37 joris 214: goto out;
1.1 joris 215: }
1.37 joris 216:
1.31 niallo 217: b1 = rcs_kwexp_buf(b1, file, rev);
1.24 niallo 218: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev);
219: tv[1].tv_sec = tv[0].tv_sec;
1.1 joris 220:
1.57 joris 221: if ((b2 = rcs_buf_load(filename, BUF_AUTOEXT)) == NULL) {
1.52 xsa 222: warnx("failed to load file: `%s'", filename);
1.37 joris 223: goto out;
1.4 niallo 224: }
1.37 joris 225:
1.49 joris 226: /* XXX - GNU uses GMT */
1.58 joris 227: if (fstat(fd, &st) == -1)
1.63 xsa 228: err(D_ERROR, "%s", filename);
1.58 joris 229:
1.49 joris 230: tb = gmtime(&st.st_mtime);
231: t = mktime(tb);
232:
233: tv2[0].tv_sec = t;
1.50 deraadt 234: tv2[1].tv_sec = t;
1.4 niallo 235:
1.60 xsa 236: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 237: rcs_buf_write_stmp(b1, path1);
1.37 joris 238:
1.57 joris 239: rcs_buf_free(b1);
1.37 joris 240: b1 = NULL;
241:
1.24 niallo 242: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 243: warn("utimes");
1.4 niallo 244:
1.60 xsa 245: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 246: rcs_buf_write_stmp(b2, path2);
1.37 joris 247:
1.57 joris 248: rcs_buf_free(b2);
1.37 joris 249: b2 = NULL;
250:
1.24 niallo 251: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 252: warn("utimes");
1.4 niallo 253:
1.61 xsa 254: ret = rcs_diffreg(path1, path2, NULL);
1.37 joris 255:
256: out:
1.58 joris 257: if (fd != -1)
258: (void)close(fd);
1.37 joris 259: if (b1 != NULL)
1.57 joris 260: rcs_buf_free(b1);
1.37 joris 261: if (b2 != NULL)
1.57 joris 262: rcs_buf_free(b2);
1.60 xsa 263: if (path1 != NULL)
264: xfree(path1);
265: if (path2 != NULL)
266: xfree(path2);
1.37 joris 267:
268: return (ret);
1.4 niallo 269: }
270:
271: static int
1.38 niallo 272: rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2)
1.4 niallo 273: {
1.68 ! ray 274: struct timeval tv[2], tv2[2];
! 275: BUF *b1, *b2;
1.37 joris 276: int ret;
1.68 ! ray 277: char *path1, *path2, rbuf1[64], rbuf2[64];
1.24 niallo 278:
1.61 xsa 279: ret = D_ERROR;
1.37 joris 280: b1 = b2 = NULL;
1.24 niallo 281: memset(&tv, 0, sizeof(tv));
282: memset(&tv2, 0, sizeof(tv2));
1.4 niallo 283:
1.49 joris 284: diff_rev1 = rev1;
285: diff_rev2 = rev2;
1.66 niallo 286: path1 = path2 = NULL;
1.49 joris 287:
1.16 xsa 288: rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1));
1.51 xsa 289: if (!(flags & QUIET))
1.49 joris 290: fprintf(stderr, "retrieving revision %s\n", rbuf1);
1.4 niallo 291:
1.23 xsa 292: if ((b1 = rcs_getrev(file, rev1)) == NULL) {
1.52 xsa 293: warnx("failed to retrieve revision %s", rbuf1);
1.37 joris 294: goto out;
1.4 niallo 295: }
1.37 joris 296:
1.31 niallo 297: b1 = rcs_kwexp_buf(b1, file, rev1);
1.24 niallo 298: tv[0].tv_sec = (long)rcs_rev_getdate(file, rev1);
299: tv[1].tv_sec = tv[0].tv_sec;
1.4 niallo 300:
1.16 xsa 301: rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2));
1.51 xsa 302: if (!(flags & QUIET))
1.18 xsa 303: fprintf(stderr, "retrieving revision %s\n", rbuf2);
1.4 niallo 304:
1.23 xsa 305: if ((b2 = rcs_getrev(file, rev2)) == NULL) {
1.52 xsa 306: warnx("failed to retrieve revision %s", rbuf2);
1.37 joris 307: goto out;
1.1 joris 308: }
1.37 joris 309:
1.31 niallo 310: b2 = rcs_kwexp_buf(b2, file, rev2);
1.24 niallo 311: tv2[0].tv_sec = (long)rcs_rev_getdate(file, rev2);
312: tv2[1].tv_sec = tv2[0].tv_sec;
1.16 xsa 313:
1.51 xsa 314: if (!(flags & QUIET))
1.33 xsa 315: fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2);
1.1 joris 316:
1.60 xsa 317: (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 318: rcs_buf_write_stmp(b1, path1);
1.37 joris 319:
1.57 joris 320: rcs_buf_free(b1);
1.37 joris 321: b1 = NULL;
322:
1.24 niallo 323: if (utimes(path1, (const struct timeval *)&tv) < 0)
1.52 xsa 324: warn("utimes");
1.1 joris 325:
1.60 xsa 326: (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1.67 ray 327: rcs_buf_write_stmp(b2, path2);
1.37 joris 328:
1.57 joris 329: rcs_buf_free(b2);
1.37 joris 330: b2 = NULL;
1.1 joris 331:
1.24 niallo 332: if (utimes(path2, (const struct timeval *)&tv2) < 0)
1.52 xsa 333: warn("utimes");
1.32 deraadt 334:
1.61 xsa 335: ret = rcs_diffreg(path1, path2, NULL);
1.37 joris 336:
337: out:
338: if (b1 != NULL)
1.57 joris 339: rcs_buf_free(b1);
1.37 joris 340: if (b2 != NULL)
1.57 joris 341: rcs_buf_free(b2);
1.60 xsa 342: if (path1 != NULL)
343: xfree(path1);
344: if (path2 != NULL)
345: xfree(path2);
1.1 joris 346:
1.37 joris 347: return (ret);
1.1 joris 348: }