diff options
Diffstat (limited to 'gnu/usr.bin/cvs/cvs/patch.c')
| -rw-r--r-- | gnu/usr.bin/cvs/cvs/patch.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/gnu/usr.bin/cvs/cvs/patch.c b/gnu/usr.bin/cvs/cvs/patch.c new file mode 100644 index 000000000000..11134a714989 --- /dev/null +++ b/gnu/usr.bin/cvs/cvs/patch.c @@ -0,0 +1,523 @@ +/* + * 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.3 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 char rcsid[] = "@(#)patch.c 1.50 92/04/10"; + +#endif + +#if __STDC__ +static SIGTYPE patch_cleanup (void); +static Dtype patch_dirproc (char *dir, char *repos, char *update_dir); +static int patch_fileproc (char *file, char *update_dir, char *repository, + List * entries, List * srcfiles); +static int patch_proc (int *pargc, char *argv[], char *xwhere, + char *mwhere, char *mfile, int shorten, + int local_specified, char *mname, char *msg); +#else +static int patch_proc (); +static int patch_fileproc (); +static Dtype patch_dirproc (); +static SIGTYPE patch_cleanup (); +#endif /* __STDC__ */ + +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 char *patch_usage[] = +{ + "Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n", + " -r rev|-D date [-r rev2 | -D date2] modules...\n", + "\t-Q\tReally quiet.\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 = gnu_getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1) + { + switch (c) + { + case 'Q': + really_quiet = 1; + /* FALL THROUGH */ + case 'q': + quiet = 1; + 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 (""); + + /* clean up if we get a signal */ + (void) SIG_register (SIGHUP, patch_cleanup); + (void) SIG_register (SIGINT, patch_cleanup); + (void) SIG_register (SIGQUIT, patch_cleanup); + (void) SIG_register (SIGPIPE, patch_cleanup); + (void) SIG_register (SIGTERM, patch_cleanup); + + 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 = rindex (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); + + 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; +{ + 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) + (void) printf ("%s is removed; not included in release %s\n", + rcs, rev2 ? rev2 : date2); + 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; + } + } + 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; + } + } + 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. + */ + 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 = index (line1, '\t')) == NULL || + (cp2 = index (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 = index (line1, '\t')) == NULL || + (cp2 = index (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 = rindex (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 SIGTYPE +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); +} |
