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