1 /* $OpenBSD: diff.c,v 1.118 2007/02/22 06:42:09 otto Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * 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. 16 */ 17 18 #include <sys/stat.h> 19 20 #include <errno.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include "cvs.h" 25 #include "diff.h" 26 #include "remote.h" 27 28 void cvs_diff_local(struct cvs_file *); 29 30 static int Nflag = 0; 31 static char *rev1 = NULL; 32 static char *rev2 = NULL; 33 34 struct cvs_cmd cvs_cmd_diff = { 35 CVS_OP_DIFF, 0, "diff", 36 { "di", "dif" }, 37 "Show differences between revisions", 38 "[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] " 39 "[-k mode] [file ...]", 40 "cD:iklNnpr:Ru", 41 NULL, 42 cvs_diff 43 }; 44 45 int 46 cvs_diff(int argc, char **argv) 47 { 48 int ch; 49 char *arg = "."; 50 int flags; 51 struct cvs_recursion cr; 52 53 flags = CR_RECURSE_DIRS; 54 strlcpy(diffargs, argv[0], sizeof(diffargs)); 55 56 while ((ch = getopt(argc, argv, cvs_cmd_diff.cmd_opts)) != -1) { 57 switch (ch) { 58 case 'c': 59 strlcat(diffargs, " -c", sizeof(diffargs)); 60 diff_format = D_CONTEXT; 61 break; 62 case 'l': 63 flags &= ~CR_RECURSE_DIRS; 64 break; 65 case 'n': 66 strlcat(diffargs, " -n", sizeof(diffargs)); 67 diff_format = D_RCSDIFF; 68 break; 69 case 'N': 70 strlcat(diffargs, " -N", sizeof(diffargs)); 71 Nflag = 1; 72 break; 73 case 'p': 74 strlcat(diffargs, " -p", sizeof(diffargs)); 75 diff_pflag = 1; 76 break; 77 case 'r': 78 if (rev1 == NULL) { 79 rev1 = optarg; 80 } else if (rev2 == NULL) { 81 rev2 = optarg; 82 } else { 83 fatal("no more than 2 revisions/dates can" 84 " be specified"); 85 } 86 break; 87 case 'u': 88 strlcat(diffargs, " -u", sizeof(diffargs)); 89 diff_format = D_UNIFIED; 90 break; 91 default: 92 fatal("%s", cvs_cmd_diff.cmd_synopsis); 93 } 94 } 95 96 argc -= optind; 97 argv += optind; 98 99 cr.enterdir = NULL; 100 cr.leavedir = NULL; 101 102 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 103 cvs_client_connect_to_server(); 104 cr.fileproc = cvs_client_sendfile; 105 106 if (!(flags & CR_RECURSE_DIRS)) 107 cvs_client_send_request("Argument -l"); 108 109 switch (diff_format) { 110 case D_CONTEXT: 111 cvs_client_send_request("Argument -c"); 112 break; 113 case D_RCSDIFF: 114 cvs_client_send_request("Argument -n"); 115 break; 116 case D_UNIFIED: 117 cvs_client_send_request("Argument -u"); 118 break; 119 default: 120 break; 121 } 122 123 if (Nflag == 1) 124 cvs_client_send_request("Argument -N"); 125 126 if (diff_pflag == 1) 127 cvs_client_send_request("Argument -p"); 128 129 if (rev1 != NULL) 130 cvs_client_send_request("Argument -r%s", rev1); 131 if (rev2 != NULL) 132 cvs_client_send_request("Argument -r%s", rev2); 133 } else { 134 cr.fileproc = cvs_diff_local; 135 } 136 137 cr.flags = flags; 138 139 diff_rev1 = diff_rev2 = NULL; 140 141 if (argc > 0) 142 cvs_file_run(argc, argv, &cr); 143 else 144 cvs_file_run(1, &arg, &cr); 145 146 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 147 cvs_client_send_files(argv, argc); 148 cvs_client_senddir("."); 149 cvs_client_send_request("diff"); 150 cvs_client_get_responses(); 151 } 152 153 return (0); 154 } 155 156 void 157 cvs_diff_local(struct cvs_file *cf) 158 { 159 RCSNUM *r1; 160 BUF *b1; 161 struct stat st; 162 struct timeval tv[2], tv2[2]; 163 char rbuf[16], *p1, *p2; 164 165 r1 = NULL; 166 b1 = NULL; 167 168 cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); 169 170 if (cf->file_type == CVS_DIR) { 171 if (verbosity > 1) 172 cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path); 173 return; 174 } 175 176 cvs_file_classify(cf, NULL); 177 178 if (cf->file_status == FILE_LOST) { 179 cvs_log(LP_ERR, "cannot find file %s", cf->file_path); 180 return; 181 } else if (cf->file_status == FILE_UNKNOWN) { 182 cvs_log(LP_ERR, "I know nothing about %s", cf->file_path); 183 return; 184 } else if (cf->file_status == FILE_ADDED && Nflag == 0) { 185 cvs_log(LP_ERR, "%s is a new entry, no comparison available", 186 cf->file_path); 187 return; 188 } else if (cf->file_status == FILE_REMOVED && Nflag == 0) { 189 cvs_log(LP_ERR, "%s was removed, no comparison available", 190 cf->file_path); 191 return; 192 } else if (cf->file_status == FILE_UPTODATE && rev1 == NULL && 193 rev2 == NULL) { 194 return; 195 } 196 197 if (rev1 != NULL) 198 if ((diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs)) == NULL) 199 fatal("cvs_diff_local: could not translate tag `%s'", rev1); 200 if (rev2 != NULL) 201 if ((diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs)) == NULL) 202 fatal("cvs_diff_local: could not translate tag `%s'", rev2); 203 204 diff_file = cf->file_path; 205 cvs_printf("Index: %s\n%s\nRCS file: %s\n", cf->file_path, 206 RCS_DIFF_DIV, cf->file_rpath); 207 208 (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 209 (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 210 211 if (cf->file_status != FILE_ADDED) { 212 if (diff_rev1 != NULL) 213 r1 = diff_rev1; 214 else 215 r1 = cf->file_ent->ce_rev; 216 217 diff_rev1 = r1; 218 rcsnum_tostr(r1, rbuf , sizeof(rbuf)); 219 220 tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1); 221 tv[0].tv_usec = 0; 222 tv[1] = tv[0]; 223 224 printf("Retrieving revision %s\n", rbuf); 225 rcs_rev_write_stmp(cf->file_rcs, r1, p1, 0); 226 } 227 228 if (diff_rev2 != NULL && cf->file_status != FILE_ADDED && 229 cf->file_status != FILE_REMOVED) { 230 rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); 231 232 tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); 233 tv2[0].tv_usec = 0; 234 tv2[1] = tv2[0]; 235 236 printf("Retrieving revision %s\n", rbuf); 237 rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0); 238 } else if (cf->file_status != FILE_REMOVED) { 239 if (fstat(cf->fd, &st) == -1) 240 fatal("fstat failed %s", strerror(errno)); 241 if ((b1 = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL) 242 fatal("failed to load %s", cf->file_path); 243 244 tv2[0].tv_sec = st.st_mtime; 245 tv2[0].tv_usec = 0; 246 tv2[1] = tv2[0]; 247 248 cvs_buf_write_stmp(b1, p2, tv2); 249 cvs_buf_free(b1); 250 } 251 252 cvs_printf("%s", diffargs); 253 254 if (cf->file_status != FILE_ADDED) { 255 rcsnum_tostr(r1, rbuf, sizeof(rbuf)); 256 cvs_printf(" -r%s", rbuf); 257 258 if (diff_rev2 != NULL) { 259 rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); 260 cvs_printf(" -r%s", rbuf); 261 } 262 } 263 264 cvs_printf(" %s\n", cf->file_path); 265 266 if (cf->file_status == FILE_ADDED) { 267 xfree(p1); 268 (void)xasprintf(&p1, "%s", CVS_PATH_DEVNULL); 269 } else if (cf->file_status == FILE_REMOVED) { 270 xfree(p2); 271 (void)xasprintf(&p2, "%s", CVS_PATH_DEVNULL); 272 } 273 274 if (cvs_diffreg(p1, p2, NULL) == D_ERROR) 275 fatal("cvs_diff_local: failed to get RCS patch"); 276 277 cvs_worklist_run(&temp_files, cvs_worklist_unlink); 278 279 if (p1 != NULL) 280 xfree(p1); 281 if (p2 != NULL) 282 xfree(p2); 283 284 if (diff_rev1 != NULL && diff_rev1 != cf->file_ent->ce_rev) 285 rcsnum_free(diff_rev1); 286 if (diff_rev2 != NULL && diff_rev2 != cf->file_rcsrev) 287 rcsnum_free(diff_rev2); 288 289 diff_rev1 = diff_rev2 = NULL; 290 } 291