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