Annotation of src/usr.bin/cvs/diff.c, Revision 1.133
1.133 ! tobias 1: /* $OpenBSD: diff.c,v 1.132 2008/03/01 21:29:36 deraadt Exp $ */
1.1 jfb 2: /*
1.91 joris 3: * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.91 joris 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 jfb 16: */
17:
1.118 otto 18: #include <sys/stat.h>
1.125 tobias 19: #include <sys/time.h>
1.118 otto 20:
21: #include <errno.h>
1.131 joris 22: #include <fcntl.h>
1.125 tobias 23: #include <stdlib.h>
1.118 otto 24: #include <string.h>
25: #include <unistd.h>
1.1 jfb 26:
27: #include "cvs.h"
1.56 niallo 28: #include "diff.h"
1.108 joris 29: #include "remote.h"
1.86 joris 30:
1.91 joris 31: void cvs_diff_local(struct cvs_file *);
1.46 xsa 32:
1.126 tobias 33: static int Nflag = 0;
34: static int force_head = 0;
35: static char *koptstr;
36: static char *rev1 = NULL;
37: static char *rev2 = NULL;
1.94 joris 38:
1.36 jfb 39: struct cvs_cmd cvs_cmd_diff = {
1.124 tobias 40: CVS_OP_DIFF, CVS_USE_WDIR, "diff",
1.36 jfb 41: { "di", "dif" },
42: "Show differences between revisions",
1.122 tobias 43: "[-cilNnpRu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
1.43 xsa 44: "[-k mode] [file ...]",
1.126 tobias 45: "cfD:ik:lNnpr:Ru",
1.125 tobias 46: NULL,
47: cvs_diff
48: };
49:
50: struct cvs_cmd cvs_cmd_rdiff = {
51: CVS_OP_RDIFF, 0, "rdiff",
52: { "patch", "pa" },
53: "Show differences between revisions",
54: "[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev\n"
55: "[-D date2 | -r rev2] [-k mode] module ...",
1.126 tobias 56: "cfD:k:lr:RuV:",
1.36 jfb 57: NULL,
1.91 joris 58: cvs_diff
1.36 jfb 59: };
60:
1.91 joris 61: int
62: cvs_diff(int argc, char **argv)
1.1 jfb 63: {
1.127 tobias 64: int ch, flags;
1.91 joris 65: char *arg = ".";
66: struct cvs_recursion cr;
1.1 jfb 67:
1.92 joris 68: flags = CR_RECURSE_DIRS;
1.128 tobias 69: strlcpy(diffargs, cvs_cmdop == CVS_OP_DIFF ? "diff" : "rdiff",
70: sizeof(diffargs));
1.1 jfb 71:
1.127 tobias 72: while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_DIFF ?
73: cvs_cmd_diff.cmd_opts : cvs_cmd_rdiff.cmd_opts)) != -1) {
1.1 jfb 74: switch (ch) {
75: case 'c':
1.2 jfb 76: strlcat(diffargs, " -c", sizeof(diffargs));
1.58 niallo 77: diff_format = D_CONTEXT;
1.1 jfb 78: break;
1.125 tobias 79: case 'f':
80: force_head = 1;
81: break;
82: case 'i':
83: strlcat(diffargs, " -i", sizeof(diffargs));
84: diff_iflag = 1;
85: break;
1.126 tobias 86: case 'k':
87: koptstr = optarg;
88: kflag = rcs_kflag_get(koptstr);
1.132 deraadt 89: if (RCS_KWEXP_INVAL(kflag)) {
1.126 tobias 90: cvs_log(LP_ERR,
91: "invalid RCS keyword expension mode");
1.133 ! tobias 92: fatal("%s", cvs_cmdop == CVS_OP_DIFF ?
! 93: cvs_cmd_diff.cmd_synopsis :
! 94: cvs_cmd_rdiff.cmd_synopsis);
1.126 tobias 95: }
96: break;
1.92 joris 97: case 'l':
98: flags &= ~CR_RECURSE_DIRS;
99: break;
1.33 jfb 100: case 'n':
101: strlcat(diffargs, " -n", sizeof(diffargs));
1.58 niallo 102: diff_format = D_RCSDIFF;
1.33 jfb 103: break;
1.94 joris 104: case 'N':
1.95 joris 105: Nflag = 1;
1.94 joris 106: break;
1.101 joris 107: case 'p':
108: strlcat(diffargs, " -p", sizeof(diffargs));
109: diff_pflag = 1;
1.122 tobias 110: break;
111: case 'R':
112: flags |= CR_RECURSE_DIRS;
1.101 joris 113: break;
1.1 jfb 114: case 'r':
1.97 joris 115: if (rev1 == NULL) {
116: rev1 = optarg;
117: } else if (rev2 == NULL) {
118: rev2 = optarg;
1.23 joris 119: } else {
1.91 joris 120: fatal("no more than 2 revisions/dates can"
121: " be specified");
1.1 jfb 122: }
1.32 joris 123: break;
1.1 jfb 124: case 'u':
1.2 jfb 125: strlcat(diffargs, " -u", sizeof(diffargs));
1.58 niallo 126: diff_format = D_UNIFIED;
1.1 jfb 127: break;
1.126 tobias 128: case 'V':
129: fatal("the -V option is obsolete "
130: "and should not be used");
1.1 jfb 131: default:
1.133 ! tobias 132: fatal("%s", cvs_cmdop == CVS_OP_DIFF ?
! 133: cvs_cmd_diff.cmd_synopsis :
! 134: cvs_cmd_rdiff.cmd_synopsis);
1.36 jfb 135: }
1.13 jfb 136: }
137:
1.91 joris 138: argc -= optind;
139: argv += optind;
1.1 jfb 140:
1.91 joris 141: cr.enterdir = NULL;
142: cr.leavedir = NULL;
1.108 joris 143:
1.125 tobias 144: if (cvs_cmdop == CVS_OP_RDIFF) {
145: if (rev1 == NULL)
146: fatal("must specify at least one revision/date!");
147:
148: if (!diff_format) {
149: strlcat(diffargs, " -c", sizeof(diffargs));
150: diff_format = D_CONTEXT;
151: }
152:
153: flags |= CR_REPO;
154: }
155:
1.108 joris 156: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.110 joris 157: cvs_client_connect_to_server();
1.108 joris 158: cr.fileproc = cvs_client_sendfile;
159:
160: if (!(flags & CR_RECURSE_DIRS))
161: cvs_client_send_request("Argument -l");
162:
1.126 tobias 163: if (kflag)
164: cvs_client_send_request("Argument -k%s", koptstr);
165:
1.108 joris 166: switch (diff_format) {
167: case D_CONTEXT:
168: cvs_client_send_request("Argument -c");
169: break;
170: case D_RCSDIFF:
171: cvs_client_send_request("Argument -n");
172: break;
173: case D_UNIFIED:
174: cvs_client_send_request("Argument -u");
175: break;
176: default:
177: break;
178: }
179:
180: if (Nflag == 1)
181: cvs_client_send_request("Argument -N");
182:
183: if (diff_pflag == 1)
184: cvs_client_send_request("Argument -p");
185:
186: if (rev1 != NULL)
187: cvs_client_send_request("Argument -r%s", rev1);
188: if (rev2 != NULL)
189: cvs_client_send_request("Argument -r%s", rev2);
190: } else {
1.125 tobias 191: if (cvs_cmdop == CVS_OP_RDIFF &&
192: chdir(current_cvsroot->cr_dir) == -1)
193: fatal("cvs_diff: %s", strerror(errno));
194:
1.108 joris 195: cr.fileproc = cvs_diff_local;
196: }
197:
1.92 joris 198: cr.flags = flags;
1.7 jfb 199:
1.97 joris 200: diff_rev1 = diff_rev2 = NULL;
201:
1.125 tobias 202: if (cvs_cmdop == CVS_OP_DIFF ||
203: current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
204: if (argc > 0)
205: cvs_file_run(argc, argv, &cr);
206: else
207: cvs_file_run(1, &arg, &cr);
208: }
1.108 joris 209:
210: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
211: cvs_client_send_files(argv, argc);
212: cvs_client_senddir(".");
1.125 tobias 213:
214: cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ?
215: "rdiff" : "diff");
216:
1.108 joris 217: cvs_client_get_responses();
218: }
1.36 jfb 219:
220: return (0);
221: }
222:
1.91 joris 223: void
224: cvs_diff_local(struct cvs_file *cf)
1.36 jfb 225: {
1.91 joris 226: RCSNUM *r1;
1.114 joris 227: BUF *b1;
1.131 joris 228: int fd1, fd2;
1.91 joris 229: struct stat st;
1.69 niallo 230: struct timeval tv[2], tv2[2];
1.120 xsa 231: char rbuf[CVS_REV_BUFSZ], *p1, *p2;
1.111 xsa 232:
233: r1 = NULL;
1.114 joris 234: b1 = NULL;
1.69 niallo 235:
1.91 joris 236: cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path);
1.1 jfb 237:
1.91 joris 238: if (cf->file_type == CVS_DIR) {
1.46 xsa 239: if (verbosity > 1)
1.91 joris 240: cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path);
1.1 jfb 241: return;
242: }
243:
1.121 joris 244: cvs_file_classify(cf, cvs_directory_tag);
1.1 jfb 245:
1.91 joris 246: if (cf->file_status == FILE_LOST) {
247: cvs_log(LP_ERR, "cannot find file %s", cf->file_path);
1.1 jfb 248: return;
1.91 joris 249: } else if (cf->file_status == FILE_UNKNOWN) {
250: cvs_log(LP_ERR, "I know nothing about %s", cf->file_path);
1.1 jfb 251: return;
1.95 joris 252: } else if (cf->file_status == FILE_ADDED && Nflag == 0) {
1.94 joris 253: cvs_log(LP_ERR, "%s is a new entry, no comparison available",
254: cf->file_path);
1.1 jfb 255: return;
1.95 joris 256: } else if (cf->file_status == FILE_REMOVED && Nflag == 0) {
257: cvs_log(LP_ERR, "%s was removed, no comparison available",
258: cf->file_path);
259: return;
1.99 joris 260: } else if (cf->file_status == FILE_UPTODATE && rev1 == NULL &&
261: rev2 == NULL) {
1.94 joris 262: return;
263: }
1.126 tobias 264:
265: if (kflag)
266: rcs_kwexp_set(cf->file_rcs, kflag);
1.97 joris 267:
268: if (rev1 != NULL)
1.125 tobias 269: if ((diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs)) ==
270: NULL) {
271: if (cvs_cmdop == CVS_OP_DIFF) {
272: cvs_log(LP_ERR, "tag %s is not in file %s",
273: rev1, cf->file_path);
274: return;
275: }
1.132 deraadt 276: if (force_head) {
1.125 tobias 277: /* -f is not allowed for unknown symbols */
278: diff_rev1 = rcsnum_parse(rev1);
279: if (diff_rev1 == NULL)
280: fatal("no such tag %s", rev1);
281: rcsnum_free(diff_rev1);
282:
283: diff_rev1 = rcsnum_alloc();
284: rcsnum_cpy(cf->file_rcs->rf_head, diff_rev1, 0);
285: }
286: }
1.121 joris 287:
1.97 joris 288: if (rev2 != NULL)
1.123 tobias 289: if ((diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs)) ==
290: NULL) {
1.125 tobias 291: if (cvs_cmdop == CVS_OP_DIFF) {
292: rcsnum_free(diff_rev1);
293: cvs_log(LP_ERR, "tag %s is not in file %s",
294: rev2, cf->file_path);
295: return;
296: }
1.132 deraadt 297: if (force_head) {
1.125 tobias 298: /* -f is not allowed for unknown symbols */
299: diff_rev2 = rcsnum_parse(rev2);
300: if (diff_rev2 == NULL)
301: fatal("no such tag %s", rev2);
302: rcsnum_free(diff_rev2);
303:
304: diff_rev2 = rcsnum_alloc();
305: rcsnum_cpy(cf->file_rcs->rf_head, diff_rev2, 0);
306: }
1.123 tobias 307: }
1.1 jfb 308:
1.125 tobias 309: if (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL && diff_rev2 == NULL)
310: return;
311:
1.91 joris 312: diff_file = cf->file_path;
1.125 tobias 313:
314: cvs_printf("Index: %s\n", cf->file_path);
315: if (cvs_cmdop == CVS_OP_DIFF)
316: cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, cf->file_rpath);
1.1 jfb 317:
1.114 joris 318: (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
319: (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
320:
1.94 joris 321: if (cf->file_status != FILE_ADDED) {
322: if (diff_rev1 != NULL)
323: r1 = diff_rev1;
1.125 tobias 324: else if (cf->file_ent != NULL)
325: r1 = cf->file_ent->ce_rev;
1.94 joris 326: else
1.125 tobias 327: r1 = NULL;
1.1 jfb 328:
1.94 joris 329: diff_rev1 = r1;
1.91 joris 330:
1.125 tobias 331: if (diff_rev1 != NULL) {
332: (void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
1.114 joris 333:
1.125 tobias 334: tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1);
335: tv[0].tv_usec = 0;
336: tv[1] = tv[0];
337:
338: if (cvs_cmdop == CVS_OP_DIFF)
339: cvs_printf("retrieving revision %s\n", rbuf);
1.131 joris 340: fd1 = rcs_rev_write_stmp(cf->file_rcs, r1, p1, 0);
341: if (futimes(fd1, tv) == -1)
1.125 tobias 342: fatal("cvs_diff_local: utimes failed");
343: }
1.94 joris 344: }
1.91 joris 345:
1.95 joris 346: if (diff_rev2 != NULL && cf->file_status != FILE_ADDED &&
347: cf->file_status != FILE_REMOVED) {
1.125 tobias 348: (void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf));
1.91 joris 349:
350: tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2);
351: tv2[0].tv_usec = 0;
352: tv2[1] = tv2[0];
1.114 joris 353:
1.125 tobias 354: if (cvs_cmdop == CVS_OP_DIFF)
355: cvs_printf("retrieving revision %s\n", rbuf);
1.131 joris 356: fd2 = rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0);
357: if (futimes(fd2, tv2) == -1)
1.125 tobias 358: fatal("cvs_diff_local: utimes failed");
1.95 joris 359: } else if (cf->file_status != FILE_REMOVED) {
1.125 tobias 360: if (cvs_cmdop == CVS_OP_RDIFF || (cvs_server_active == 1 &&
361: cf->file_status != FILE_MODIFIED)) {
362: if (diff_rev2 != NULL) {
363: tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs,
364: cf->file_rcsrev);
365: tv2[0].tv_usec = 0;
366: tv2[1] = tv2[0];
367:
1.131 joris 368: fd2 = rcs_rev_write_stmp(cf->file_rcs,
1.125 tobias 369: cf->file_rcsrev, p2, 0);
1.131 joris 370: if (futimes(fd2, tv2) == -1)
1.125 tobias 371: fatal("cvs_diff_local: utimes failed");
372: }
1.121 joris 373: } else {
374: if (fstat(cf->fd, &st) == -1)
375: fatal("fstat failed %s", strerror(errno));
1.129 tobias 376: b1 = cvs_buf_load_fd(cf->fd);
1.121 joris 377:
378: tv2[0].tv_sec = st.st_mtime;
379: tv2[0].tv_usec = 0;
380: tv2[1] = tv2[0];
1.91 joris 381:
1.131 joris 382: fd2 = cvs_buf_write_stmp(b1, p2, tv2);
1.121 joris 383: cvs_buf_free(b1);
384: }
1.17 jfb 385: }
386:
1.125 tobias 387: if (cvs_cmdop == CVS_OP_DIFF) {
388: cvs_printf("%s", diffargs);
389:
390: if (cf->file_status != FILE_ADDED) {
391: (void)rcsnum_tostr(r1, rbuf, sizeof(rbuf));
392: cvs_printf(" -r%s", rbuf);
393:
394: if (diff_rev2 != NULL) {
395: (void)rcsnum_tostr(diff_rev2, rbuf,
396: sizeof(rbuf));
397: cvs_printf(" -r%s", rbuf);
398: }
399: }
1.1 jfb 400:
1.125 tobias 401: if (diff_rev2 == NULL)
1.130 tobias 402: cvs_printf(" %s", cf->file_name);
403: cvs_printf("\n");
1.125 tobias 404: } else {
405: cvs_printf("diff ");
406: switch (diff_format) {
407: case D_CONTEXT:
408: cvs_printf("-c ");
409: break;
410: case D_RCSDIFF:
411: cvs_printf("-n ");
412: break;
413: case D_UNIFIED:
414: cvs_printf("-u ");
415: break;
416: default:
417: break;
418: }
419: if (diff_rev1 == NULL) {
420: cvs_printf("%s ", CVS_PATH_DEVNULL);
421: } else {
422: (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf));
423: cvs_printf("%s:%s ", cf->file_path, rbuf);
424: }
1.1 jfb 425:
1.125 tobias 426: if (diff_rev2 == NULL) {
427: cvs_printf("%s:removed\n", cf->file_path);
428: } else {
429: (void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 :
430: cf->file_rcs->rf_head, rbuf, sizeof(rbuf));
431: cvs_printf("%s:%s\n", cf->file_path, rbuf);
1.94 joris 432: }
1.1 jfb 433: }
434:
1.125 tobias 435: if (cf->file_status == FILE_ADDED ||
436: (cvs_cmdop == CVS_OP_RDIFF && diff_rev1 == NULL)) {
1.114 joris 437: xfree(p1);
1.131 joris 438: close(fd1);
1.111 xsa 439: (void)xasprintf(&p1, "%s", CVS_PATH_DEVNULL);
1.131 joris 440: if ((fd1 = open(p1, O_RDONLY)) == -1)
441: fatal("cvs_diff_local: cannot open %s",
442: CVS_PATH_DEVNULL);
1.125 tobias 443: } else if (cf->file_status == FILE_REMOVED ||
444: (cvs_cmdop == CVS_OP_RDIFF && diff_rev2 == NULL)) {
1.114 joris 445: xfree(p2);
1.131 joris 446: close(fd2);
1.111 xsa 447: (void)xasprintf(&p2, "%s", CVS_PATH_DEVNULL);
1.131 joris 448: if ((fd1 = open(p2, O_RDONLY)) == -1)
449: fatal("cvs_diff_local: cannot open %s",
450: CVS_PATH_DEVNULL);
1.114 joris 451: }
1.111 xsa 452:
1.131 joris 453: if (cvs_diffreg(p1, p2, fd1, fd2, NULL) == D_ERROR)
1.111 xsa 454: fatal("cvs_diff_local: failed to get RCS patch");
1.131 joris 455:
456: close(fd1);
457: close(fd2);
1.58 niallo 458:
1.91 joris 459: cvs_worklist_run(&temp_files, cvs_worklist_unlink);
1.111 xsa 460:
461: if (p1 != NULL)
462: xfree(p1);
463: if (p2 != NULL)
464: xfree(p2);
1.101 joris 465:
1.125 tobias 466: if (diff_rev1 != NULL && diff_rev1 != cf->file_rcs->rf_head &&
467: (cf->file_ent != NULL && diff_rev1 != cf->file_ent->ce_rev))
1.101 joris 468: rcsnum_free(diff_rev1);
1.125 tobias 469: if (diff_rev2 != NULL)
1.101 joris 470: rcsnum_free(diff_rev2);
1.94 joris 471:
472: diff_rev1 = diff_rev2 = NULL;
1.1 jfb 473: }