14 |
* operations directly on RCS files. |
* operations directly on RCS files. |
15 |
*/ |
*/ |
16 |
|
|
17 |
#include "cvs.h" |
#ifdef HAVE_CONFIG_H |
18 |
|
# include <config.h> |
19 |
|
#endif |
20 |
|
|
21 |
|
/* Standard headers. */ |
22 |
#include <stdio.h> |
#include <stdio.h> |
23 |
#include "diffrun.h" |
|
24 |
|
/* GNULIB */ |
25 |
#include "quotearg.h" |
#include "quotearg.h" |
26 |
|
|
27 |
|
/* CVS */ |
28 |
|
#include "difflib.h" |
29 |
|
#include "cvs.h" |
30 |
|
|
31 |
/* This file, rcs.h, and rcs.c, together sometimes known as the "RCS |
/* This file, rcs.h, and rcs.c, together sometimes known as the "RCS |
32 |
library", are intended to define our interface to RCS files. |
library", are intended to define our interface to RCS files. |
33 |
|
|
74 |
to resemble a command line rather than something closer to "struct |
to resemble a command line rather than something closer to "struct |
75 |
log_data" in log.c. */ |
log_data" in log.c. */ |
76 |
|
|
|
/* First call call_diff_setup to setup any initial arguments. The |
|
|
argument will be parsed into whitespace separated words and added |
|
|
to the global call_diff_argv list. |
|
|
|
|
|
Then, optionally, call call_diff_add_arg for each additional argument |
|
|
that you'd like to pass to the diff library. |
|
|
|
|
|
Finally, call call_diff or call_diff3 to produce the diffs. */ |
|
|
|
|
|
static char **call_diff_argv; |
|
|
static int call_diff_argc; |
|
|
static size_t call_diff_arg_allocated; |
|
|
|
|
|
static int call_diff (const char *out); |
|
|
static int call_diff3 (char *out); |
|
|
|
|
|
static void call_diff_write_output (const char *, size_t); |
|
|
static void call_diff_flush_output (void); |
|
|
static void call_diff_write_stdout (const char *); |
|
|
static void call_diff_error (const char *, const char *, const char *); |
|
|
|
|
|
|
|
|
|
|
|
/* VARARGS */ |
|
|
static void |
|
|
call_diff_add_arg (const char *s) |
|
|
{ |
|
|
TRACE (TRACE_DATA, "call_diff_add_arg (%s)", s); |
|
|
run_add_arg_p (&call_diff_argc, &call_diff_arg_allocated, &call_diff_argv, |
|
|
s); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
call_diff_setup (const char *prog, int argc, char * const *argv) |
|
|
{ |
|
|
int i; |
|
|
|
|
|
/* clean out any malloc'ed values from call_diff_argv */ |
|
|
run_arg_free_p (call_diff_argc, call_diff_argv); |
|
|
call_diff_argc = 0; |
|
|
|
|
|
/* put each word into call_diff_argv, allocating it as we go */ |
|
|
call_diff_add_arg (prog); |
|
|
for (i = 0; i < argc; i++) |
|
|
call_diff_add_arg (argv[i]); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Callback function for the diff library to write data to the output |
|
|
file. This is used when we are producing output to stdout. */ |
|
|
|
|
|
static void |
|
|
call_diff_write_output (const char *text, size_t len) |
|
|
{ |
|
|
if (len > 0) |
|
|
cvs_output (text, len); |
|
|
} |
|
|
|
|
|
/* Call back function for the diff library to flush the output file. |
|
|
This is used when we are producing output to stdout. */ |
|
|
|
|
|
static void |
|
|
call_diff_flush_output (void) |
|
|
{ |
|
|
cvs_flushout (); |
|
|
} |
|
|
|
|
|
/* Call back function for the diff library to write to stdout. */ |
|
|
|
|
|
static void |
|
|
call_diff_write_stdout (const char *text) |
|
|
{ |
|
|
cvs_output (text, 0); |
|
|
} |
|
|
|
|
|
/* Call back function for the diff library to write to stderr. */ |
|
|
|
|
|
static void |
|
|
call_diff_error (const char *format, const char *a1, const char *a2) |
|
|
{ |
|
|
/* FIXME: Should we somehow indicate that this error is coming from |
|
|
the diff library? */ |
|
|
error (0, 0, format, a1, a2); |
|
|
} |
|
|
|
|
|
/* This set of callback functions is used if we are sending the diff |
|
|
to stdout. */ |
|
|
|
|
|
static struct diff_callbacks call_diff_stdout_callbacks = |
|
|
{ |
|
|
call_diff_write_output, |
|
|
call_diff_flush_output, |
|
|
call_diff_write_stdout, |
|
|
call_diff_error |
|
|
}; |
|
|
|
|
|
/* This set of callback functions is used if we are sending the diff |
|
|
to a file. */ |
|
|
|
|
|
static struct diff_callbacks call_diff_file_callbacks = |
|
|
{ |
|
|
NULL, |
|
|
NULL, |
|
|
call_diff_write_stdout, |
|
|
call_diff_error |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
call_diff (const char *out) |
|
|
{ |
|
|
call_diff_add_arg (NULL); |
|
|
|
|
|
if (out == RUN_TTY) |
|
|
return diff_run( call_diff_argc, call_diff_argv, NULL, |
|
|
&call_diff_stdout_callbacks ); |
|
|
else |
|
|
return diff_run( call_diff_argc, call_diff_argv, out, |
|
|
&call_diff_file_callbacks ); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
call_diff3 (char *out) |
|
|
{ |
|
|
if (out == RUN_TTY) |
|
|
return diff3_run (call_diff_argc, call_diff_argv, NULL, |
|
|
&call_diff_stdout_callbacks); |
|
|
else |
|
|
return diff3_run (call_diff_argc, call_diff_argv, out, |
|
|
&call_diff_file_callbacks); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Merge revisions REV1 and REV2. */ |
|
|
|
|
|
int |
|
|
RCS_merge (RCSNode *rcs, const char *path, const char *workfile, |
|
|
const char *options, const char *rev1, const char *rev2) |
|
|
{ |
|
|
char *xrev1, *xrev2; |
|
|
char *tmp1, *tmp2; |
|
|
char *diffout = NULL; |
|
|
int retval; |
|
|
|
|
|
if (options != NULL && options[0] != '\0') |
|
|
assert (options[0] == '-' && options[1] == 'k'); |
|
|
|
|
|
cvs_output ("RCS file: ", 0); |
|
|
cvs_output (rcs->print_path, 0); |
|
|
cvs_output ("\n", 1); |
|
|
|
|
|
/* Calculate numeric revision numbers from rev1 and rev2 (may be |
|
|
symbolic). |
|
|
FIXME - No they can't. Both calls to RCS_merge are passing in |
|
|
numeric revisions. */ |
|
|
xrev1 = RCS_gettag (rcs, rev1, 0, NULL); |
|
|
xrev2 = RCS_gettag (rcs, rev2, 0, NULL); |
|
|
assert (xrev1 && xrev2); |
|
|
|
|
|
/* Check out chosen revisions. The error message when RCS_checkout |
|
|
fails is not very informative -- it is taken verbatim from RCS 5.7, |
|
|
and relies on RCS_checkout saying something intelligent upon failure. */ |
|
|
cvs_output ("retrieving revision ", 0); |
|
|
cvs_output (xrev1, 0); |
|
|
cvs_output ("\n", 1); |
|
|
|
|
|
tmp1 = cvs_temp_name(); |
|
|
if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, NULL, NULL)) |
|
|
{ |
|
|
cvs_outerr ("rcsmerge: co failed\n", 0); |
|
|
exit (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
cvs_output ("retrieving revision ", 0); |
|
|
cvs_output (xrev2, 0); |
|
|
cvs_output ("\n", 1); |
|
|
|
|
|
tmp2 = cvs_temp_name(); |
|
|
if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, NULL, NULL)) |
|
|
{ |
|
|
cvs_outerr ("rcsmerge: co failed\n", 0); |
|
|
exit (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
/* Merge changes. */ |
|
|
cvs_output ("Merging differences between ", 0); |
|
|
cvs_output (xrev1, 0); |
|
|
cvs_output (" and ", 0); |
|
|
cvs_output (xrev2, 0); |
|
|
cvs_output (" into ", 0); |
|
|
cvs_output (workfile, 0); |
|
|
cvs_output ("\n", 1); |
|
|
|
|
|
/* Remember that the first word in the `call_diff_setup' string is used now |
|
|
only for diagnostic messages -- CVS no longer forks to run diff3. */ |
|
|
diffout = cvs_temp_name(); |
|
|
call_diff_setup ("diff3", 0, NULL); |
|
|
call_diff_add_arg ("-E"); |
|
|
call_diff_add_arg ("-am"); |
|
|
|
|
|
call_diff_add_arg ("-L"); |
|
|
call_diff_add_arg (workfile); |
|
|
call_diff_add_arg ("-L"); |
|
|
call_diff_add_arg (xrev1); |
|
|
call_diff_add_arg ("-L"); |
|
|
call_diff_add_arg (xrev2); |
|
|
|
|
|
call_diff_add_arg ("--"); |
|
|
call_diff_add_arg (workfile); |
|
|
call_diff_add_arg (tmp1); |
|
|
call_diff_add_arg (tmp2); |
|
|
|
|
|
retval = call_diff3 (diffout); |
|
|
|
|
|
if (retval == 1) |
|
|
cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0); |
|
|
else if (retval == 2) |
|
|
exit (EXIT_FAILURE); |
|
|
|
|
|
if (diffout) |
|
|
copy_file (diffout, workfile); |
|
|
|
|
|
/* Clean up. */ |
|
|
{ |
|
|
int save_noexec = noexec; |
|
|
noexec = 0; |
|
|
if (unlink_file (tmp1) < 0) |
|
|
{ |
|
|
if (!existence_error (errno)) |
|
|
error (0, errno, "cannot remove temp file %s", tmp1); |
|
|
} |
|
|
free (tmp1); |
|
|
if (unlink_file (tmp2) < 0) |
|
|
{ |
|
|
if (!existence_error (errno)) |
|
|
error (0, errno, "cannot remove temp file %s", tmp2); |
|
|
} |
|
|
free (tmp2); |
|
|
if (diffout) |
|
|
{ |
|
|
if (unlink_file (diffout) < 0) |
|
|
{ |
|
|
if (!existence_error (errno)) |
|
|
error (0, errno, "cannot remove temp file %s", diffout); |
|
|
} |
|
|
free (diffout); |
|
|
} |
|
|
free (xrev1); |
|
|
free (xrev2); |
|
|
noexec = save_noexec; |
|
|
} |
|
77 |
|
|
|
return retval; |
|
|
} |
|
78 |
|
|
79 |
/* Diff revisions and/or files. OPTS controls the format of the diff |
/* Diff revisions and/or files. OPTS controls the format of the diff |
80 |
(it contains options such as "-w -c", &c), or "" for the default. |
(it contains options such as "-w -c", &c), or "" for the default. |