mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-19 10:53:58 +00:00
801 lines
20 KiB
C
801 lines
20 KiB
C
/*
|
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
|
* Copyright (c) 1989-1992, Brian Berliner
|
|
*
|
|
* You may distribute under the terms of the GNU General Public License as
|
|
* specified in the README file that comes with the CVS source distribution.
|
|
*
|
|
* Patch
|
|
*
|
|
* Create a Larry Wall format "patch" file between a previous release and the
|
|
* current head of a module, or between two releases. Can specify the
|
|
* release as either a date or a revision number.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include "cvs.h"
|
|
#include "getline.h"
|
|
|
|
static RETSIGTYPE patch_cleanup PROTO((void));
|
|
static Dtype patch_dirproc PROTO ((void *callerdat, char *dir,
|
|
char *repos, char *update_dir,
|
|
List *entries));
|
|
static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
|
static int patch_proc PROTO((int argc, char **argv, char *xwhere,
|
|
char *mwhere, char *mfile, int shorten,
|
|
int local_specified, char *mname, char *msg));
|
|
|
|
static int force_tag_match = 1;
|
|
static int patch_short = 0;
|
|
static int toptwo_diffs = 0;
|
|
static char *options = NULL;
|
|
static char *rev1 = NULL;
|
|
static int rev1_validated = 0;
|
|
static char *rev2 = NULL;
|
|
static int rev2_validated = 0;
|
|
static char *date1 = NULL;
|
|
static char *date2 = NULL;
|
|
static char *tmpfile1 = NULL;
|
|
static char *tmpfile2 = NULL;
|
|
static char *tmpfile3 = NULL;
|
|
static int unidiff = 0;
|
|
|
|
static const char *const patch_usage[] =
|
|
{
|
|
"Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n",
|
|
" -r rev|-D date [-r rev2 | -D date2] modules...\n",
|
|
"\t-f\tForce a head revision match if tag/date not found.\n",
|
|
"\t-l\tLocal directory only, not recursive\n",
|
|
"\t-R\tProcess directories recursively.\n",
|
|
"\t-c\tContext diffs (default)\n",
|
|
"\t-u\tUnidiff format.\n",
|
|
"\t-s\tShort patch - one liner per file.\n",
|
|
"\t-t\tTop two diffs - last change made to the file.\n",
|
|
"\t-D date\tDate.\n",
|
|
"\t-r rev\tRevision - symbolic or numeric.\n",
|
|
"\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
|
|
"(Specify the --help global option for a list of other help options)\n",
|
|
NULL
|
|
};
|
|
|
|
int
|
|
patch (argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
register int i;
|
|
int local = 0;
|
|
int c;
|
|
int err = 0;
|
|
DBM *db;
|
|
|
|
if (argc == -1)
|
|
usage (patch_usage);
|
|
|
|
optind = 0;
|
|
while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'Q':
|
|
case 'q':
|
|
#ifdef SERVER_SUPPORT
|
|
/* The CVS 1.5 client sends these options (in addition to
|
|
Global_option requests), so we must ignore them. */
|
|
if (!server_active)
|
|
#endif
|
|
error (1, 0,
|
|
"-q or -Q must be specified before \"%s\"",
|
|
command_name);
|
|
break;
|
|
case 'f':
|
|
force_tag_match = 0;
|
|
break;
|
|
case 'l':
|
|
local = 1;
|
|
break;
|
|
case 'R':
|
|
local = 0;
|
|
break;
|
|
case 't':
|
|
toptwo_diffs = 1;
|
|
break;
|
|
case 's':
|
|
patch_short = 1;
|
|
break;
|
|
case 'D':
|
|
if (rev2 != NULL || date2 != NULL)
|
|
error (1, 0,
|
|
"no more than two revisions/dates can be specified");
|
|
if (rev1 != NULL || date1 != NULL)
|
|
date2 = Make_Date (optarg);
|
|
else
|
|
date1 = Make_Date (optarg);
|
|
break;
|
|
case 'r':
|
|
if (rev2 != NULL || date2 != NULL)
|
|
error (1, 0,
|
|
"no more than two revisions/dates can be specified");
|
|
if (rev1 != NULL || date1 != NULL)
|
|
rev2 = optarg;
|
|
else
|
|
rev1 = optarg;
|
|
break;
|
|
case 'k':
|
|
if (options)
|
|
free (options);
|
|
options = RCS_check_kflag (optarg);
|
|
break;
|
|
case 'V':
|
|
/* This option is pretty seriously broken:
|
|
1. It is not clear what it does (does it change keyword
|
|
expansion behavior? If so, how? Or does it have
|
|
something to do with what version of RCS we are using?
|
|
Or the format we write RCS files in?).
|
|
2. Because both it and -k use the options variable,
|
|
specifying both -V and -k doesn't work.
|
|
3. At least as of CVS 1.9, it doesn't work (failed
|
|
assertion in RCS_checkout where it asserts that options
|
|
starts with -k). Few people seem to be complaining.
|
|
In the future (perhaps the near future), I have in mind
|
|
removing it entirely, and updating NEWS and cvs.texinfo,
|
|
but in case it is a good idea to give people more time
|
|
to complain if they would miss it, I'll just add this
|
|
quick and dirty error message for now. */
|
|
error (1, 0,
|
|
"the -V option is obsolete and should not be used");
|
|
#if 0
|
|
if (atoi (optarg) <= 0)
|
|
error (1, 0, "must specify a version number to -V");
|
|
if (options)
|
|
free (options);
|
|
options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */
|
|
(void) sprintf (options, "-V%s", optarg);
|
|
#endif
|
|
break;
|
|
case 'u':
|
|
unidiff = 1; /* Unidiff */
|
|
break;
|
|
case 'c': /* Context diff */
|
|
unidiff = 0;
|
|
break;
|
|
case '?':
|
|
default:
|
|
usage (patch_usage);
|
|
break;
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
/* Sanity checks */
|
|
if (argc < 1)
|
|
usage (patch_usage);
|
|
|
|
if (toptwo_diffs && patch_short)
|
|
error (1, 0, "-t and -s options are mutually exclusive");
|
|
if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
|
|
rev1 != NULL || rev2 != NULL))
|
|
error (1, 0, "must not specify revisions/dates with -t option!");
|
|
|
|
if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
|
|
rev1 == NULL && rev2 == NULL))
|
|
error (1, 0, "must specify at least one revision/date!");
|
|
if (date1 != NULL && date2 != NULL)
|
|
if (RCS_datecmp (date1, date2) >= 0)
|
|
error (1, 0, "second date must come after first date!");
|
|
|
|
/* if options is NULL, make it a NULL string */
|
|
if (options == NULL)
|
|
options = xstrdup ("");
|
|
|
|
#ifdef CLIENT_SUPPORT
|
|
if (current_parsed_root->isremote)
|
|
{
|
|
/* We're the client side. Fire up the remote server. */
|
|
start_server ();
|
|
|
|
ign_setup ();
|
|
|
|
if (local)
|
|
send_arg("-l");
|
|
if (!force_tag_match)
|
|
send_arg("-f");
|
|
if (toptwo_diffs)
|
|
send_arg("-t");
|
|
if (patch_short)
|
|
send_arg("-s");
|
|
if (unidiff)
|
|
send_arg("-u");
|
|
|
|
if (rev1)
|
|
option_with_arg ("-r", rev1);
|
|
if (date1)
|
|
client_senddate (date1);
|
|
if (rev2)
|
|
option_with_arg ("-r", rev2);
|
|
if (date2)
|
|
client_senddate (date2);
|
|
if (options[0] != '\0')
|
|
send_arg (options);
|
|
|
|
{
|
|
int i;
|
|
for (i = 0; i < argc; ++i)
|
|
send_arg (argv[i]);
|
|
}
|
|
|
|
send_to_server ("rdiff\012", 0);
|
|
return get_responses_and_close ();
|
|
}
|
|
#endif
|
|
|
|
/* clean up if we get a signal */
|
|
#ifdef SIGABRT
|
|
(void) SIG_register (SIGABRT, patch_cleanup);
|
|
#endif
|
|
#ifdef SIGHUP
|
|
(void) SIG_register (SIGHUP, patch_cleanup);
|
|
#endif
|
|
#ifdef SIGINT
|
|
(void) SIG_register (SIGINT, patch_cleanup);
|
|
#endif
|
|
#ifdef SIGQUIT
|
|
(void) SIG_register (SIGQUIT, patch_cleanup);
|
|
#endif
|
|
#ifdef SIGPIPE
|
|
(void) SIG_register (SIGPIPE, patch_cleanup);
|
|
#endif
|
|
#ifdef SIGTERM
|
|
(void) SIG_register (SIGTERM, patch_cleanup);
|
|
#endif
|
|
|
|
db = open_module ();
|
|
for (i = 0; i < argc; i++)
|
|
err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
|
|
(char *) NULL, 0, 0, 0, 0, (char *) NULL);
|
|
close_module (db);
|
|
free (options);
|
|
patch_cleanup ();
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* callback proc for doing the real work of patching
|
|
*/
|
|
/* ARGSUSED */
|
|
static int
|
|
patch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
|
|
mname, msg)
|
|
int argc;
|
|
char **argv;
|
|
char *xwhere;
|
|
char *mwhere;
|
|
char *mfile;
|
|
int shorten;
|
|
int local_specified;
|
|
char *mname;
|
|
char *msg;
|
|
{
|
|
char *myargv[2];
|
|
int err = 0;
|
|
int which;
|
|
char *repository;
|
|
char *where;
|
|
|
|
repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
|
|
+ (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
|
|
(void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
|
|
where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
|
|
+ 1);
|
|
(void) strcpy (where, argv[0]);
|
|
|
|
/* if mfile isn't null, we need to set up to do only part of the module */
|
|
if (mfile != NULL)
|
|
{
|
|
char *cp;
|
|
char *path;
|
|
|
|
/* if the portion of the module is a path, put the dir part on repos */
|
|
if ((cp = strrchr (mfile, '/')) != NULL)
|
|
{
|
|
*cp = '\0';
|
|
(void) strcat (repository, "/");
|
|
(void) strcat (repository, mfile);
|
|
(void) strcat (where, "/");
|
|
(void) strcat (where, mfile);
|
|
mfile = cp + 1;
|
|
}
|
|
|
|
/* take care of the rest */
|
|
path = xmalloc (strlen (repository) + strlen (mfile) + 2);
|
|
(void) sprintf (path, "%s/%s", repository, mfile);
|
|
if (isdir (path))
|
|
{
|
|
/* directory means repository gets the dir tacked on */
|
|
(void) strcpy (repository, path);
|
|
(void) strcat (where, "/");
|
|
(void) strcat (where, mfile);
|
|
}
|
|
else
|
|
{
|
|
myargv[0] = argv[0];
|
|
myargv[1] = mfile;
|
|
argc = 2;
|
|
argv = myargv;
|
|
}
|
|
free (path);
|
|
}
|
|
|
|
/* cd to the starting repository */
|
|
if ( CVS_CHDIR (repository) < 0)
|
|
{
|
|
error (0, errno, "cannot chdir to %s", repository);
|
|
free (repository);
|
|
return (1);
|
|
}
|
|
free (repository);
|
|
|
|
if (force_tag_match)
|
|
which = W_REPOS | W_ATTIC;
|
|
else
|
|
which = W_REPOS;
|
|
|
|
if (rev1 != NULL && !rev1_validated)
|
|
{
|
|
tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0, NULL);
|
|
rev1_validated = 1;
|
|
}
|
|
if (rev2 != NULL && !rev2_validated)
|
|
{
|
|
tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0, NULL);
|
|
rev2_validated = 1;
|
|
}
|
|
|
|
/* start the recursion processor */
|
|
err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
|
|
(DIRLEAVEPROC) NULL, NULL,
|
|
argc - 1, argv + 1, local_specified,
|
|
which, 0, CVS_LOCK_READ, where, 1);
|
|
free (where);
|
|
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* Called to examine a particular RCS file, as appropriate with the options
|
|
* that were set above.
|
|
*/
|
|
/* ARGSUSED */
|
|
static int
|
|
patch_fileproc (callerdat, finfo)
|
|
void *callerdat;
|
|
struct file_info *finfo;
|
|
{
|
|
struct utimbuf t;
|
|
char *vers_tag, *vers_head;
|
|
char *rcs = NULL;
|
|
RCSNode *rcsfile;
|
|
FILE *fp1, *fp2, *fp3;
|
|
int ret = 0;
|
|
int isattic = 0;
|
|
int retcode = 0;
|
|
char *file1;
|
|
char *file2;
|
|
char *strippath;
|
|
char *line1, *line2;
|
|
size_t line1_chars_allocated;
|
|
size_t line2_chars_allocated;
|
|
char *cp1, *cp2;
|
|
FILE *fp;
|
|
int line_length;
|
|
|
|
line1 = NULL;
|
|
line1_chars_allocated = 0;
|
|
line2 = NULL;
|
|
line2_chars_allocated = 0;
|
|
|
|
/* find the parsed rcs file */
|
|
if ((rcsfile = finfo->rcs) == NULL)
|
|
{
|
|
ret = 1;
|
|
goto out2;
|
|
}
|
|
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
|
|
isattic = 1;
|
|
|
|
rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
|
|
(void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
|
|
|
|
/* if vers_head is NULL, may have been removed from the release */
|
|
if (isattic && rev2 == NULL && date2 == NULL)
|
|
vers_head = NULL;
|
|
else
|
|
{
|
|
vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
|
|
(int *) NULL);
|
|
if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
|
|
{
|
|
free (vers_head);
|
|
vers_head = NULL;
|
|
}
|
|
}
|
|
|
|
if (toptwo_diffs)
|
|
{
|
|
if (vers_head == NULL)
|
|
{
|
|
ret = 1;
|
|
goto out2;
|
|
}
|
|
|
|
if (!date1)
|
|
date1 = xmalloc (MAXDATELEN);
|
|
*date1 = '\0';
|
|
if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
|
|
{
|
|
if (!really_quiet)
|
|
error (0, 0, "cannot find date in rcs file %s revision %s",
|
|
rcs, vers_head);
|
|
ret = 1;
|
|
goto out2;
|
|
}
|
|
}
|
|
vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
|
|
(int *) NULL);
|
|
if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
|
|
{
|
|
free (vers_tag);
|
|
vers_tag = NULL;
|
|
}
|
|
|
|
if (vers_tag == NULL && vers_head == NULL)
|
|
{
|
|
/* Nothing known about specified revs. */
|
|
ret = 0;
|
|
goto out2;
|
|
}
|
|
|
|
if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
|
|
{
|
|
/* Not changed between releases. */
|
|
ret = 0;
|
|
goto out2;
|
|
}
|
|
|
|
if (patch_short)
|
|
{
|
|
cvs_output ("File ", 0);
|
|
cvs_output (finfo->fullname, 0);
|
|
if (vers_tag == NULL)
|
|
{
|
|
cvs_output (" is new; current revision ", 0);
|
|
cvs_output (vers_head, 0);
|
|
cvs_output ("\n", 1);
|
|
}
|
|
else if (vers_head == NULL)
|
|
{
|
|
cvs_output (" is removed; not included in ", 0);
|
|
if (rev2 != NULL)
|
|
{
|
|
cvs_output ("release tag ", 0);
|
|
cvs_output (rev2, 0);
|
|
}
|
|
else if (date2 != NULL)
|
|
{
|
|
cvs_output ("release date ", 0);
|
|
cvs_output (date2, 0);
|
|
}
|
|
else
|
|
cvs_output ("current release", 0);
|
|
cvs_output ("\n", 1);
|
|
}
|
|
else
|
|
{
|
|
cvs_output (" changed from revision ", 0);
|
|
cvs_output (vers_tag, 0);
|
|
cvs_output (" to ", 0);
|
|
cvs_output (vers_head, 0);
|
|
cvs_output ("\n", 1);
|
|
}
|
|
ret = 0;
|
|
goto out2;
|
|
}
|
|
|
|
/* Create 3 empty files. I'm not really sure there is any advantage
|
|
* to doing so now rather than just waiting until later.
|
|
*
|
|
* There is - cvs_temp_file opens the file so that it can guarantee that
|
|
* we have exclusive write access to the file. Unfortunately we spoil that
|
|
* by closing it and reopening it again. Of course any better solution
|
|
* requires that the RCS functions accept open file pointers rather than
|
|
* simple file names.
|
|
*/
|
|
if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
|
|
{
|
|
error (0, errno, "cannot create temporary file %s", tmpfile1);
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
else
|
|
if (fclose (fp1) < 0)
|
|
error (0, errno, "warning: cannot close %s", tmpfile1);
|
|
if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
|
|
{
|
|
error (0, errno, "cannot create temporary file %s", tmpfile2);
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
else
|
|
if (fclose (fp2) < 0)
|
|
error (0, errno, "warning: cannot close %s", tmpfile2);
|
|
if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
|
|
{
|
|
error (0, errno, "cannot create temporary file %s", tmpfile3);
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
else
|
|
if (fclose (fp3) < 0)
|
|
error (0, errno, "warning: cannot close %s", tmpfile3);
|
|
|
|
if (vers_tag != NULL)
|
|
{
|
|
retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag,
|
|
rev1, options, tmpfile1,
|
|
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
|
if (retcode != 0)
|
|
{
|
|
error (0, 0,
|
|
"cannot check out revision %s of %s", vers_tag, rcs);
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
memset ((char *) &t, 0, sizeof (t));
|
|
if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
|
|
(char *) 0, 0)) != -1)
|
|
/* I believe this timestamp only affects the dates in our diffs,
|
|
and therefore should be on the server, not the client. */
|
|
(void) utime (tmpfile1, &t);
|
|
}
|
|
else if (toptwo_diffs)
|
|
{
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
if (vers_head != NULL)
|
|
{
|
|
retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head,
|
|
rev2, options, tmpfile2,
|
|
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
|
if (retcode != 0)
|
|
{
|
|
error (0, 0,
|
|
"cannot check out revision %s of %s", vers_head, rcs);
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
|
|
(char *) 0, 0)) != -1)
|
|
/* I believe this timestamp only affects the dates in our diffs,
|
|
and therefore should be on the server, not the client. */
|
|
(void) utime (tmpfile2, &t);
|
|
}
|
|
|
|
switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, unidiff ? "-u" : "-c", tmpfile3))
|
|
{
|
|
case -1: /* fork/wait failure */
|
|
error (1, errno, "fork for diff failed on %s", rcs);
|
|
break;
|
|
case 0: /* nothing to do */
|
|
break;
|
|
case 1:
|
|
/*
|
|
* The two revisions are really different, so read the first two
|
|
* lines of the diff output file, and munge them to include more
|
|
* reasonable file names that "patch" will understand.
|
|
*/
|
|
|
|
/* Output an "Index:" line for patch to use */
|
|
cvs_output ("Index: ", 0);
|
|
cvs_output (finfo->fullname, 0);
|
|
cvs_output ("\n", 1);
|
|
|
|
fp = open_file (tmpfile3, "r");
|
|
if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
|
|
getline (&line2, &line2_chars_allocated, fp) < 0)
|
|
{
|
|
if (feof (fp))
|
|
error (0, 0, "\
|
|
failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
|
|
else
|
|
error (0, errno,
|
|
"failed to read diff file header %s for %s",
|
|
tmpfile3, rcs);
|
|
ret = 1;
|
|
if (fclose (fp) < 0)
|
|
error (0, errno, "error closing %s", tmpfile3);
|
|
goto out;
|
|
}
|
|
if (!unidiff)
|
|
{
|
|
if (strncmp (line1, "*** ", 4) != 0 ||
|
|
strncmp (line2, "--- ", 4) != 0 ||
|
|
(cp1 = strchr (line1, '\t')) == NULL ||
|
|
(cp2 = strchr (line2, '\t')) == NULL)
|
|
{
|
|
error (0, 0, "invalid diff header for %s", rcs);
|
|
ret = 1;
|
|
if (fclose (fp) < 0)
|
|
error (0, errno, "error closing %s", tmpfile3);
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (strncmp (line1, "--- ", 4) != 0 ||
|
|
strncmp (line2, "+++ ", 4) != 0 ||
|
|
(cp1 = strchr (line1, '\t')) == NULL ||
|
|
(cp2 = strchr (line2, '\t')) == NULL)
|
|
{
|
|
error (0, 0, "invalid unidiff header for %s", rcs);
|
|
ret = 1;
|
|
if (fclose (fp) < 0)
|
|
error (0, errno, "error closing %s", tmpfile3);
|
|
goto out;
|
|
}
|
|
}
|
|
assert (current_parsed_root != NULL);
|
|
assert (current_parsed_root->directory != NULL);
|
|
{
|
|
strippath = xmalloc (strlen (current_parsed_root->directory) + 2);
|
|
(void) sprintf (strippath, "%s/", current_parsed_root->directory);
|
|
}
|
|
/*else
|
|
strippath = xstrdup (REPOS_STRIP); */
|
|
if (strncmp (rcs, strippath, strlen (strippath)) == 0)
|
|
rcs += strlen (strippath);
|
|
free (strippath);
|
|
if (vers_tag != NULL)
|
|
{
|
|
file1 = xmalloc (strlen (finfo->fullname)
|
|
+ strlen (vers_tag)
|
|
+ 10);
|
|
(void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
|
|
}
|
|
else
|
|
{
|
|
file1 = xstrdup (DEVNULL);
|
|
}
|
|
file2 = xmalloc (strlen (finfo->fullname)
|
|
+ (vers_head != NULL ? strlen (vers_head) : 10)
|
|
+ 10);
|
|
(void) sprintf (file2, "%s:%s", finfo->fullname,
|
|
vers_head ? vers_head : "removed");
|
|
|
|
/* Note that the string "diff" is specified by POSIX (for -c)
|
|
and is part of the diff output format, not the name of a
|
|
program. */
|
|
if (unidiff)
|
|
{
|
|
cvs_output ("diff -u ", 0);
|
|
cvs_output (file1, 0);
|
|
cvs_output (" ", 1);
|
|
cvs_output (file2, 0);
|
|
cvs_output ("\n", 1);
|
|
|
|
cvs_output ("--- ", 0);
|
|
cvs_output (file1, 0);
|
|
cvs_output (cp1, 0);
|
|
cvs_output ("+++ ", 0);
|
|
}
|
|
else
|
|
{
|
|
cvs_output ("diff -c ", 0);
|
|
cvs_output (file1, 0);
|
|
cvs_output (" ", 1);
|
|
cvs_output (file2, 0);
|
|
cvs_output ("\n", 1);
|
|
|
|
cvs_output ("*** ", 0);
|
|
cvs_output (file1, 0);
|
|
cvs_output (cp1, 0);
|
|
cvs_output ("--- ", 0);
|
|
}
|
|
|
|
cvs_output (finfo->fullname, 0);
|
|
cvs_output (cp2, 0);
|
|
|
|
/* spew the rest of the diff out */
|
|
while ((line_length
|
|
= getline (&line1, &line1_chars_allocated, fp))
|
|
>= 0)
|
|
cvs_output (line1, 0);
|
|
if (line_length < 0 && !feof (fp))
|
|
error (0, errno, "cannot read %s", tmpfile3);
|
|
|
|
if (fclose (fp) < 0)
|
|
error (0, errno, "cannot close %s", tmpfile3);
|
|
free (file1);
|
|
free (file2);
|
|
break;
|
|
default:
|
|
error (0, 0, "diff failed for %s", finfo->fullname);
|
|
}
|
|
out:
|
|
if (line1)
|
|
free (line1);
|
|
if (line2)
|
|
free (line2);
|
|
if (CVS_UNLINK (tmpfile1) < 0)
|
|
error (0, errno, "cannot unlink %s", tmpfile1);
|
|
if (CVS_UNLINK (tmpfile2) < 0)
|
|
error (0, errno, "cannot unlink %s", tmpfile2);
|
|
if (CVS_UNLINK (tmpfile3) < 0)
|
|
error (0, errno, "cannot unlink %s", tmpfile3);
|
|
free (tmpfile1);
|
|
free (tmpfile2);
|
|
free (tmpfile3);
|
|
tmpfile1 = tmpfile2 = tmpfile3 = NULL;
|
|
|
|
out2:
|
|
if (vers_tag != NULL)
|
|
free (vers_tag);
|
|
if (vers_head != NULL)
|
|
free (vers_head);
|
|
if (rcs != NULL)
|
|
free (rcs);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Print a warm fuzzy message
|
|
*/
|
|
/* ARGSUSED */
|
|
static Dtype
|
|
patch_dirproc (callerdat, dir, repos, update_dir, entries)
|
|
void *callerdat;
|
|
char *dir;
|
|
char *repos;
|
|
char *update_dir;
|
|
List *entries;
|
|
{
|
|
if (!quiet)
|
|
error (0, 0, "Diffing %s", update_dir);
|
|
return (R_PROCESS);
|
|
}
|
|
|
|
/*
|
|
* Clean up temporary files
|
|
*/
|
|
static RETSIGTYPE
|
|
patch_cleanup ()
|
|
{
|
|
/* Note that the checks for existence_error are because we are
|
|
called from a signal handler, without SIG_begincrsect, so
|
|
we don't know whether the files got created. */
|
|
|
|
if (tmpfile1 != NULL)
|
|
{
|
|
if (unlink_file (tmpfile1) < 0
|
|
&& !existence_error (errno))
|
|
error (0, errno, "cannot remove %s", tmpfile1);
|
|
free (tmpfile1);
|
|
}
|
|
if (tmpfile2 != NULL)
|
|
{
|
|
if (unlink_file (tmpfile2) < 0
|
|
&& !existence_error (errno))
|
|
error (0, errno, "cannot remove %s", tmpfile2);
|
|
free (tmpfile2);
|
|
}
|
|
if (tmpfile3 != NULL)
|
|
{
|
|
if (unlink_file (tmpfile3) < 0
|
|
&& !existence_error (errno))
|
|
error (0, errno, "cannot remove %s", tmpfile3);
|
|
free (tmpfile3);
|
|
}
|
|
tmpfile1 = tmpfile2 = tmpfile3 = NULL;
|
|
}
|