117721Speter/* 2175282Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3175282Sobrien * 4175282Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175282Sobrien * and others. 6175282Sobrien * 7175282Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175282Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 917721Speter * 1017721Speter * You may distribute under the terms of the GNU General Public License as 1132785Speter * specified in the README file that comes with the CVS source distribution. 1217721Speter * 1317721Speter * The functions in this file provide an interface for performing 1417721Speter * operations directly on RCS files. 1554431Speter * 1654431Speter * $FreeBSD$ 1717721Speter */ 1817721Speter 1917721Speter#include "cvs.h" 2017721Speter#include <assert.h> 2144856Speter#include <stdio.h> 2244856Speter#include "diffrun.h" 2317721Speter 2432785Speter/* This file, rcs.h, and rcs.c, together sometimes known as the "RCS 2532785Speter library", are intended to define our interface to RCS files. 2625839Speter 2725839Speter Whether there will also be a version of RCS which uses this 2825839Speter library, or whether the library will be packaged for uses beyond 2925839Speter CVS or RCS (many people would like such a thing) is an open 3025839Speter question. Some considerations: 3125839Speter 3225839Speter 1. An RCS library for CVS must have the capabilities of the 3325839Speter existing CVS code which accesses RCS files. In particular, simple 3425839Speter approaches will often be slow. 3525839Speter 3632785Speter 2. An RCS library should not use code from the current RCS 3725839Speter (5.7 and its ancestors). The code has many problems. Too few 3825839Speter comments, too many layers of abstraction, too many global variables 3925839Speter (the correct number for a library is zero), too much intricately 4025839Speter interwoven functionality, and too many clever hacks. Paul Eggert, 4125839Speter the current RCS maintainer, agrees. 4225839Speter 4325839Speter 3. More work needs to be done in terms of separating out the RCS 4425839Speter library from the rest of CVS (for example, cvs_output should be 4525839Speter replaced by a callback, and the declarations should be centralized 4625839Speter into rcs.h, and probably other such cleanups). 4725839Speter 4825839Speter 4. To be useful for RCS and perhaps for other uses, the library 4925839Speter may need features beyond those needed by CVS. 5025839Speter 5125839Speter 5. Any changes to the RCS file format *must* be compatible. Many, 5225839Speter many tools (not just CVS and RCS) can at least import this format. 5325839Speter RCS and CVS must preserve the current ability to import/export it 5425839Speter (preferably improved--magic branches are currently a roadblock). 5525839Speter See doc/RCSFILES in the CVS distribution for documentation of this 5625839Speter file format. 5725839Speter 5832785Speter On a related note, see the comments at diff_exec, later in this file, 5932785Speter for more on the diff library. */ 6025839Speter 61175282Sobrienstatic void RCS_output_diff_options PROTO ((int, char *const *, const char *, 62175282Sobrien const char *, const char *)); 6325839Speter 6425839Speter 6532785Speter/* Stuff to deal with passing arguments the way libdiff.a wants to deal 6632785Speter with them. This is a crufty interface; there is no good reason for it 6732785Speter to resemble a command line rather than something closer to "struct 6832785Speter log_data" in log.c. */ 6917721Speter 7032785Speter/* First call call_diff_setup to setup any initial arguments. The 7132785Speter argument will be parsed into whitespace separated words and added 7232785Speter to the global call_diff_argv list. 7332785Speter 74175282Sobrien Then, optionally, call call_diff_add_arg for each additional argument 7532785Speter that you'd like to pass to the diff library. 7632785Speter 7732785Speter Finally, call call_diff or call_diff3 to produce the diffs. */ 7832785Speter 7932785Speterstatic char **call_diff_argv; 8032785Speterstatic int call_diff_argc; 81175282Sobrienstatic size_t call_diff_argc_allocated; 8232785Speter 8332785Speterstatic void call_diff_add_arg PROTO ((const char *)); 84175282Sobrienstatic void call_diff_setup PROTO ((const char *prog, 85175282Sobrien int argc, char * const *argv)); 86128269Speterstatic int call_diff PROTO ((const char *out)); 8732785Speterstatic int call_diff3 PROTO ((char *out)); 8832785Speter 8944856Speterstatic void call_diff_write_output PROTO((const char *, size_t)); 9044856Speterstatic void call_diff_flush_output PROTO((void)); 9144856Speterstatic void call_diff_write_stdout PROTO((const char *)); 9244856Speterstatic void call_diff_error PROTO((const char *, const char *, const char *)); 9344856Speter 94175282Sobrien 95175282Sobrien 96175282Sobrienstatic void 97175282Sobriencall_diff_add_arg (s) 98175282Sobrien const char *s; 99175282Sobrien{ 100175282Sobrien run_add_arg_p (&call_diff_argc, &call_diff_argc_allocated, &call_diff_argv, 101175282Sobrien s); 102175282Sobrien} 103175282Sobrien 104175282Sobrien 105175282Sobrien 10632785Speter/* VARARGS */ 10732785Speterstatic void 108175282Sobriencall_diff_setup (prog, argc, argv) 10932785Speter const char *prog; 110175282Sobrien int argc; 111175282Sobrien char * const *argv; 11217721Speter{ 11332785Speter int i; 11432785Speter 11532785Speter /* clean out any malloc'ed values from call_diff_argv */ 116175282Sobrien run_arg_free_p (call_diff_argc, call_diff_argv); 11732785Speter call_diff_argc = 0; 11832785Speter 11932785Speter /* put each word into call_diff_argv, allocating it as we go */ 120175282Sobrien call_diff_add_arg (prog); 121175282Sobrien for (i = 0; i < argc; i++) 122175282Sobrien call_diff_add_arg (argv[i]); 12317721Speter} 12417721Speter 12517721Speter 12644856Speter/* Callback function for the diff library to write data to the output 12744856Speter file. This is used when we are producing output to stdout. */ 12832785Speter 12944856Speterstatic void 13044856Spetercall_diff_write_output (text, len) 13144856Speter const char *text; 13244856Speter size_t len; 13344856Speter{ 13454431Speter if (len > 0) 13554431Speter cvs_output (text, len); 13644856Speter} 13744856Speter 13844856Speter/* Call back function for the diff library to flush the output file. 13944856Speter This is used when we are producing output to stdout. */ 14044856Speter 14144856Speterstatic void 14244856Spetercall_diff_flush_output () 14344856Speter{ 14444856Speter cvs_flushout (); 14544856Speter} 14644856Speter 14744856Speter/* Call back function for the diff library to write to stdout. */ 14844856Speter 14944856Speterstatic void 15044856Spetercall_diff_write_stdout (text) 15144856Speter const char *text; 15244856Speter{ 15344856Speter cvs_output (text, 0); 15444856Speter} 15544856Speter 15644856Speter/* Call back function for the diff library to write to stderr. */ 15744856Speter 15844856Speterstatic void 15944856Spetercall_diff_error (format, a1, a2) 16044856Speter const char *format; 16144856Speter const char *a1; 16244856Speter const char *a2; 16344856Speter{ 16444856Speter /* FIXME: Should we somehow indicate that this error is coming from 16544856Speter the diff library? */ 16644856Speter error (0, 0, format, a1, a2); 16744856Speter} 16844856Speter 16944856Speter/* This set of callback functions is used if we are sending the diff 17044856Speter to stdout. */ 17144856Speter 17244856Speterstatic struct diff_callbacks call_diff_stdout_callbacks = 17344856Speter{ 17444856Speter call_diff_write_output, 17544856Speter call_diff_flush_output, 17644856Speter call_diff_write_stdout, 17744856Speter call_diff_error 17844856Speter}; 17944856Speter 18044856Speter/* This set of callback functions is used if we are sending the diff 18144856Speter to a file. */ 18244856Speter 18344856Speterstatic struct diff_callbacks call_diff_file_callbacks = 18444856Speter{ 18544856Speter (void (*) PROTO((const char *, size_t))) NULL, 18644856Speter (void (*) PROTO((void))) NULL, 18744856Speter call_diff_write_stdout, 18844856Speter call_diff_error 18944856Speter}; 19044856Speter 191128269Speter 192128269Speter 19332785Speterstatic int 19432785Spetercall_diff (out) 195128269Speter const char *out; 19617721Speter{ 197175282Sobrien call_diff_add_arg (NULL); 198175282Sobrien 19932785Speter if (out == RUN_TTY) 20044856Speter return diff_run (call_diff_argc, call_diff_argv, NULL, 20144856Speter &call_diff_stdout_callbacks); 20232785Speter else 20344856Speter return diff_run (call_diff_argc, call_diff_argv, out, 20444856Speter &call_diff_file_callbacks); 20517721Speter} 20617721Speter 207128269Speter 208128269Speter 20932785Speterstatic int 21032785Spetercall_diff3 (out) 21132785Speter char *out; 21217721Speter{ 21332785Speter if (out == RUN_TTY) 21444856Speter return diff3_run (call_diff_argc, call_diff_argv, NULL, 21544856Speter &call_diff_stdout_callbacks); 21632785Speter else 21744856Speter return diff3_run (call_diff_argc, call_diff_argv, out, 21844856Speter &call_diff_file_callbacks); 21917721Speter} 22017721Speter 22132785Speter 22232785Speter 22317721Speter/* Merge revisions REV1 and REV2. */ 22432785Speter 22517721Speterint 22632785SpeterRCS_merge(rcs, path, workfile, options, rev1, rev2) 22732785Speter RCSNode *rcs; 228128269Speter const char *path; 229128269Speter const char *workfile; 230128269Speter const char *options; 231128269Speter const char *rev1; 232128269Speter const char *rev2; 23317721Speter{ 23432785Speter char *xrev1, *xrev2; 23532785Speter char *tmp1, *tmp2; 23632785Speter char *diffout = NULL; 23732785Speter int retval; 23817721Speter 23932785Speter if (options != NULL && options[0] != '\0') 24032785Speter assert (options[0] == '-' && options[1] == 'k'); 24117721Speter 24232785Speter cvs_output ("RCS file: ", 0); 24332785Speter cvs_output (rcs->path, 0); 24432785Speter cvs_output ("\n", 1); 24532785Speter 24632785Speter /* Calculate numeric revision numbers from rev1 and rev2 (may be 24732785Speter symbolic). */ 24832785Speter xrev1 = RCS_gettag (rcs, rev1, 0, NULL); 24932785Speter xrev2 = RCS_gettag (rcs, rev2, 0, NULL); 250175282Sobrien assert (xrev1 && xrev2); 25132785Speter 25232785Speter /* Check out chosen revisions. The error message when RCS_checkout 25332785Speter fails is not very informative -- it is taken verbatim from RCS 5.7, 25432785Speter and relies on RCS_checkout saying something intelligent upon failure. */ 25532785Speter cvs_output ("retrieving revision ", 0); 25632785Speter cvs_output (xrev1, 0); 25732785Speter cvs_output ("\n", 1); 25832785Speter 25932785Speter tmp1 = cvs_temp_name(); 26032785Speter if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, 26132785Speter (RCSCHECKOUTPROC)0, NULL)) 26217721Speter { 26332785Speter cvs_outerr ("rcsmerge: co failed\n", 0); 26432785Speter error_exit(); 26517721Speter } 26632785Speter 26732785Speter cvs_output ("retrieving revision ", 0); 26832785Speter cvs_output (xrev2, 0); 26932785Speter cvs_output ("\n", 1); 27032785Speter 27132785Speter tmp2 = cvs_temp_name(); 27232785Speter if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, 27332785Speter (RCSCHECKOUTPROC)0, NULL)) 27432785Speter { 27532785Speter cvs_outerr ("rcsmerge: co failed\n", 0); 27632785Speter error_exit(); 27732785Speter } 27832785Speter 27932785Speter /* Merge changes. */ 28032785Speter cvs_output ("Merging differences between ", 0); 28132785Speter cvs_output (xrev1, 0); 28232785Speter cvs_output (" and ", 0); 28332785Speter cvs_output (xrev2, 0); 28432785Speter cvs_output (" into ", 0); 28532785Speter cvs_output (workfile, 0); 28632785Speter cvs_output ("\n", 1); 28732785Speter 28832785Speter /* Remember that the first word in the `call_diff_setup' string is used now 28932785Speter only for diagnostic messages -- CVS no longer forks to run diff3. */ 29032785Speter diffout = cvs_temp_name(); 291175282Sobrien call_diff_setup ("diff3", 0, NULL); 292175282Sobrien call_diff_add_arg ("-E"); 293175282Sobrien call_diff_add_arg ("-am"); 29432785Speter 295175282Sobrien call_diff_add_arg ("-L"); 296175282Sobrien call_diff_add_arg (workfile); 297175282Sobrien call_diff_add_arg ("-L"); 298175282Sobrien call_diff_add_arg (xrev1); 299175282Sobrien call_diff_add_arg ("-L"); 300175282Sobrien call_diff_add_arg (xrev2); 30132785Speter 302175282Sobrien call_diff_add_arg ("--"); 303175282Sobrien call_diff_add_arg (workfile); 304175282Sobrien call_diff_add_arg (tmp1); 305175282Sobrien call_diff_add_arg (tmp2); 30632785Speter 30732785Speter retval = call_diff3 (diffout); 30832785Speter 30932785Speter if (retval == 1) 31032785Speter cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0); 31132785Speter else if (retval == 2) 31232785Speter error_exit(); 31332785Speter 31432785Speter if (diffout) 31532785Speter copy_file (diffout, workfile); 31632785Speter 31732785Speter /* Clean up. */ 31832785Speter { 31932785Speter int save_noexec = noexec; 32032785Speter noexec = 0; 32132785Speter if (unlink_file (tmp1) < 0) 32232785Speter { 32332785Speter if (!existence_error (errno)) 32432785Speter error (0, errno, "cannot remove temp file %s", tmp1); 32532785Speter } 32632785Speter free (tmp1); 32732785Speter if (unlink_file (tmp2) < 0) 32832785Speter { 32932785Speter if (!existence_error (errno)) 33032785Speter error (0, errno, "cannot remove temp file %s", tmp2); 33132785Speter } 33232785Speter free (tmp2); 33332785Speter if (diffout) 33432785Speter { 33532785Speter if (unlink_file (diffout) < 0) 33632785Speter { 33732785Speter if (!existence_error (errno)) 33832785Speter error (0, errno, "cannot remove temp file %s", diffout); 33932785Speter } 34032785Speter free (diffout); 34132785Speter } 34232785Speter free (xrev1); 34332785Speter free (xrev2); 34432785Speter noexec = save_noexec; 34532785Speter } 34632785Speter 34732785Speter return retval; 34817721Speter} 34917721Speter 35032785Speter/* Diff revisions and/or files. OPTS controls the format of the diff 35132785Speter (it contains options such as "-w -c", &c), or "" for the default. 35232785Speter OPTIONS controls keyword expansion, as a string starting with "-k", 35332785Speter or "" to use the default. REV1 is the first revision to compare 35432785Speter against; it must be non-NULL. If REV2 is non-NULL, compare REV1 35532785Speter and REV2; if REV2 is NULL compare REV1 with the file in the working 35632785Speter directory, whose name is WORKFILE. LABEL1 and LABEL2 are default 35732785Speter file labels, and (if non-NULL) should be added as -L options 35832785Speter to diff. Output goes to stdout. 35932785Speter 36032785Speter Return value is 0 for success, -1 for a failure which set errno, 36132785Speter or positive for a failure which printed a message on stderr. 36232785Speter 36332785Speter This used to exec rcsdiff, but now calls RCS_checkout and diff_exec. 36432785Speter 36532785Speter An issue is what timezone is used for the dates which appear in the 36632785Speter diff output. rcsdiff uses the -z flag, which is not presently 36732785Speter processed by CVS diff, but I'm not sure exactly how hard to worry 36832785Speter about this--any such features are undocumented in the context of 36932785Speter CVS, and I'm not sure how important to users. */ 37017721Speterint 371175282SobrienRCS_exec_rcsdiff (rcsfile, diff_argc, diff_argv, options, rev1, rev1_cache, 372175282Sobrien rev2, label1, label2, workfile) 37332785Speter RCSNode *rcsfile; 374175282Sobrien int diff_argc; 375175282Sobrien char * const *diff_argv; 376128269Speter const char *options; 377128269Speter const char *rev1; 378128269Speter const char *rev1_cache; 379128269Speter const char *rev2; 380128269Speter const char *label1; 381128269Speter const char *label2; 382128269Speter const char *workfile; 38317721Speter{ 384128269Speter char *tmpfile1 = NULL; 385128269Speter char *tmpfile2 = NULL; 386128269Speter const char *use_file1, *use_file2; 38732785Speter int status, retval; 38825839Speter 38925839Speter 39032785Speter cvs_output ("\ 39132785Speter===================================================================\n\ 39232785SpeterRCS file: ", 0); 39332785Speter cvs_output (rcsfile->path, 0); 39432785Speter cvs_output ("\n", 1); 39532785Speter 39632785Speter /* Historically, `cvs diff' has expanded the $Name keyword to the 39732785Speter empty string when checking out revisions. This is an accident, 39832785Speter but no one has considered the issue thoroughly enough to determine 39932785Speter what the best behavior is. Passing NULL for the `nametag' argument 40032785Speter preserves the existing behavior. */ 40132785Speter 40232785Speter cvs_output ("retrieving revision ", 0); 40332785Speter cvs_output (rev1, 0); 40432785Speter cvs_output ("\n", 1); 405128269Speter 406128269Speter if (rev1_cache != NULL) 407128269Speter use_file1 = rev1_cache; 408128269Speter else 40925839Speter { 410128269Speter tmpfile1 = cvs_temp_name(); 411128269Speter status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, 412128269Speter (RCSCHECKOUTPROC)0, NULL); 413128269Speter if (status > 0) 414128269Speter { 415128269Speter retval = status; 416128269Speter goto error_return; 417128269Speter } 418128269Speter else if (status < 0) 419128269Speter { 420128269Speter error( 0, errno, 421128269Speter "cannot check out revision %s of %s", rev1, rcsfile->path ); 422128269Speter retval = 1; 423128269Speter goto error_return; 424128269Speter } 425128269Speter use_file1 = tmpfile1; 42625839Speter } 42732785Speter 42832785Speter if (rev2 == NULL) 42932785Speter { 43032785Speter assert (workfile != NULL); 43132785Speter use_file2 = workfile; 43232785Speter } 43332785Speter else 43432785Speter { 43532785Speter tmpfile2 = cvs_temp_name (); 43632785Speter cvs_output ("retrieving revision ", 0); 43732785Speter cvs_output (rev2, 0); 43832785Speter cvs_output ("\n", 1); 43932785Speter status = RCS_checkout (rcsfile, NULL, rev2, NULL, options, 44032785Speter tmpfile2, (RCSCHECKOUTPROC)0, NULL); 44132785Speter if (status > 0) 44232785Speter { 44332785Speter retval = status; 44432785Speter goto error_return; 44532785Speter } 44632785Speter else if (status < 0) 44732785Speter { 44832785Speter error (0, errno, 44932785Speter "cannot check out revision %s of %s", rev2, rcsfile->path); 45032785Speter return 1; 45132785Speter } 45232785Speter use_file2 = tmpfile2; 45332785Speter } 45432785Speter 455175282Sobrien RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile); 456175282Sobrien status = diff_exec (use_file1, use_file2, label1, label2, 457175282Sobrien diff_argc, diff_argv, RUN_TTY); 45832785Speter if (status >= 0) 45932785Speter { 46032785Speter retval = status; 46132785Speter goto error_return; 46232785Speter } 46332785Speter else if (status < 0) 46432785Speter { 46532785Speter error (0, errno, 466128269Speter "cannot diff %s and %s", use_file1, use_file2); 46732785Speter retval = 1; 46832785Speter goto error_return; 46932785Speter } 47032785Speter 47132785Speter error_return: 47232785Speter { 473128269Speter /* Call CVS_UNLINK() below rather than unlink_file to avoid the check 474128269Speter * for noexec. 475128269Speter */ 476128269Speter if( tmpfile1 != NULL ) 47732785Speter { 478128269Speter if( CVS_UNLINK( tmpfile1 ) < 0 ) 479128269Speter { 480128269Speter if( !existence_error( errno ) ) 481128269Speter error( 0, errno, "cannot remove temp file %s", tmpfile1 ); 482128269Speter } 483128269Speter free( tmpfile1 ); 48432785Speter } 485128269Speter if( tmpfile2 != NULL ) 48632785Speter { 487128269Speter if( CVS_UNLINK( tmpfile2 ) < 0 ) 488128269Speter { 489128269Speter if( !existence_error( errno ) ) 490128269Speter error( 0, errno, "cannot remove temp file %s", tmpfile2 ); 491128269Speter } 492128269Speter free (tmpfile2); 49332785Speter } 49432785Speter } 49532785Speter 49625839Speter return retval; 49717721Speter} 49832785Speter 49932785Speter 500128269Speter 50132785Speter/* Show differences between two files. This is the start of a diff library. 50232785Speter 50332785Speter Some issues: 50432785Speter 50532785Speter * Should option parsing be part of the library or the caller? The 50632785Speter former allows the library to add options without changing the callers, 50732785Speter but it causes various problems. One is that something like --brief really 50832785Speter wants special handling in CVS, and probably the caller should retain 50932785Speter some flexibility in this area. Another is online help (the library could 51032785Speter have some feature for providing help, but how does that interact with 51132785Speter the help provided by the caller directly?). Another is that as things 51232785Speter stand currently, there is no separate namespace for diff options versus 51332785Speter "cvs diff" options like -l (that is, if the library adds an option which 51432785Speter conflicts with a CVS option, it is trouble). 51532785Speter 51632785Speter * This isn't required for a first-cut diff library, but if there 51732785Speter would be a way for the caller to specify the timestamps that appear 51832785Speter in the diffs (rather than the library getting them from the files), 51932785Speter that would clean up the kludgy utime() calls in patch.c. 52032785Speter 52132785Speter Show differences between FILE1 and FILE2. Either one can be 52232785Speter DEVNULL to indicate a nonexistent file (same as an empty file 52332785Speter currently, I suspect, but that may be an issue in and of itself). 52432785Speter OPTIONS is a list of diff options, or "" if none. At a minimum, 52532785Speter CVS expects that -c (update.c, patch.c) and -n (update.c) will be 52632785Speter supported. Other options, like -u, --speed-large-files, &c, will 52732785Speter be specified if the user specified them. 52832785Speter 52932785Speter OUT is a filename to send the diffs to, or RUN_TTY to send them to 53032785Speter stdout. Error messages go to stderr. Return value is 0 for 53132785Speter success, -1 for a failure which set errno, 1 for success (and some 53232785Speter differences were found), or >1 for a failure which printed a 53332785Speter message on stderr. */ 53432785Speter 53532785Speterint 536175282Sobriendiff_exec (file1, file2, label1, label2, dargc, dargv, out) 537128269Speter const char *file1; 538128269Speter const char *file2; 539128269Speter const char *label1; 540128269Speter const char *label2; 541175282Sobrien int dargc; 542175282Sobrien char * const *dargv; 543128269Speter const char *out; 54432785Speter{ 54534467Speter#ifdef PRESERVE_PERMISSIONS_SUPPORT 54634467Speter /* If either file1 or file2 are special files, pretend they are 54734467Speter /dev/null. Reason: suppose a file that represents a block 54834467Speter special device in one revision becomes a regular file. CVS 54934467Speter must find the `difference' between these files, but a special 55034467Speter file contains no data useful for calculating this metric. The 55134467Speter safe thing to do is to treat the special file as an empty file, 55234467Speter thus recording the regular file's full contents. Doing so will 55334467Speter create extremely large deltas at the point of transition 55434467Speter between device files and regular files, but this is probably 55534467Speter very rare anyway. 55634467Speter 55734467Speter There may be ways around this, but I think they are fraught 55834467Speter with danger. -twp */ 55934467Speter 56034467Speter if (preserve_perms && 56134467Speter strcmp (file1, DEVNULL) != 0 && 56234467Speter strcmp (file2, DEVNULL) != 0) 56334467Speter { 56434467Speter struct stat sb1, sb2; 56534467Speter 56634467Speter if (CVS_LSTAT (file1, &sb1) < 0) 56734467Speter error (1, errno, "cannot get file information for %s", file1); 56834467Speter if (CVS_LSTAT (file2, &sb2) < 0) 56934467Speter error (1, errno, "cannot get file information for %s", file2); 57034467Speter 57134467Speter if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) 57234467Speter file1 = DEVNULL; 57334467Speter if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) 57434467Speter file2 = DEVNULL; 57534467Speter } 57634467Speter#endif 57734467Speter 578175282Sobrien /* The first arg to call_diff_setup is used only for error reporting. */ 579175282Sobrien call_diff_setup ("diff", dargc, dargv); 58081407Speter if (label1) 581175282Sobrien call_diff_add_arg (label1); 58281407Speter if (label2) 583175282Sobrien call_diff_add_arg (label2); 584175282Sobrien call_diff_add_arg ("--"); 585175282Sobrien call_diff_add_arg (file1); 586175282Sobrien call_diff_add_arg (file2); 58732785Speter 58832785Speter return call_diff (out); 58932785Speter} 59032785Speter 59132785Speter/* Print the options passed to DIFF, in the format used by rcsdiff. 59232785Speter The rcsdiff code that produces this output is extremely hairy, and 59332785Speter it is not clear how rcsdiff decides which options to print and 59432785Speter which not to print. The code below reproduces every rcsdiff run 59532785Speter that I have seen. */ 59632785Speter 59732785Speterstatic void 598175282SobrienRCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile) 599175282Sobrien int diff_argc; 600175282Sobrien char * const *diff_argv; 601128269Speter const char *rev1; 602128269Speter const char *rev2; 603128269Speter const char *workfile; 60432785Speter{ 605175282Sobrien int i; 606175282Sobrien 607175282Sobrien cvs_output ("diff", 0); 608175282Sobrien for (i = 0; i < diff_argc; i++) 609175282Sobrien { 610175282Sobrien cvs_output (" ", 1); 611175282Sobrien cvs_output (diff_argv[i], 0); 612175282Sobrien } 613175282Sobrien cvs_output (" -r", 3); 614175282Sobrien cvs_output (rev1, 0); 61532785Speter 61632785Speter if (rev2) 61732785Speter { 61832785Speter cvs_output (" -r", 3); 61932785Speter cvs_output (rev2, 0); 62032785Speter } 62132785Speter else 62232785Speter { 62332785Speter assert (workfile != NULL); 62432785Speter cvs_output (" ", 1); 62532785Speter cvs_output (workfile, 0); 62632785Speter } 62732785Speter cvs_output ("\n", 1); 62832785Speter} 629