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