summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/patch.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/patch.c')
-rw-r--r--contrib/cvs/src/patch.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/contrib/cvs/src/patch.c b/contrib/cvs/src/patch.c
new file mode 100644
index 0000000..65f5051
--- /dev/null
+++ b/contrib/cvs/src/patch.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include "cvs.h"
+#include "getline.h"
+
+static RETSIGTYPE patch_cleanup PROTO((void));
+static Dtype patch_dirproc PROTO ((void *callerdat, const char *dir,
+ const char *repos, const 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] [-k kopt]\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-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
+ "\t-k kopt\tSpecify keyword expansion mode.\n",
+ "\t-D date\tDate.\n",
+ "\t-r rev\tRevision - symbolic or numeric.\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':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_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, local, 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);
+ free (where);
+ return 1;
+ }
+
+ 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,
+ repository);
+ rev1_validated = 1;
+ }
+ if (rev2 != NULL && !rev2_validated)
+ {
+ tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
+ repository);
+ 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, repository);
+ free (repository);
+ 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;
+ char *rcs_orig = 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;
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+
+ line1 = NULL;
+ line1_chars_allocated = 0;
+ line2 = NULL;
+ line2_chars_allocated = 0;
+ vers_tag = vers_head = NULL;
+
+ /* find the parsed rcs file */
+ if ((rcsfile = finfo->rcs) == NULL)
+ {
+ ret = 1;
+ goto out2;
+ }
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ isattic = 1;
+
+ rcs_orig = 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) ||
+ (vers_tag != NULL && vers_head != NULL &&
+ strcmp (vers_head, vers_tag) == 0))
+ {
+ /* Nothing known about specified revs or
+ * not changed between releases.
+ */
+ ret = 0;
+ goto out2;
+ }
+
+ if( patch_short && ( vers_tag == NULL || vers_head == NULL ) )
+ {
+ /* For adds & removes with a short patch requested, we can print our
+ * error message now and get out.
+ */
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ if (vers_tag == NULL)
+ {
+ cvs_output( " is new; ", 0 );
+ cvs_output( rev2 ? rev2 : date2 ? date2 : "current", 0 );
+ cvs_output( " revision ", 0 );
+ cvs_output (vers_head, 0);
+ cvs_output ("\n", 1);
+ }
+ else
+ {
+ cvs_output( " is removed; ", 0 );
+ cvs_output( rev1 ? rev1 : date1, 0 );
+ cvs_output( " revision ", 0 );
+ cvs_output( vers_tag, 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 ? tmpfile1 : "(null)");
+ 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 ? tmpfile2 : "(null)");
+ 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 ? tmpfile3 : "(null)");
+ 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);
+ }
+
+ if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
+ else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
+ switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
+ 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, unless the
+ * user wanted a short patch. In that case, just output the short
+ * message.
+ */
+ if( patch_short )
+ {
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ 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 out;
+ }
+
+ /* Output an "Index:" line for patch to use */
+ cvs_output ("Index: ", 0);
+ cvs_output (finfo->fullname, 0);
+ cvs_output ("\n", 1);
+
+ /* Now the munging. */
+ 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 (tmpfile1 != NULL)
+ {
+ if (CVS_UNLINK (tmpfile1) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile1);
+ free (tmpfile1);
+ tmpfile1 = NULL;
+ }
+ if (tmpfile2 != NULL)
+ {
+ if (CVS_UNLINK (tmpfile2) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile2);
+ free (tmpfile2);
+ tmpfile2 = NULL;
+ }
+ if (tmpfile3 != NULL)
+ {
+ if (CVS_UNLINK (tmpfile3) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile3);
+ free (tmpfile3);
+ tmpfile3 = NULL;
+ }
+
+ if (dargc)
+ {
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+ }
+
+ out2:
+ if (vers_tag != NULL)
+ free (vers_tag);
+ if (vers_head != NULL)
+ free (vers_head);
+ if (rcs_orig)
+ free (rcs_orig);
+ return ret;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+patch_dirproc (callerdat, dir, repos, update_dir, entries)
+ void *callerdat;
+ const char *dir;
+ const char *repos;
+ const 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;
+}
OpenPOWER on IntegriCloud