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