1 /* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 * The functions in this file provide an interface for performing 14 * operations directly on RCS files. 15 */ 16 17 #include "cvs.h" 18 #include <stdio.h> 19 #include "diffrun.h" 20 #include "quotearg.h" 21 22 /* This file, rcs.h, and rcs.c, together sometimes known as the "RCS 23 library", are intended to define our interface to RCS files. 24 25 Whether there will also be a version of RCS which uses this 26 library, or whether the library will be packaged for uses beyond 27 CVS or RCS (many people would like such a thing) is an open 28 question. Some considerations: 29 30 1. An RCS library for CVS must have the capabilities of the 31 existing CVS code which accesses RCS files. In particular, simple 32 approaches will often be slow. 33 34 2. An RCS library should not use code from the current RCS 35 (5.7 and its ancestors). The code has many problems. Too few 36 comments, too many layers of abstraction, too many global variables 37 (the correct number for a library is zero), too much intricately 38 interwoven functionality, and too many clever hacks. Paul Eggert, 39 the current RCS maintainer, agrees. 40 41 3. More work needs to be done in terms of separating out the RCS 42 library from the rest of CVS (for example, cvs_output should be 43 replaced by a callback, and the declarations should be centralized 44 into rcs.h, and probably other such cleanups). 45 46 4. To be useful for RCS and perhaps for other uses, the library 47 may need features beyond those needed by CVS. 48 49 5. Any changes to the RCS file format *must* be compatible. Many, 50 many tools (not just CVS and RCS) can at least import this format. 51 RCS and CVS must preserve the current ability to import/export it 52 (preferably improved--magic branches are currently a roadblock). 53 See doc/RCSFILES in the CVS distribution for documentation of this 54 file format. 55 56 On a related note, see the comments at diff_exec, later in this file, 57 for more on the diff library. */ 58 59 static void RCS_output_diff_options (int, char * const *, const char *, 60 const char *, const char *); 61 62 63 /* Stuff to deal with passing arguments the way libdiff.a wants to deal 64 with them. This is a crufty interface; there is no good reason for it 65 to resemble a command line rather than something closer to "struct 66 log_data" in log.c. */ 67 68 /* First call call_diff_setup to setup any initial arguments. The 69 argument will be parsed into whitespace separated words and added 70 to the global call_diff_argv list. 71 72 Then, optionally, call call_diff_add_arg for each additional argument 73 that you'd like to pass to the diff library. 74 75 Finally, call call_diff or call_diff3 to produce the diffs. */ 76 77 static char **call_diff_argv; 78 static int call_diff_argc; 79 static size_t call_diff_arg_allocated; 80 81 static int call_diff (const char *out); 82 static int call_diff3 (char *out); 83 84 static void call_diff_write_output (const char *, size_t); 85 static void call_diff_flush_output (void); 86 static void call_diff_write_stdout (const char *); 87 static void call_diff_error (const char *, const char *, const char *); 88 89 90 91 /* VARARGS */ 92 static void 93 call_diff_add_arg (const char *s) 94 { 95 TRACE (TRACE_DATA, "call_diff_add_arg (%s)", s); 96 run_add_arg_p (&call_diff_argc, &call_diff_arg_allocated, &call_diff_argv, 97 s); 98 } 99 100 101 102 static void 103 call_diff_setup (const char *prog, int argc, char * const *argv) 104 { 105 int i; 106 107 /* clean out any malloc'ed values from call_diff_argv */ 108 run_arg_free_p (call_diff_argc, call_diff_argv); 109 call_diff_argc = 0; 110 111 /* put each word into call_diff_argv, allocating it as we go */ 112 call_diff_add_arg (prog); 113 for (i = 0; i < argc; i++) 114 call_diff_add_arg (argv[i]); 115 } 116 117 118 119 /* Callback function for the diff library to write data to the output 120 file. This is used when we are producing output to stdout. */ 121 122 static void 123 call_diff_write_output (const char *text, size_t len) 124 { 125 if (len > 0) 126 cvs_output (text, len); 127 } 128 129 /* Call back function for the diff library to flush the output file. 130 This is used when we are producing output to stdout. */ 131 132 static void 133 call_diff_flush_output (void) 134 { 135 cvs_flushout (); 136 } 137 138 /* Call back function for the diff library to write to stdout. */ 139 140 static void 141 call_diff_write_stdout (const char *text) 142 { 143 cvs_output (text, 0); 144 } 145 146 /* Call back function for the diff library to write to stderr. */ 147 148 static void 149 call_diff_error (const char *format, const char *a1, const char *a2) 150 { 151 /* FIXME: Should we somehow indicate that this error is coming from 152 the diff library? */ 153 error (0, 0, format, a1, a2); 154 } 155 156 /* This set of callback functions is used if we are sending the diff 157 to stdout. */ 158 159 static struct diff_callbacks call_diff_stdout_callbacks = 160 { 161 call_diff_write_output, 162 call_diff_flush_output, 163 call_diff_write_stdout, 164 call_diff_error 165 }; 166 167 /* This set of callback functions is used if we are sending the diff 168 to a file. */ 169 170 static struct diff_callbacks call_diff_file_callbacks = 171 { 172 NULL, 173 NULL, 174 call_diff_write_stdout, 175 call_diff_error 176 }; 177 178 179 180 static int 181 call_diff (const char *out) 182 { 183 call_diff_add_arg (NULL); 184 185 if (out == RUN_TTY) 186 return diff_run( call_diff_argc, call_diff_argv, NULL, 187 &call_diff_stdout_callbacks ); 188 else 189 return diff_run( call_diff_argc, call_diff_argv, out, 190 &call_diff_file_callbacks ); 191 } 192 193 194 195 static int 196 call_diff3 (char *out) 197 { 198 if (out == RUN_TTY) 199 return diff3_run (call_diff_argc, call_diff_argv, NULL, 200 &call_diff_stdout_callbacks); 201 else 202 return diff3_run (call_diff_argc, call_diff_argv, out, 203 &call_diff_file_callbacks); 204 } 205 206 207 208 /* Merge revisions REV1 and REV2. */ 209 210 int 211 RCS_merge (RCSNode *rcs, const char *path, const char *workfile, 212 const char *options, const char *rev1, const char *rev2) 213 { 214 char *xrev1, *xrev2; 215 char *tmp1, *tmp2; 216 char *diffout = NULL; 217 int retval; 218 219 if (options != NULL && options[0] != '\0') 220 assert (options[0] == '-' && options[1] == 'k'); 221 222 cvs_output ("RCS file: ", 0); 223 cvs_output (rcs->print_path, 0); 224 cvs_output ("\n", 1); 225 226 /* Calculate numeric revision numbers from rev1 and rev2 (may be 227 symbolic). 228 FIXME - No they can't. Both calls to RCS_merge are passing in 229 numeric revisions. */ 230 xrev1 = RCS_gettag (rcs, rev1, 0, NULL); 231 xrev2 = RCS_gettag (rcs, rev2, 0, NULL); 232 assert (xrev1 && xrev2); 233 234 /* Check out chosen revisions. The error message when RCS_checkout 235 fails is not very informative -- it is taken verbatim from RCS 5.7, 236 and relies on RCS_checkout saying something intelligent upon failure. */ 237 cvs_output ("retrieving revision ", 0); 238 cvs_output (xrev1, 0); 239 cvs_output ("\n", 1); 240 241 tmp1 = cvs_temp_name(); 242 if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, NULL, NULL)) 243 { 244 cvs_outerr ("rcsmerge: co failed\n", 0); 245 exit (EXIT_FAILURE); 246 } 247 248 cvs_output ("retrieving revision ", 0); 249 cvs_output (xrev2, 0); 250 cvs_output ("\n", 1); 251 252 tmp2 = cvs_temp_name(); 253 if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, NULL, NULL)) 254 { 255 cvs_outerr ("rcsmerge: co failed\n", 0); 256 exit (EXIT_FAILURE); 257 } 258 259 /* Merge changes. */ 260 cvs_output ("Merging differences between ", 0); 261 cvs_output (xrev1, 0); 262 cvs_output (" and ", 0); 263 cvs_output (xrev2, 0); 264 cvs_output (" into ", 0); 265 cvs_output (workfile, 0); 266 cvs_output ("\n", 1); 267 268 /* Remember that the first word in the `call_diff_setup' string is used now 269 only for diagnostic messages -- CVS no longer forks to run diff3. */ 270 diffout = cvs_temp_name(); 271 call_diff_setup ("diff3", 0, NULL); 272 call_diff_add_arg ("-E"); 273 call_diff_add_arg ("-am"); 274 275 call_diff_add_arg ("-L"); 276 call_diff_add_arg (workfile); 277 call_diff_add_arg ("-L"); 278 call_diff_add_arg (xrev1); 279 call_diff_add_arg ("-L"); 280 call_diff_add_arg (xrev2); 281 282 call_diff_add_arg ("--"); 283 call_diff_add_arg (workfile); 284 call_diff_add_arg (tmp1); 285 call_diff_add_arg (tmp2); 286 287 retval = call_diff3 (diffout); 288 289 if (retval == 1) 290 cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0); 291 else if (retval == 2) 292 exit (EXIT_FAILURE); 293 294 if (diffout) 295 copy_file (diffout, workfile); 296 297 /* Clean up. */ 298 { 299 int save_noexec = noexec; 300 noexec = 0; 301 if (unlink_file (tmp1) < 0) 302 { 303 if (!existence_error (errno)) 304 error (0, errno, "cannot remove temp file %s", tmp1); 305 } 306 free (tmp1); 307 if (unlink_file (tmp2) < 0) 308 { 309 if (!existence_error (errno)) 310 error (0, errno, "cannot remove temp file %s", tmp2); 311 } 312 free (tmp2); 313 if (diffout) 314 { 315 if (unlink_file (diffout) < 0) 316 { 317 if (!existence_error (errno)) 318 error (0, errno, "cannot remove temp file %s", diffout); 319 } 320 free (diffout); 321 } 322 free (xrev1); 323 free (xrev2); 324 noexec = save_noexec; 325 } 326 327 return retval; 328 } 329 330 /* Diff revisions and/or files. OPTS controls the format of the diff 331 (it contains options such as "-w -c", &c), or "" for the default. 332 OPTIONS controls keyword expansion, as a string starting with "-k", 333 or "" to use the default. REV1 is the first revision to compare 334 against; it must be non-NULL. If REV2 is non-NULL, compare REV1 335 and REV2; if REV2 is NULL compare REV1 with the file in the working 336 directory, whose name is WORKFILE. LABEL1 and LABEL2 are default 337 file labels, and (if non-NULL) should be added as -L options 338 to diff. Output goes to stdout. 339 340 Return value is 0 for success, -1 for a failure which set errno, 341 or positive for a failure which printed a message on stderr. 342 343 This used to exec rcsdiff, but now calls RCS_checkout and diff_exec. 344 345 An issue is what timezone is used for the dates which appear in the 346 diff output. rcsdiff uses the -z flag, which is not presently 347 processed by CVS diff, but I'm not sure exactly how hard to worry 348 about this--any such features are undocumented in the context of 349 CVS, and I'm not sure how important to users. */ 350 int 351 RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc, 352 char * const *diff_argv, const char *options, 353 const char *rev1, const char *rev1_cache, const char *rev2, 354 const char *label1, const char *label2, const char *workfile) 355 { 356 char *tmpfile1 = NULL; 357 char *tmpfile2 = NULL; 358 const char *use_file1, *use_file2; 359 int status, retval; 360 361 362 cvs_output ("\ 363 ===================================================================\n\ 364 RCS file: ", 0); 365 cvs_output (rcsfile->print_path, 0); 366 cvs_output ("\n", 1); 367 368 /* Historically, `cvs diff' has expanded the $Name keyword to the 369 empty string when checking out revisions. This is an accident, 370 but no one has considered the issue thoroughly enough to determine 371 what the best behavior is. Passing NULL for the `nametag' argument 372 preserves the existing behavior. */ 373 374 cvs_output ("retrieving revision ", 0); 375 cvs_output (rev1, 0); 376 cvs_output ("\n", 1); 377 378 if (rev1_cache != NULL) 379 use_file1 = rev1_cache; 380 else 381 { 382 tmpfile1 = cvs_temp_name(); 383 status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, 384 NULL, NULL); 385 if (status > 0) 386 { 387 retval = status; 388 goto error_return; 389 } 390 else if (status < 0) 391 { 392 error( 0, errno, 393 "cannot check out revision %s of %s", rev1, rcsfile->path ); 394 retval = 1; 395 goto error_return; 396 } 397 use_file1 = tmpfile1; 398 } 399 400 if (rev2 == NULL) 401 { 402 assert (workfile != NULL); 403 use_file2 = workfile; 404 } 405 else 406 { 407 tmpfile2 = cvs_temp_name (); 408 cvs_output ("retrieving revision ", 0); 409 cvs_output (rev2, 0); 410 cvs_output ("\n", 1); 411 status = RCS_checkout (rcsfile, NULL, rev2, NULL, options, 412 tmpfile2, NULL, NULL); 413 if (status > 0) 414 { 415 retval = status; 416 goto error_return; 417 } 418 else if (status < 0) 419 { 420 error (0, errno, 421 "cannot check out revision %s of %s", rev2, rcsfile->path); 422 return 1; 423 } 424 use_file2 = tmpfile2; 425 } 426 427 RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile); 428 status = diff_exec (use_file1, use_file2, label1, label2, 429 diff_argc, diff_argv, RUN_TTY); 430 if (status >= 0) 431 { 432 retval = status; 433 goto error_return; 434 } 435 else if (status < 0) 436 { 437 error (0, errno, 438 "cannot diff %s and %s", use_file1, use_file2); 439 retval = 1; 440 goto error_return; 441 } 442 443 error_return: 444 { 445 /* Call CVS_UNLINK() below rather than unlink_file to avoid the check 446 * for noexec. 447 */ 448 if( tmpfile1 != NULL ) 449 { 450 if( CVS_UNLINK( tmpfile1 ) < 0 ) 451 { 452 if( !existence_error( errno ) ) 453 error( 0, errno, "cannot remove temp file %s", tmpfile1 ); 454 } 455 free( tmpfile1 ); 456 } 457 if( tmpfile2 != NULL ) 458 { 459 if( CVS_UNLINK( tmpfile2 ) < 0 ) 460 { 461 if( !existence_error( errno ) ) 462 error( 0, errno, "cannot remove temp file %s", tmpfile2 ); 463 } 464 free (tmpfile2); 465 } 466 } 467 468 return retval; 469 } 470 471 472 473 /* Show differences between two files. This is the start of a diff library. 474 475 Some issues: 476 477 * Should option parsing be part of the library or the caller? The 478 former allows the library to add options without changing the callers, 479 but it causes various problems. One is that something like --brief really 480 wants special handling in CVS, and probably the caller should retain 481 some flexibility in this area. Another is online help (the library could 482 have some feature for providing help, but how does that interact with 483 the help provided by the caller directly?). Another is that as things 484 stand currently, there is no separate namespace for diff options versus 485 "cvs diff" options like -l (that is, if the library adds an option which 486 conflicts with a CVS option, it is trouble). 487 488 * This isn't required for a first-cut diff library, but if there 489 would be a way for the caller to specify the timestamps that appear 490 in the diffs (rather than the library getting them from the files), 491 that would clean up the kludgy utime() calls in patch.c. 492 493 Show differences between FILE1 and FILE2. Either one can be 494 DEVNULL to indicate a nonexistent file (same as an empty file 495 currently, I suspect, but that may be an issue in and of itself). 496 OPTIONS is a list of diff options, or "" if none. At a minimum, 497 CVS expects that -c (update.c, patch.c) and -n (update.c) will be 498 supported. Other options, like -u, --speed-large-files, &c, will 499 be specified if the user specified them. 500 501 OUT is a filename to send the diffs to, or RUN_TTY to send them to 502 stdout. Error messages go to stderr. Return value is 0 for 503 success, -1 for a failure which set errno, 1 for success (and some 504 differences were found), or >1 for a failure which printed a 505 message on stderr. */ 506 507 int 508 diff_exec (const char *file1, const char *file2, const char *label1, 509 const char *label2, int dargc, char * const *dargv, 510 const char *out) 511 { 512 TRACE (TRACE_FUNCTION, "diff_exec (%s, %s, %s, %s, %s)", 513 file1, file2, label1, label2, out); 514 515 #ifdef PRESERVE_PERMISSIONS_SUPPORT 516 /* If either file1 or file2 are special files, pretend they are 517 /dev/null. Reason: suppose a file that represents a block 518 special device in one revision becomes a regular file. CVS 519 must find the `difference' between these files, but a special 520 file contains no data useful for calculating this metric. The 521 safe thing to do is to treat the special file as an empty file, 522 thus recording the regular file's full contents. Doing so will 523 create extremely large deltas at the point of transition 524 between device files and regular files, but this is probably 525 very rare anyway. 526 527 There may be ways around this, but I think they are fraught 528 with danger. -twp */ 529 530 if (preserve_perms && 531 strcmp (file1, DEVNULL) != 0 && 532 strcmp (file2, DEVNULL) != 0) 533 { 534 struct stat sb1, sb2; 535 536 if (lstat (file1, &sb1) < 0) 537 error (1, errno, "cannot get file information for %s", file1); 538 if (lstat (file2, &sb2) < 0) 539 error (1, errno, "cannot get file information for %s", file2); 540 541 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) 542 file1 = DEVNULL; 543 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) 544 file2 = DEVNULL; 545 } 546 #endif 547 548 /* The first arg to call_diff_setup is used only for error reporting. */ 549 call_diff_setup ("diff", dargc, dargv); 550 if (label1) 551 call_diff_add_arg (label1); 552 if (label2) 553 call_diff_add_arg (label2); 554 call_diff_add_arg ("--"); 555 call_diff_add_arg (file1); 556 call_diff_add_arg (file2); 557 558 return call_diff (out); 559 } 560 561 /* Print the options passed to DIFF, in the format used by rcsdiff. 562 The rcsdiff code that produces this output is extremely hairy, and 563 it is not clear how rcsdiff decides which options to print and 564 which not to print. The code below reproduces every rcsdiff run 565 that I have seen. */ 566 567 static void 568 RCS_output_diff_options (int diff_argc, char * const *diff_argv, 569 const char *rev1, const char *rev2, 570 const char *workfile) 571 { 572 int i; 573 574 cvs_output ("diff", 0); 575 for (i = 0; i < diff_argc; i++) 576 { 577 cvs_output (" ", 1); 578 cvs_output (quotearg_style (shell_quoting_style, diff_argv[i]), 0); 579 } 580 cvs_output (" -r", 3); 581 cvs_output (rev1, 0); 582 583 if (rev2) 584 { 585 cvs_output (" -r", 3); 586 cvs_output (rev2, 0); 587 } 588 else 589 { 590 assert (workfile != NULL); 591 cvs_output (" ", 1); 592 cvs_output (workfile, 0); 593 } 594 cvs_output ("\n", 1); 595 } 596