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