This is patch.c in view mode; [Download] [Up]
/*
* 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 1.4 kit.
*
* 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 "cvs.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)patch.c 1.57 94/09/30 $";
USE(rcsid)
#endif
static RETSIGTYPE patch_cleanup PROTO((void));
static Dtype patch_dirproc PROTO((char *dir, char *repos, char *update_dir));
static int patch_fileproc PROTO((char *file, char *update_dir, char *repository,
List * entries, List * srcfiles));
static int patch_proc PROTO((int *pargc, 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 int local = 0;
static char *options = NULL;
static char *rev1 = NULL;
static char *rev2 = NULL;
static char *date1 = NULL;
static char *date2 = NULL;
static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
static int unidiff = 0;
static const char *const patch_usage[] =
{
"Usage: %s %s [-fl] [-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-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",
NULL
};
int
patch (argc, argv)
int argc;
char **argv;
{
register int i;
int c;
int err = 0;
DBM *db;
if (argc == -1)
usage (patch_usage);
optind = 1;
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':
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);
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 (client_active)
{
/* 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]);
}
if (fprintf (to_server, "rdiff\n") < 0)
error (1, errno, "writing to server");
return get_responses_and_close ();
}
#endif
/* clean up if we get a signal */
#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, (char *) NULL);
close_module (db);
free (options);
patch_cleanup ();
return (err);
}
/*
* callback proc for doing the real work of patching
*/
/* ARGSUSED */
static char where[PATH_MAX];
static int
patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
mname, msg)
int *pargc;
char **argv;
char *xwhere;
char *mwhere;
char *mfile;
int shorten;
int local_specified;
char *mname;
char *msg;
{
int err = 0;
int which;
char repository[PATH_MAX];
(void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
(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[PATH_MAX];
/* 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 */
(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
{
int i;
/* a file means muck argv */
for (i = 1; i < *pargc; i++)
free (argv[i]);
argv[1] = xstrdup (mfile);
(*pargc) = 2;
}
}
/* cd to the starting repository */
if (chdir (repository) < 0)
{
error (0, errno, "cannot chdir to %s", repository);
return (1);
}
if (force_tag_match)
which = W_REPOS | W_ATTIC;
else
which = W_REPOS;
/* start the recursion processor */
err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
(int (*) ()) NULL, *pargc - 1, argv + 1, local,
which, 0, 1, where, 1, 1);
return (err);
}
/*
* Called to examine a particular RCS file, as appropriate with the options
* that were set above.
*/
/* ARGSUSED */
static int
patch_fileproc (file, update_dir, repository, entries, srcfiles)
char *file;
char *update_dir;
char *repository;
List *entries;
List *srcfiles;
{
struct utimbuf t;
char *vers_tag, *vers_head;
char rcsspace[PATH_MAX];
char *rcs = rcsspace;
Node *p;
RCSNode *rcsfile;
FILE *fp1, *fp2, *fp3;
int ret = 0;
int isattic = 0;
int retcode = 0;
char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
char line1[MAXLINELEN], line2[MAXLINELEN];
char *cp1, *cp2, *commap;
FILE *fp;
/* find the parsed rcs file */
p = findnode (srcfiles, file);
if (p == NULL)
return (1);
rcsfile = (RCSNode *) p->data;
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
isattic = 1;
(void) sprintf (rcs, "%s%s", 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);
if (toptwo_diffs)
{
if (vers_head == NULL)
return (1);
if (!date1)
date1 = xmalloc (50); /* plenty big :-) */
*date1 = '\0';
if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
{
if (!really_quiet)
error (0, 0, "cannot find date in rcs file %s revision %s",
rcs, vers_head);
return (1);
}
}
vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
if (vers_tag == NULL && (vers_head == NULL || isattic))
return (0); /* nothing known about specified revs */
if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
return (0); /* not changed between releases */
if (patch_short)
{
(void) printf ("File ");
if (vers_tag == NULL)
(void) printf ("%s is new; current revision %s\n", rcs, vers_head);
else if (vers_head == NULL)
#ifdef DEATH_SUPPORT
{
(void) printf ("%s is removed; not included in ", rcs);
if (rev2 != NULL)
(void) printf ("release tag %s", rev2);
else if (date2 != NULL)
(void) printf ("release date %s", date2);
else
(void) printf ("current release");
(void) printf ("\n");
}
#else
(void) printf ("%s is removed; not included in release %s\n",
rcs, rev2 ? rev2 : date2);
#endif
else
(void) printf ("%s changed from revision %s to %s\n",
rcs, vers_tag, vers_head);
return (0);
}
if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL)
(void) fclose (fp1);
if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL)
(void) fclose (fp2);
if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL)
(void) fclose (fp3);
if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
{
error (0, 0, "cannot create temporary files");
ret = 1;
goto out;
}
if (vers_tag != NULL)
{
run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_tag);
run_arg (rcsfile->path);
if ((retcode = run_exec (RUN_TTY, tmpfile1, RUN_TTY, RUN_NORMAL)) != 0)
{
if (!really_quiet)
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
"co of revision %s in %s failed", 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)
(void) utime (tmpfile1, &t);
}
else if (toptwo_diffs)
{
ret = 1;
goto out;
}
if (vers_head != NULL)
{
run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_head);
run_arg (rcsfile->path);
if ((retcode = run_exec (RUN_TTY, tmpfile2, RUN_TTY, RUN_NORMAL)) != 0)
{
if (!really_quiet)
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
"co of revision %s in %s failed", vers_head, rcs);
ret = 1;
goto out;
}
if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
(char *) 0, 0)) != -1)
(void) utime (tmpfile2, &t);
}
run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
run_arg (tmpfile1);
run_arg (tmpfile2);
switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
{
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 */
(void) fflush (stdout);
if (update_dir[0])
(void) printf ("Index: %s/%s\n", update_dir, file);
else
(void) printf ("Index: %s\n", file);
(void) fflush (stdout);
fp = open_file (tmpfile3, "r");
if (fgets (line1, sizeof (line1), fp) == NULL ||
fgets (line2, sizeof (line2), fp) == NULL)
{
error (0, errno, "failed to read diff file header %s for %s",
tmpfile3, rcs);
ret = 1;
(void) fclose (fp);
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;
(void) fclose (fp);
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;
(void) fclose (fp);
goto out;
}
}
if (CVSroot != NULL)
(void) sprintf (strippath, "%s/", CVSroot);
else
(void) strcpy (strippath, REPOS_STRIP);
if (strncmp (rcs, strippath, strlen (strippath)) == 0)
rcs += strlen (strippath);
commap = strrchr (rcs, ',');
*commap = '\0';
if (vers_tag != NULL)
{
(void) sprintf (file1, "%s%s%s:%s", update_dir,
update_dir[0] ? "/" : "", rcs, vers_tag);
}
else
{
(void) strcpy (file1, DEVNULL);
}
(void) sprintf (file2, "%s%s%s:%s", update_dir,
update_dir[0] ? "/" : "", rcs,
vers_head ? vers_head : "removed");
if (unidiff)
{
(void) printf ("diff -u %s %s\n", file1, file2);
(void) printf ("--- %s%s+++ ", file1, cp1);
}
else
{
(void) printf ("diff -c %s %s\n", file1, file2);
(void) printf ("*** %s%s--- ", file1, cp1);
}
if (update_dir[0] != '\0')
(void) printf ("%s/", update_dir);
(void) printf ("%s%s", rcs, cp2);
while (fgets (line1, sizeof (line1), fp) != NULL)
(void) printf ("%s", line1);
(void) fclose (fp);
break;
default:
error (0, 0, "diff failed for %s", rcs);
}
out:
(void) unlink_file (tmpfile1);
(void) unlink_file (tmpfile2);
(void) unlink_file (tmpfile3);
return (ret);
}
/*
* Print a warm fuzzy message
*/
/* ARGSUSED */
static Dtype
patch_dirproc (dir, repos, update_dir)
char *dir;
char *repos;
char *update_dir;
{
if (!quiet)
error (0, 0, "Diffing %s", update_dir);
return (R_PROCESS);
}
/*
* Clean up temporary files
*/
static RETSIGTYPE
patch_cleanup ()
{
if (tmpfile1[0] != '\0')
(void) unlink_file (tmpfile1);
if (tmpfile2[0] != '\0')
(void) unlink_file (tmpfile2);
if (tmpfile3[0] != '\0')
(void) unlink_file (tmpfile3);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.