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