#include "cvs.h"
#include <assert.h>
#include <stdio.h>
#include "diffrun.h"
static void RCS_output_diff_options PROTO ((char *, char *, char *, char *));
static char **call_diff_argv;
static int call_diff_argc;
static int call_diff_argc_allocated;
static void call_diff_add_arg PROTO ((const char *));
static void call_diff_setup PROTO ((const char *prog));
static int call_diff PROTO ((char *out));
static int call_diff3 PROTO ((char *out));
static void call_diff_write_output PROTO((const char *, size_t));
static void call_diff_flush_output PROTO((void));
static void call_diff_write_stdout PROTO((const char *));
static void call_diff_error PROTO((const char *, const char *, const char *));
static void
call_diff_setup (prog)
const char *prog;
{
char *cp;
int i;
char *call_diff_prog;
for (i = 0; i < call_diff_argc; i++)
{
if (call_diff_argv[i])
{
free (call_diff_argv[i]);
call_diff_argv[i] = (char *) 0;
}
}
call_diff_argc = 0;
call_diff_prog = xstrdup (prog);
for (cp = strtok (call_diff_prog, " \t");
cp != NULL;
cp = strtok ((char *) NULL, " \t"))
call_diff_add_arg (cp);
free (call_diff_prog);
}
static void
call_diff_arg (s)
const char *s;
{
call_diff_add_arg (s);
}
static void
call_diff_add_arg (s)
const char *s;
{
if (call_diff_argc >= call_diff_argc_allocated)
{
call_diff_argc_allocated += 50;
call_diff_argv = (char **)
xrealloc ((char *) call_diff_argv,
call_diff_argc_allocated * sizeof (char **));
}
if (s)
call_diff_argv[call_diff_argc++] = xstrdup (s);
else
call_diff_argv[call_diff_argc] = (char *) 0;
}
static void
call_diff_write_output (text, len)
const char *text;
size_t len;
{
cvs_output (text, len);
}
static void
call_diff_flush_output ()
{
cvs_flushout ();
}
static void
call_diff_write_stdout (text)
const char *text;
{
cvs_output (text, 0);
}
static void
call_diff_error (format, a1, a2)
const char *format;
const char *a1;
const char *a2;
{
error (0, 0, format, a1, a2);
}
static struct diff_callbacks call_diff_stdout_callbacks =
{
call_diff_write_output,
call_diff_flush_output,
call_diff_write_stdout,
call_diff_error
};
static struct diff_callbacks call_diff_file_callbacks =
{
(void (*) PROTO((const char *, size_t))) NULL,
(void (*) PROTO((void))) NULL,
call_diff_write_stdout,
call_diff_error
};
static int
call_diff (out)
char *out;
{
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 (out)
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);
}
int
RCS_merge(rcs, path, workfile, options, rev1, rev2)
RCSNode *rcs;
char *path;
char *workfile;
char *options;
char *rev1;
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->path, 0);
cvs_output ("\n", 1);
xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
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,
(RCSCHECKOUTPROC)0, NULL))
{
cvs_outerr ("rcsmerge: co failed\n", 0);
error_exit();
}
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,
(RCSCHECKOUTPROC)0, NULL))
{
cvs_outerr ("rcsmerge: co failed\n", 0);
error_exit();
}
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);
diffout = cvs_temp_name();
call_diff_setup ("diff3");
call_diff_arg ("-E");
call_diff_arg ("-am");
call_diff_arg ("-L");
call_diff_arg (workfile);
call_diff_arg ("-L");
call_diff_arg (xrev1);
call_diff_arg ("-L");
call_diff_arg (xrev2);
call_diff_arg (workfile);
call_diff_arg (tmp1);
call_diff_arg (tmp2);
retval = call_diff3 (diffout);
if (retval == 1)
cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
else if (retval == 2)
error_exit();
if (diffout)
copy_file (diffout, workfile);
{
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;
}
return retval;
}
int
RCS_exec_rcsdiff (rcsfile, opts, options, rev1, rev2, label1, label2, workfile)
RCSNode *rcsfile;
char *opts;
char *options;
char *rev1;
char *rev2;
char *label1;
char *label2;
char *workfile;
{
char *tmpfile1;
char *tmpfile2;
char *use_file2;
int status, retval;
tmpfile1 = cvs_temp_name ();
tmpfile2 = NULL;
cvs_output ("\
===================================================================\n\
RCS file: ", 0);
cvs_output (rcsfile->path, 0);
cvs_output ("\n", 1);
cvs_output ("retrieving revision ", 0);
cvs_output (rev1, 0);
cvs_output ("\n", 1);
status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
(RCSCHECKOUTPROC)0, NULL);
if (status > 0)
{
retval = status;
goto error_return;
}
else if (status < 0)
{
error (0, errno,
"cannot check out revision %s of %s", rev1, rcsfile->path);
retval = 1;
goto error_return;
}
if (rev2 == NULL)
{
assert (workfile != NULL);
use_file2 = workfile;
}
else
{
tmpfile2 = cvs_temp_name ();
cvs_output ("retrieving revision ", 0);
cvs_output (rev2, 0);
cvs_output ("\n", 1);
status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
tmpfile2, (RCSCHECKOUTPROC)0, NULL);
if (status > 0)
{
retval = status;
goto error_return;
}
else if (status < 0)
{
error (0, errno,
"cannot check out revision %s of %s", rev2, rcsfile->path);
return 1;
}
use_file2 = tmpfile2;
}
RCS_output_diff_options (opts, rev1, rev2, workfile);
status = diff_execv (tmpfile1, use_file2, label1, label2, opts, RUN_TTY);
if (status >= 0)
{
retval = status;
goto error_return;
}
else if (status < 0)
{
error (0, errno,
"cannot diff %s and %s", tmpfile1, use_file2);
retval = 1;
goto error_return;
}
error_return:
{
int save_noexec = noexec;
noexec = 0;
if (unlink_file (tmpfile1) < 0)
{
if (!existence_error (errno))
error (0, errno, "cannot remove temp file %s", tmpfile1);
}
noexec = save_noexec;
}
free (tmpfile1);
if (tmpfile2 != NULL)
{
int save_noexec = noexec;
noexec = 0;
if (unlink_file (tmpfile2) < 0)
{
if (!existence_error (errno))
error (0, errno, "cannot remove temp file %s", tmpfile2);
}
noexec = save_noexec;
free (tmpfile2);
}
return retval;
}
int
diff_exec (file1, file2, options, out)
char *file1;
char *file2;
char *options;
char *out;
{
char *args;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms &&
strcmp (file1, DEVNULL) != 0 &&
strcmp (file2, DEVNULL) != 0)
{
struct stat sb1, sb2;
if (CVS_LSTAT (file1, &sb1) < 0)
error (1, errno, "cannot get file information for %s", file1);
if (CVS_LSTAT (file2, &sb2) < 0)
error (1, errno, "cannot get file information for %s", file2);
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
file1 = DEVNULL;
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
file2 = DEVNULL;
}
#endif
args = xmalloc (strlen (options) + 10);
sprintf (args, "diff %s", options);
call_diff_setup (args);
call_diff_arg (file1);
call_diff_arg (file2);
free (args);
return call_diff (out);
}
int
diff_execv (file1, file2, label1, label2, options, out)
char *file1;
char *file2;
char *label1;
char *label2;
char *options;
char *out;
{
char *args;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms &&
strcmp (file1, DEVNULL) != 0 &&
strcmp (file2, DEVNULL) != 0)
{
struct stat sb1, sb2;
if (CVS_LSTAT (file1, &sb1) < 0)
error (1, errno, "cannot get file information for %s", file1);
if (CVS_LSTAT (file2, &sb2) < 0)
error (1, errno, "cannot get file information for %s", file2);
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
file1 = DEVNULL;
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
file2 = DEVNULL;
}
#endif
args = xmalloc (strlen (options) + 10);
sprintf (args, "diff%s", options);
call_diff_setup (args);
if (label1)
call_diff_arg (label1);
if (label2)
call_diff_arg (label2);
call_diff_arg (file1);
call_diff_arg (file2);
free (args);
return call_diff (out);
}
static void
RCS_output_diff_options (opts, rev1, rev2, workfile)
char *opts;
char *rev1;
char *rev2;
char *workfile;
{
char *tmp;
tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10);
sprintf (tmp, "diff%s -r%s", opts, rev1);
cvs_output (tmp, 0);
free (tmp);
if (rev2)
{
cvs_output (" -r", 3);
cvs_output (rev2, 0);
}
else
{
assert (workfile != NULL);
cvs_output (" ", 1);
cvs_output (workfile, 0);
}
cvs_output ("\n", 1);
}