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 status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, 410 (RCSCHECKOUTPROC)0, NULL); 411 if (status > 0) 412 { 413 retval = status; 414 goto error_return; 415 } 416 else if (status < 0) 417 { 418 error (0, errno, 419 "cannot check out revision %s of %s", rev1, rcsfile->path); 420 retval = 1; 421 goto error_return; 422 } 423 424 if (rev2 == NULL) 425 { 426 assert (workfile != NULL); 427 use_file2 = workfile; 428 } 429 else 430 { 431 tmpfile2 = cvs_temp_name (); 432 status = RCS_checkout (rcsfile, NULL, rev2, NULL, options, 433 tmpfile2, (RCSCHECKOUTPROC)0, NULL); 434 if (status > 0) 435 { 436 retval = status; 437 goto error_return; 438 } 439 else if (status < 0) 440 { 441 error (0, errno, 442 "cannot check out revision %s of %s", rev2, rcsfile->path); 443 return 1; 444 } 445 use_file2 = tmpfile2; 446 } 447 448 RCS_output_diff_options (opts, rev1, rev2, workfile); 449 status = diff_execv (tmpfile1, use_file2, label1, label2, opts, RUN_TTY); 450 if (status >= 0) 451 { 452 retval = status; 453 goto error_return; 454 } 455 else if (status < 0) 456 { 457 error (0, errno, 458 "cannot diff %s and %s", tmpfile1, use_file2); 459 retval = 1; 460 goto error_return; 461 } 462 463 error_return: 464 { 465 int save_noexec = noexec; 466 noexec = 0; 467 if (unlink_file (tmpfile1) < 0) 468 { 469 if (!existence_error (errno)) 470 error (0, errno, "cannot remove temp file %s", tmpfile1); 471 } 472 noexec = save_noexec; 473 } 474 free (tmpfile1); 475 if (tmpfile2 != NULL) 476 { 477 int save_noexec = noexec; 478 noexec = 0; 479 if (unlink_file (tmpfile2) < 0) 480 { 481 if (!existence_error (errno)) 482 error (0, errno, "cannot remove temp file %s", tmpfile2); 483 } 484 noexec = save_noexec; 485 free (tmpfile2); 486 } 487 488 return retval; 489 } 490 491 492 /* Show differences between two files. This is the start of a diff library. 493 494 Some issues: 495 496 * Should option parsing be part of the library or the caller? The 497 former allows the library to add options without changing the callers, 498 but it causes various problems. One is that something like --brief really 499 wants special handling in CVS, and probably the caller should retain 500 some flexibility in this area. Another is online help (the library could 501 have some feature for providing help, but how does that interact with 502 the help provided by the caller directly?). Another is that as things 503 stand currently, there is no separate namespace for diff options versus 504 "cvs diff" options like -l (that is, if the library adds an option which 505 conflicts with a CVS option, it is trouble). 506 507 * This isn't required for a first-cut diff library, but if there 508 would be a way for the caller to specify the timestamps that appear 509 in the diffs (rather than the library getting them from the files), 510 that would clean up the kludgy utime() calls in patch.c. 511 512 Show differences between FILE1 and FILE2. Either one can be 513 DEVNULL to indicate a nonexistent file (same as an empty file 514 currently, I suspect, but that may be an issue in and of itself). 515 OPTIONS is a list of diff options, or "" if none. At a minimum, 516 CVS expects that -c (update.c, patch.c) and -n (update.c) will be 517 supported. Other options, like -u, --speed-large-files, &c, will 518 be specified if the user specified them. 519 520 OUT is a filename to send the diffs to, or RUN_TTY to send them to 521 stdout. Error messages go to stderr. Return value is 0 for 522 success, -1 for a failure which set errno, 1 for success (and some 523 differences were found), or >1 for a failure which printed a 524 message on stderr. */ 525 526 int 527 diff_exec (file1, file2, label1, label2, options, out) 528 char *file1; 529 char *file2; 530 char *label1; 531 char *label2; 532 char *options; 533 char *out; 534 { 535 char *args; 536 537 #ifdef PRESERVE_PERMISSIONS_SUPPORT 538 /* If either file1 or file2 are special files, pretend they are 539 /dev/null. Reason: suppose a file that represents a block 540 special device in one revision becomes a regular file. CVS 541 must find the `difference' between these files, but a special 542 file contains no data useful for calculating this metric. The 543 safe thing to do is to treat the special file as an empty file, 544 thus recording the regular file's full contents. Doing so will 545 create extremely large deltas at the point of transition 546 between device files and regular files, but this is probably 547 very rare anyway. 548 549 There may be ways around this, but I think they are fraught 550 with danger. -twp */ 551 552 if (preserve_perms && 553 strcmp (file1, DEVNULL) != 0 && 554 strcmp (file2, DEVNULL) != 0) 555 { 556 struct stat sb1, sb2; 557 558 if (CVS_LSTAT (file1, &sb1) < 0) 559 error (1, errno, "cannot get file information for %s", file1); 560 if (CVS_LSTAT (file2, &sb2) < 0) 561 error (1, errno, "cannot get file information for %s", file2); 562 563 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) 564 file1 = DEVNULL; 565 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) 566 file2 = DEVNULL; 567 } 568 #endif 569 570 args = xmalloc (strlen (options) + 10); 571 /* The first word in this string is used only for error reporting. */ 572 sprintf (args, "diff %s", options); 573 call_diff_setup (args); 574 if (label1) 575 call_diff_arg (label1); 576 if (label2) 577 call_diff_arg (label2); 578 call_diff_arg (file1); 579 call_diff_arg (file2); 580 free (args); 581 582 return call_diff (out); 583 } 584 585 int 586 diff_execv (file1, file2, label1, label2, options, out) 587 char *file1; 588 char *file2; 589 char *label1; 590 char *label2; 591 char *options; 592 char *out; 593 { 594 char *args; 595 596 #ifdef PRESERVE_PERMISSIONS_SUPPORT 597 /* Pretend that special files are /dev/null for purposes of making 598 diffs. See comments in diff_exec. */ 599 600 if (preserve_perms && 601 strcmp (file1, DEVNULL) != 0 && 602 strcmp (file2, DEVNULL) != 0) 603 { 604 struct stat sb1, sb2; 605 606 if (CVS_LSTAT (file1, &sb1) < 0) 607 error (1, errno, "cannot get file information for %s", file1); 608 if (CVS_LSTAT (file2, &sb2) < 0) 609 error (1, errno, "cannot get file information for %s", file2); 610 611 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) 612 file1 = DEVNULL; 613 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) 614 file2 = DEVNULL; 615 } 616 #endif 617 618 args = xmalloc (strlen (options) + 10); 619 /* The first word in this string is used only for error reporting. */ 620 /* I guess we are pretty confident that options starts with a space. */ 621 sprintf (args, "diff%s", options); 622 call_diff_setup (args); 623 if (label1) 624 call_diff_arg (label1); 625 if (label2) 626 call_diff_arg (label2); 627 call_diff_arg (file1); 628 call_diff_arg (file2); 629 free (args); 630 631 return call_diff (out); 632 } 633 634 /* Print the options passed to DIFF, in the format used by rcsdiff. 635 The rcsdiff code that produces this output is extremely hairy, and 636 it is not clear how rcsdiff decides which options to print and 637 which not to print. The code below reproduces every rcsdiff run 638 that I have seen. */ 639 640 static void 641 RCS_output_diff_options (opts, rev1, rev2, workfile) 642 char *opts; 643 char *rev1; 644 char *rev2; 645 char *workfile; 646 { 647 char *tmp; 648 649 tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10); 650 651 sprintf (tmp, "diff%s -r%s", opts, rev1); 652 cvs_output (tmp, 0); 653 free (tmp); 654 655 if (rev2) 656 { 657 cvs_output (" -r", 3); 658 cvs_output (rev2, 0); 659 } 660 else 661 { 662 assert (workfile != NULL); 663 cvs_output (" ", 1); 664 cvs_output (workfile, 0); 665 } 666 cvs_output ("\n", 1); 667 } 668