11e72d8d2Sderaadt /*
21e72d8d2Sderaadt * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt *
51e72d8d2Sderaadt * You may distribute under the terms of the GNU General Public License as
6b2b690deStholo * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt *
81e72d8d2Sderaadt * Patch
91e72d8d2Sderaadt *
101e72d8d2Sderaadt * Create a Larry Wall format "patch" file between a previous release and the
111e72d8d2Sderaadt * current head of a module, or between two releases. Can specify the
121e72d8d2Sderaadt * release as either a date or a revision number.
131e72d8d2Sderaadt */
141e72d8d2Sderaadt
1579822b2aStholo #include <assert.h>
161e72d8d2Sderaadt #include "cvs.h"
1713571821Stholo #include "getline.h"
181e72d8d2Sderaadt
191e72d8d2Sderaadt static RETSIGTYPE patch_cleanup PROTO((void));
207e4d5a85Stholo static Dtype patch_dirproc PROTO ((void *callerdat, char *dir,
217e4d5a85Stholo char *repos, char *update_dir,
227e4d5a85Stholo List *entries));
237e4d5a85Stholo static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
24bde78045Stholo static int patch_proc PROTO((int argc, char **argv, char *xwhere,
251e72d8d2Sderaadt char *mwhere, char *mfile, int shorten,
261e72d8d2Sderaadt int local_specified, char *mname, char *msg));
271e72d8d2Sderaadt
281e72d8d2Sderaadt static int force_tag_match = 1;
291e72d8d2Sderaadt static int patch_short = 0;
301e72d8d2Sderaadt static int toptwo_diffs = 0;
311e72d8d2Sderaadt static int local = 0;
321e72d8d2Sderaadt static char *options = NULL;
331e72d8d2Sderaadt static char *rev1 = NULL;
347e4d5a85Stholo static int rev1_validated = 0;
351e72d8d2Sderaadt static char *rev2 = NULL;
367e4d5a85Stholo static int rev2_validated = 0;
371e72d8d2Sderaadt static char *date1 = NULL;
381e72d8d2Sderaadt static char *date2 = NULL;
397e4d5a85Stholo static char *tmpfile1 = NULL;
407e4d5a85Stholo static char *tmpfile2 = NULL;
417e4d5a85Stholo static char *tmpfile3 = NULL;
421e72d8d2Sderaadt static int unidiff = 0;
431e72d8d2Sderaadt
441e72d8d2Sderaadt static const char *const patch_usage[] =
451e72d8d2Sderaadt {
46066e68faStholo "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n",
471e72d8d2Sderaadt " -r rev|-D date [-r rev2 | -D date2] modules...\n",
481e72d8d2Sderaadt "\t-f\tForce a head revision match if tag/date not found.\n",
491e72d8d2Sderaadt "\t-l\tLocal directory only, not recursive\n",
50066e68faStholo "\t-R\tProcess directories recursively.\n",
511e72d8d2Sderaadt "\t-c\tContext diffs (default)\n",
521e72d8d2Sderaadt "\t-u\tUnidiff format.\n",
531e72d8d2Sderaadt "\t-s\tShort patch - one liner per file.\n",
541e72d8d2Sderaadt "\t-t\tTop two diffs - last change made to the file.\n",
551e72d8d2Sderaadt "\t-D date\tDate.\n",
561e72d8d2Sderaadt "\t-r rev\tRevision - symbolic or numeric.\n",
571e72d8d2Sderaadt "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
58b2b690deStholo "(Specify the --help global option for a list of other help options)\n",
591e72d8d2Sderaadt NULL
601e72d8d2Sderaadt };
611e72d8d2Sderaadt
621e72d8d2Sderaadt int
patch(argc,argv)631e72d8d2Sderaadt patch (argc, argv)
641e72d8d2Sderaadt int argc;
651e72d8d2Sderaadt char **argv;
661e72d8d2Sderaadt {
671e72d8d2Sderaadt register int i;
681e72d8d2Sderaadt int c;
691e72d8d2Sderaadt int err = 0;
701e72d8d2Sderaadt DBM *db;
711e72d8d2Sderaadt
721e72d8d2Sderaadt if (argc == -1)
731e72d8d2Sderaadt usage (patch_usage);
741e72d8d2Sderaadt
75066e68faStholo optind = 0;
76172be96aStholo while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
771e72d8d2Sderaadt {
781e72d8d2Sderaadt switch (c)
791e72d8d2Sderaadt {
801e72d8d2Sderaadt case 'Q':
811e72d8d2Sderaadt case 'q':
821e72d8d2Sderaadt #ifdef SERVER_SUPPORT
831e72d8d2Sderaadt /* The CVS 1.5 client sends these options (in addition to
841e72d8d2Sderaadt Global_option requests), so we must ignore them. */
851e72d8d2Sderaadt if (!server_active)
861e72d8d2Sderaadt #endif
871e72d8d2Sderaadt error (1, 0,
881e72d8d2Sderaadt "-q or -Q must be specified before \"%s\"",
891e72d8d2Sderaadt command_name);
901e72d8d2Sderaadt break;
911e72d8d2Sderaadt case 'f':
921e72d8d2Sderaadt force_tag_match = 0;
931e72d8d2Sderaadt break;
941e72d8d2Sderaadt case 'l':
951e72d8d2Sderaadt local = 1;
961e72d8d2Sderaadt break;
971e72d8d2Sderaadt case 'R':
981e72d8d2Sderaadt local = 0;
991e72d8d2Sderaadt break;
1001e72d8d2Sderaadt case 't':
1011e72d8d2Sderaadt toptwo_diffs = 1;
1021e72d8d2Sderaadt break;
1031e72d8d2Sderaadt case 's':
1041e72d8d2Sderaadt patch_short = 1;
1051e72d8d2Sderaadt break;
1061e72d8d2Sderaadt case 'D':
1071e72d8d2Sderaadt if (rev2 != NULL || date2 != NULL)
1081e72d8d2Sderaadt error (1, 0,
1091e72d8d2Sderaadt "no more than two revisions/dates can be specified");
1101e72d8d2Sderaadt if (rev1 != NULL || date1 != NULL)
1111e72d8d2Sderaadt date2 = Make_Date (optarg);
1121e72d8d2Sderaadt else
1131e72d8d2Sderaadt date1 = Make_Date (optarg);
1141e72d8d2Sderaadt break;
1151e72d8d2Sderaadt case 'r':
1161e72d8d2Sderaadt if (rev2 != NULL || date2 != NULL)
1171e72d8d2Sderaadt error (1, 0,
1181e72d8d2Sderaadt "no more than two revisions/dates can be specified");
1191e72d8d2Sderaadt if (rev1 != NULL || date1 != NULL)
1201e72d8d2Sderaadt rev2 = optarg;
1211e72d8d2Sderaadt else
1221e72d8d2Sderaadt rev1 = optarg;
1231e72d8d2Sderaadt break;
1241e72d8d2Sderaadt case 'k':
1251e72d8d2Sderaadt if (options)
1261e72d8d2Sderaadt free (options);
1271e72d8d2Sderaadt options = RCS_check_kflag (optarg);
1281e72d8d2Sderaadt break;
1291e72d8d2Sderaadt case 'V':
130c56b20e6Stholo /* This option is pretty seriously broken:
131c56b20e6Stholo 1. It is not clear what it does (does it change keyword
132c56b20e6Stholo expansion behavior? If so, how? Or does it have
133c56b20e6Stholo something to do with what version of RCS we are using?
134c56b20e6Stholo Or the format we write RCS files in?).
135c56b20e6Stholo 2. Because both it and -k use the options variable,
136c56b20e6Stholo specifying both -V and -k doesn't work.
137c56b20e6Stholo 3. At least as of CVS 1.9, it doesn't work (failed
138c56b20e6Stholo assertion in RCS_checkout where it asserts that options
139c56b20e6Stholo starts with -k). Few people seem to be complaining.
140c56b20e6Stholo In the future (perhaps the near future), I have in mind
141c56b20e6Stholo removing it entirely, and updating NEWS and cvs.texinfo,
142c56b20e6Stholo but in case it is a good idea to give people more time
143c56b20e6Stholo to complain if they would miss it, I'll just add this
144c56b20e6Stholo quick and dirty error message for now. */
145c56b20e6Stholo error (1, 0,
146c56b20e6Stholo "the -V option is obsolete and should not be used");
147c56b20e6Stholo #if 0
1481e72d8d2Sderaadt if (atoi (optarg) <= 0)
1491e72d8d2Sderaadt error (1, 0, "must specify a version number to -V");
1501e72d8d2Sderaadt if (options)
1511e72d8d2Sderaadt free (options);
1521e72d8d2Sderaadt options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */
1531e72d8d2Sderaadt (void) sprintf (options, "-V%s", optarg);
154c56b20e6Stholo #endif
1551e72d8d2Sderaadt break;
1561e72d8d2Sderaadt case 'u':
1571e72d8d2Sderaadt unidiff = 1; /* Unidiff */
1581e72d8d2Sderaadt break;
1591e72d8d2Sderaadt case 'c': /* Context diff */
1601e72d8d2Sderaadt unidiff = 0;
1611e72d8d2Sderaadt break;
1621e72d8d2Sderaadt case '?':
1631e72d8d2Sderaadt default:
1641e72d8d2Sderaadt usage (patch_usage);
1651e72d8d2Sderaadt break;
1661e72d8d2Sderaadt }
1671e72d8d2Sderaadt }
1681e72d8d2Sderaadt argc -= optind;
1691e72d8d2Sderaadt argv += optind;
1701e72d8d2Sderaadt
1711e72d8d2Sderaadt /* Sanity checks */
1721e72d8d2Sderaadt if (argc < 1)
1731e72d8d2Sderaadt usage (patch_usage);
1741e72d8d2Sderaadt
1751e72d8d2Sderaadt if (toptwo_diffs && patch_short)
1761e72d8d2Sderaadt error (1, 0, "-t and -s options are mutually exclusive");
1771e72d8d2Sderaadt if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
1781e72d8d2Sderaadt rev1 != NULL || rev2 != NULL))
1791e72d8d2Sderaadt error (1, 0, "must not specify revisions/dates with -t option!");
1801e72d8d2Sderaadt
1811e72d8d2Sderaadt if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
1821e72d8d2Sderaadt rev1 == NULL && rev2 == NULL))
1831e72d8d2Sderaadt error (1, 0, "must specify at least one revision/date!");
1841e72d8d2Sderaadt if (date1 != NULL && date2 != NULL)
1851e72d8d2Sderaadt if (RCS_datecmp (date1, date2) >= 0)
1861e72d8d2Sderaadt error (1, 0, "second date must come after first date!");
1871e72d8d2Sderaadt
1881e72d8d2Sderaadt /* if options is NULL, make it a NULL string */
1891e72d8d2Sderaadt if (options == NULL)
1901e72d8d2Sderaadt options = xstrdup ("");
1911e72d8d2Sderaadt
1921e72d8d2Sderaadt #ifdef CLIENT_SUPPORT
19379822b2aStholo if (current_parsed_root->isremote)
1941e72d8d2Sderaadt {
1951e72d8d2Sderaadt /* We're the client side. Fire up the remote server. */
1961e72d8d2Sderaadt start_server ();
1971e72d8d2Sderaadt
1981e72d8d2Sderaadt ign_setup ();
1991e72d8d2Sderaadt
2001e72d8d2Sderaadt if (local)
2011e72d8d2Sderaadt send_arg("-l");
20228393fc3Sniklas if (!force_tag_match)
2031e72d8d2Sderaadt send_arg("-f");
2041e72d8d2Sderaadt if (toptwo_diffs)
2051e72d8d2Sderaadt send_arg("-t");
2061e72d8d2Sderaadt if (patch_short)
2071e72d8d2Sderaadt send_arg("-s");
2081e72d8d2Sderaadt if (unidiff)
2091e72d8d2Sderaadt send_arg("-u");
2101e72d8d2Sderaadt
2111e72d8d2Sderaadt if (rev1)
2121e72d8d2Sderaadt option_with_arg ("-r", rev1);
2131e72d8d2Sderaadt if (date1)
2141e72d8d2Sderaadt client_senddate (date1);
2151e72d8d2Sderaadt if (rev2)
2161e72d8d2Sderaadt option_with_arg ("-r", rev2);
2171e72d8d2Sderaadt if (date2)
2181e72d8d2Sderaadt client_senddate (date2);
2191e72d8d2Sderaadt if (options[0] != '\0')
2201e72d8d2Sderaadt send_arg (options);
2211e72d8d2Sderaadt
2221e72d8d2Sderaadt {
2231e72d8d2Sderaadt int i;
2241e72d8d2Sderaadt for (i = 0; i < argc; ++i)
2251e72d8d2Sderaadt send_arg (argv[i]);
2261e72d8d2Sderaadt }
2271e72d8d2Sderaadt
22813571821Stholo send_to_server ("rdiff\012", 0);
2291e72d8d2Sderaadt return get_responses_and_close ();
2301e72d8d2Sderaadt }
2311e72d8d2Sderaadt #endif
2321e72d8d2Sderaadt
2331e72d8d2Sderaadt /* clean up if we get a signal */
234bde78045Stholo #ifdef SIGABRT
235bde78045Stholo (void) SIG_register (SIGABRT, patch_cleanup);
236bde78045Stholo #endif
2371e72d8d2Sderaadt #ifdef SIGHUP
2381e72d8d2Sderaadt (void) SIG_register (SIGHUP, patch_cleanup);
2391e72d8d2Sderaadt #endif
2401e72d8d2Sderaadt #ifdef SIGINT
2411e72d8d2Sderaadt (void) SIG_register (SIGINT, patch_cleanup);
2421e72d8d2Sderaadt #endif
2431e72d8d2Sderaadt #ifdef SIGQUIT
2441e72d8d2Sderaadt (void) SIG_register (SIGQUIT, patch_cleanup);
2451e72d8d2Sderaadt #endif
2461e72d8d2Sderaadt #ifdef SIGPIPE
2471e72d8d2Sderaadt (void) SIG_register (SIGPIPE, patch_cleanup);
2481e72d8d2Sderaadt #endif
2491e72d8d2Sderaadt #ifdef SIGTERM
2501e72d8d2Sderaadt (void) SIG_register (SIGTERM, patch_cleanup);
2511e72d8d2Sderaadt #endif
2521e72d8d2Sderaadt
2531e72d8d2Sderaadt db = open_module ();
2541e72d8d2Sderaadt for (i = 0; i < argc; i++)
2551e72d8d2Sderaadt err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
25679822b2aStholo (char *) NULL, 0, 0, 0, 0, (char *) NULL);
2571e72d8d2Sderaadt close_module (db);
2581e72d8d2Sderaadt free (options);
2591e72d8d2Sderaadt patch_cleanup ();
2601e72d8d2Sderaadt return (err);
2611e72d8d2Sderaadt }
2621e72d8d2Sderaadt
2631e72d8d2Sderaadt /*
2641e72d8d2Sderaadt * callback proc for doing the real work of patching
2651e72d8d2Sderaadt */
2661e72d8d2Sderaadt /* ARGSUSED */
2671e72d8d2Sderaadt static int
patch_proc(argc,argv,xwhere,mwhere,mfile,shorten,local_specified,mname,msg)268bde78045Stholo patch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
2691e72d8d2Sderaadt mname, msg)
270bde78045Stholo int argc;
2711e72d8d2Sderaadt char **argv;
2721e72d8d2Sderaadt char *xwhere;
2731e72d8d2Sderaadt char *mwhere;
2741e72d8d2Sderaadt char *mfile;
2751e72d8d2Sderaadt int shorten;
2761e72d8d2Sderaadt int local_specified;
2771e72d8d2Sderaadt char *mname;
2781e72d8d2Sderaadt char *msg;
2791e72d8d2Sderaadt {
280bde78045Stholo char *myargv[2];
2811e72d8d2Sderaadt int err = 0;
2821e72d8d2Sderaadt int which;
283c56b20e6Stholo char *repository;
284c56b20e6Stholo char *where;
2851e72d8d2Sderaadt
28679822b2aStholo repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
28779822b2aStholo + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
28879822b2aStholo (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
28979822b2aStholo where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
29079822b2aStholo + 1);
2911e72d8d2Sderaadt (void) strcpy (where, argv[0]);
2921e72d8d2Sderaadt
2931e72d8d2Sderaadt /* if mfile isn't null, we need to set up to do only part of the module */
2941e72d8d2Sderaadt if (mfile != NULL)
2951e72d8d2Sderaadt {
2961e72d8d2Sderaadt char *cp;
297c56b20e6Stholo char *path;
2981e72d8d2Sderaadt
2991e72d8d2Sderaadt /* if the portion of the module is a path, put the dir part on repos */
3001e72d8d2Sderaadt if ((cp = strrchr (mfile, '/')) != NULL)
3011e72d8d2Sderaadt {
3021e72d8d2Sderaadt *cp = '\0';
3031e72d8d2Sderaadt (void) strcat (repository, "/");
3041e72d8d2Sderaadt (void) strcat (repository, mfile);
3051e72d8d2Sderaadt (void) strcat (where, "/");
3061e72d8d2Sderaadt (void) strcat (where, mfile);
3071e72d8d2Sderaadt mfile = cp + 1;
3081e72d8d2Sderaadt }
3091e72d8d2Sderaadt
3101e72d8d2Sderaadt /* take care of the rest */
31179822b2aStholo path = xmalloc (strlen (repository) + strlen (mfile) + 2);
3121e72d8d2Sderaadt (void) sprintf (path, "%s/%s", repository, mfile);
3131e72d8d2Sderaadt if (isdir (path))
3141e72d8d2Sderaadt {
3151e72d8d2Sderaadt /* directory means repository gets the dir tacked on */
3161e72d8d2Sderaadt (void) strcpy (repository, path);
3171e72d8d2Sderaadt (void) strcat (where, "/");
3181e72d8d2Sderaadt (void) strcat (where, mfile);
3191e72d8d2Sderaadt }
3201e72d8d2Sderaadt else
3211e72d8d2Sderaadt {
322bde78045Stholo myargv[0] = argv[0];
323bde78045Stholo myargv[1] = mfile;
324bde78045Stholo argc = 2;
325bde78045Stholo argv = myargv;
3261e72d8d2Sderaadt }
327c56b20e6Stholo free (path);
3281e72d8d2Sderaadt }
3291e72d8d2Sderaadt
3301e72d8d2Sderaadt /* cd to the starting repository */
3317e4d5a85Stholo if ( CVS_CHDIR (repository) < 0)
3321e72d8d2Sderaadt {
3331e72d8d2Sderaadt error (0, errno, "cannot chdir to %s", repository);
334c56b20e6Stholo free (repository);
3351e72d8d2Sderaadt return (1);
3361e72d8d2Sderaadt }
337c56b20e6Stholo free (repository);
3381e72d8d2Sderaadt
3391e72d8d2Sderaadt if (force_tag_match)
3401e72d8d2Sderaadt which = W_REPOS | W_ATTIC;
3411e72d8d2Sderaadt else
3421e72d8d2Sderaadt which = W_REPOS;
3431e72d8d2Sderaadt
34413571821Stholo if (rev1 != NULL && !rev1_validated)
34513571821Stholo {
346bde78045Stholo tag_check_valid (rev1, argc - 1, argv + 1, local, 0, NULL);
34713571821Stholo rev1_validated = 1;
34813571821Stholo }
34913571821Stholo if (rev2 != NULL && !rev2_validated)
35013571821Stholo {
351bde78045Stholo tag_check_valid (rev2, argc - 1, argv + 1, local, 0, NULL);
35213571821Stholo rev2_validated = 1;
35313571821Stholo }
35413571821Stholo
3551e72d8d2Sderaadt /* start the recursion processor */
35613571821Stholo err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
3577e4d5a85Stholo (DIRLEAVEPROC) NULL, NULL,
358bde78045Stholo argc - 1, argv + 1, local,
3597e4d5a85Stholo which, 0, 1, where, 1);
360c56b20e6Stholo free (where);
3611e72d8d2Sderaadt
3621e72d8d2Sderaadt return (err);
3631e72d8d2Sderaadt }
3641e72d8d2Sderaadt
3651e72d8d2Sderaadt /*
3661e72d8d2Sderaadt * Called to examine a particular RCS file, as appropriate with the options
3671e72d8d2Sderaadt * that were set above.
3681e72d8d2Sderaadt */
3691e72d8d2Sderaadt /* ARGSUSED */
3701e72d8d2Sderaadt static int
patch_fileproc(callerdat,finfo)3717e4d5a85Stholo patch_fileproc (callerdat, finfo)
3727e4d5a85Stholo void *callerdat;
37345381119Stholo struct file_info *finfo;
3741e72d8d2Sderaadt {
3751e72d8d2Sderaadt struct utimbuf t;
3761e72d8d2Sderaadt char *vers_tag, *vers_head;
377c56b20e6Stholo char *rcs = NULL;
378420bf761Sotto char *rcs_orig = NULL;
3791e72d8d2Sderaadt RCSNode *rcsfile;
3801e72d8d2Sderaadt FILE *fp1, *fp2, *fp3;
3811e72d8d2Sderaadt int ret = 0;
3821e72d8d2Sderaadt int isattic = 0;
3831e72d8d2Sderaadt int retcode = 0;
384c56b20e6Stholo char *file1;
385c56b20e6Stholo char *file2;
386c56b20e6Stholo char *strippath;
38713571821Stholo char *line1, *line2;
38813571821Stholo size_t line1_chars_allocated;
38913571821Stholo size_t line2_chars_allocated;
3909d9224ffStholo char *cp1, *cp2;
3911e72d8d2Sderaadt FILE *fp;
392b2b690deStholo int line_length;
3931e72d8d2Sderaadt
394ae5e8a9bStholo line1 = NULL;
395ae5e8a9bStholo line1_chars_allocated = 0;
396ae5e8a9bStholo line2 = NULL;
397ae5e8a9bStholo line2_chars_allocated = 0;
398ae5e8a9bStholo
3991e72d8d2Sderaadt /* find the parsed rcs file */
4009d9224ffStholo if ((rcsfile = finfo->rcs) == NULL)
401c56b20e6Stholo {
402c56b20e6Stholo ret = 1;
403c56b20e6Stholo goto out2;
404c56b20e6Stholo }
4051e72d8d2Sderaadt if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
4061e72d8d2Sderaadt isattic = 1;
4071e72d8d2Sderaadt
408420bf761Sotto rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
40945381119Stholo (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
4101e72d8d2Sderaadt
4111e72d8d2Sderaadt /* if vers_head is NULL, may have been removed from the release */
4121e72d8d2Sderaadt if (isattic && rev2 == NULL && date2 == NULL)
4131e72d8d2Sderaadt vers_head = NULL;
4141e72d8d2Sderaadt else
4157e4d5a85Stholo {
4167e4d5a85Stholo vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
4177e4d5a85Stholo (int *) NULL);
4187e4d5a85Stholo if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
4197e4d5a85Stholo {
4207e4d5a85Stholo free (vers_head);
4217e4d5a85Stholo vers_head = NULL;
4227e4d5a85Stholo }
4237e4d5a85Stholo }
4241e72d8d2Sderaadt
4251e72d8d2Sderaadt if (toptwo_diffs)
4261e72d8d2Sderaadt {
4271e72d8d2Sderaadt if (vers_head == NULL)
428c56b20e6Stholo {
429c56b20e6Stholo ret = 1;
430c56b20e6Stholo goto out2;
431c56b20e6Stholo }
4321e72d8d2Sderaadt
4331e72d8d2Sderaadt if (!date1)
434c56b20e6Stholo date1 = xmalloc (MAXDATELEN);
4351e72d8d2Sderaadt *date1 = '\0';
4361e72d8d2Sderaadt if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
4371e72d8d2Sderaadt {
4381e72d8d2Sderaadt if (!really_quiet)
4391e72d8d2Sderaadt error (0, 0, "cannot find date in rcs file %s revision %s",
4401e72d8d2Sderaadt rcs, vers_head);
441c56b20e6Stholo ret = 1;
442c56b20e6Stholo goto out2;
4431e72d8d2Sderaadt }
4441e72d8d2Sderaadt }
4457e4d5a85Stholo vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
4467e4d5a85Stholo (int *) NULL);
4477e4d5a85Stholo if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
4487e4d5a85Stholo {
4497e4d5a85Stholo free (vers_tag);
4507e4d5a85Stholo vers_tag = NULL;
4517e4d5a85Stholo }
4521e72d8d2Sderaadt
4537e4d5a85Stholo if (vers_tag == NULL && vers_head == NULL)
454c56b20e6Stholo {
455c56b20e6Stholo /* Nothing known about specified revs. */
456c56b20e6Stholo ret = 0;
457c56b20e6Stholo goto out2;
458c56b20e6Stholo }
4591e72d8d2Sderaadt
4601e72d8d2Sderaadt if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
461c56b20e6Stholo {
462c56b20e6Stholo /* Not changed between releases. */
463c56b20e6Stholo ret = 0;
464c56b20e6Stholo goto out2;
465c56b20e6Stholo }
4661e72d8d2Sderaadt
4671e72d8d2Sderaadt if (patch_short)
4681e72d8d2Sderaadt {
469b2b690deStholo cvs_output ("File ", 0);
470b2b690deStholo cvs_output (finfo->fullname, 0);
4711e72d8d2Sderaadt if (vers_tag == NULL)
472b2b690deStholo {
473b2b690deStholo cvs_output (" is new; current revision ", 0);
474b2b690deStholo cvs_output (vers_head, 0);
475b2b690deStholo cvs_output ("\n", 1);
476b2b690deStholo }
4771e72d8d2Sderaadt else if (vers_head == NULL)
4781e72d8d2Sderaadt {
479b2b690deStholo cvs_output (" is removed; not included in ", 0);
4801e72d8d2Sderaadt if (rev2 != NULL)
481b2b690deStholo {
482b2b690deStholo cvs_output ("release tag ", 0);
483b2b690deStholo cvs_output (rev2, 0);
484b2b690deStholo }
4851e72d8d2Sderaadt else if (date2 != NULL)
486b2b690deStholo {
487b2b690deStholo cvs_output ("release date ", 0);
488b2b690deStholo cvs_output (date2, 0);
4891e72d8d2Sderaadt }
4901e72d8d2Sderaadt else
491b2b690deStholo cvs_output ("current release", 0);
492b2b690deStholo cvs_output ("\n", 1);
493b2b690deStholo }
494b2b690deStholo else
495b2b690deStholo {
496b2b690deStholo cvs_output (" changed from revision ", 0);
497b2b690deStholo cvs_output (vers_tag, 0);
498b2b690deStholo cvs_output (" to ", 0);
499b2b690deStholo cvs_output (vers_head, 0);
500b2b690deStholo cvs_output ("\n", 1);
501b2b690deStholo }
502c56b20e6Stholo ret = 0;
503c56b20e6Stholo goto out2;
5041e72d8d2Sderaadt }
50578c87a5cStholo
50678c87a5cStholo /* Create 3 empty files. I'm not really sure there is any advantage
50779822b2aStholo * to doing so now rather than just waiting until later.
50879822b2aStholo *
50979822b2aStholo * There is - cvs_temp_file opens the file so that it can guarantee that
51079822b2aStholo * we have exclusive write access to the file. Unfortunately we spoil that
51179822b2aStholo * by closing it and reopening it again. Of course any better solution
51279822b2aStholo * requires that the RCS functions accept open file pointers rather than
51379822b2aStholo * simple file names.
51479822b2aStholo */
51579822b2aStholo if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
5161e72d8d2Sderaadt {
51778c87a5cStholo error (0, errno, "cannot create temporary file %s", tmpfile1);
5181e72d8d2Sderaadt ret = 1;
5191e72d8d2Sderaadt goto out;
5201e72d8d2Sderaadt }
52178c87a5cStholo else
52278c87a5cStholo if (fclose (fp1) < 0)
52378c87a5cStholo error (0, errno, "warning: cannot close %s", tmpfile1);
52479822b2aStholo if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
52578c87a5cStholo {
52678c87a5cStholo error (0, errno, "cannot create temporary file %s", tmpfile2);
52778c87a5cStholo ret = 1;
52878c87a5cStholo goto out;
52978c87a5cStholo }
53078c87a5cStholo else
53178c87a5cStholo if (fclose (fp2) < 0)
53278c87a5cStholo error (0, errno, "warning: cannot close %s", tmpfile2);
53379822b2aStholo if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
53478c87a5cStholo {
53578c87a5cStholo error (0, errno, "cannot create temporary file %s", tmpfile3);
53678c87a5cStholo ret = 1;
53778c87a5cStholo goto out;
53878c87a5cStholo }
53978c87a5cStholo else
54078c87a5cStholo if (fclose (fp3) < 0)
54178c87a5cStholo error (0, errno, "warning: cannot close %s", tmpfile3);
54278c87a5cStholo
5431e72d8d2Sderaadt if (vers_tag != NULL)
5441e72d8d2Sderaadt {
5457e4d5a85Stholo retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag,
546172be96aStholo rev1, options, tmpfile1,
547172be96aStholo (RCSCHECKOUTPROC) NULL, (void *) NULL);
54845381119Stholo if (retcode != 0)
5491e72d8d2Sderaadt {
55078c87a5cStholo error (0, 0,
55178c87a5cStholo "cannot check out revision %s of %s", vers_tag, rcs);
5521e72d8d2Sderaadt ret = 1;
5531e72d8d2Sderaadt goto out;
5541e72d8d2Sderaadt }
5551e72d8d2Sderaadt memset ((char *) &t, 0, sizeof (t));
5561e72d8d2Sderaadt if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
5571e72d8d2Sderaadt (char *) 0, 0)) != -1)
558066e68faStholo /* I believe this timestamp only affects the dates in our diffs,
559066e68faStholo and therefore should be on the server, not the client. */
5601e72d8d2Sderaadt (void) utime (tmpfile1, &t);
5611e72d8d2Sderaadt }
5621e72d8d2Sderaadt else if (toptwo_diffs)
5631e72d8d2Sderaadt {
5641e72d8d2Sderaadt ret = 1;
5651e72d8d2Sderaadt goto out;
5661e72d8d2Sderaadt }
5671e72d8d2Sderaadt if (vers_head != NULL)
5681e72d8d2Sderaadt {
5697e4d5a85Stholo retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head,
570172be96aStholo rev2, options, tmpfile2,
571172be96aStholo (RCSCHECKOUTPROC) NULL, (void *) NULL);
57245381119Stholo if (retcode != 0)
5731e72d8d2Sderaadt {
57478c87a5cStholo error (0, 0,
57578c87a5cStholo "cannot check out revision %s of %s", vers_head, rcs);
5761e72d8d2Sderaadt ret = 1;
5771e72d8d2Sderaadt goto out;
5781e72d8d2Sderaadt }
5791e72d8d2Sderaadt if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
5801e72d8d2Sderaadt (char *) 0, 0)) != -1)
581066e68faStholo /* I believe this timestamp only affects the dates in our diffs,
582066e68faStholo and therefore should be on the server, not the client. */
5831e72d8d2Sderaadt (void) utime (tmpfile2, &t);
5841e72d8d2Sderaadt }
58513571821Stholo
58679822b2aStholo switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, unidiff ? "-u" : "-c", tmpfile3))
5871e72d8d2Sderaadt {
5881e72d8d2Sderaadt case -1: /* fork/wait failure */
5891e72d8d2Sderaadt error (1, errno, "fork for diff failed on %s", rcs);
5901e72d8d2Sderaadt break;
5911e72d8d2Sderaadt case 0: /* nothing to do */
5921e72d8d2Sderaadt break;
5931e72d8d2Sderaadt case 1:
5941e72d8d2Sderaadt /*
5951e72d8d2Sderaadt * The two revisions are really different, so read the first two
5961e72d8d2Sderaadt * lines of the diff output file, and munge them to include more
5971e72d8d2Sderaadt * reasonable file names that "patch" will understand.
5981e72d8d2Sderaadt */
5991e72d8d2Sderaadt
6001e72d8d2Sderaadt /* Output an "Index:" line for patch to use */
601b2b690deStholo cvs_output ("Index: ", 0);
602b2b690deStholo cvs_output (finfo->fullname, 0);
603b2b690deStholo cvs_output ("\n", 1);
6041e72d8d2Sderaadt
6051e72d8d2Sderaadt fp = open_file (tmpfile3, "r");
606*f9bbbf45Sfgsch if (get_line (&line1, &line1_chars_allocated, fp) < 0 ||
607*f9bbbf45Sfgsch get_line (&line2, &line2_chars_allocated, fp) < 0)
6081e72d8d2Sderaadt {
60978c87a5cStholo if (feof (fp))
61078c87a5cStholo error (0, 0, "\
61178c87a5cStholo failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
61278c87a5cStholo else
61378c87a5cStholo error (0, errno,
61478c87a5cStholo "failed to read diff file header %s for %s",
6151e72d8d2Sderaadt tmpfile3, rcs);
6161e72d8d2Sderaadt ret = 1;
61778c87a5cStholo if (fclose (fp) < 0)
61878c87a5cStholo error (0, errno, "error closing %s", tmpfile3);
6191e72d8d2Sderaadt goto out;
6201e72d8d2Sderaadt }
6211e72d8d2Sderaadt if (!unidiff)
6221e72d8d2Sderaadt {
6231e72d8d2Sderaadt if (strncmp (line1, "*** ", 4) != 0 ||
6241e72d8d2Sderaadt strncmp (line2, "--- ", 4) != 0 ||
6251e72d8d2Sderaadt (cp1 = strchr (line1, '\t')) == NULL ||
6261e72d8d2Sderaadt (cp2 = strchr (line2, '\t')) == NULL)
6271e72d8d2Sderaadt {
6281e72d8d2Sderaadt error (0, 0, "invalid diff header for %s", rcs);
6291e72d8d2Sderaadt ret = 1;
63078c87a5cStholo if (fclose (fp) < 0)
63178c87a5cStholo error (0, errno, "error closing %s", tmpfile3);
6321e72d8d2Sderaadt goto out;
6331e72d8d2Sderaadt }
6341e72d8d2Sderaadt }
6351e72d8d2Sderaadt else
6361e72d8d2Sderaadt {
6371e72d8d2Sderaadt if (strncmp (line1, "--- ", 4) != 0 ||
6381e72d8d2Sderaadt strncmp (line2, "+++ ", 4) != 0 ||
6391e72d8d2Sderaadt (cp1 = strchr (line1, '\t')) == NULL ||
6401e72d8d2Sderaadt (cp2 = strchr (line2, '\t')) == NULL)
6411e72d8d2Sderaadt {
6421e72d8d2Sderaadt error (0, 0, "invalid unidiff header for %s", rcs);
6431e72d8d2Sderaadt ret = 1;
64478c87a5cStholo if (fclose (fp) < 0)
64578c87a5cStholo error (0, errno, "error closing %s", tmpfile3);
6461e72d8d2Sderaadt goto out;
6471e72d8d2Sderaadt }
6481e72d8d2Sderaadt }
64979822b2aStholo assert (current_parsed_root != NULL);
65079822b2aStholo assert (current_parsed_root->directory != NULL);
651c56b20e6Stholo {
65279822b2aStholo strippath = xmalloc (strlen (current_parsed_root->directory) + 2);
65379822b2aStholo (void) sprintf (strippath, "%s/", current_parsed_root->directory);
654c56b20e6Stholo }
65579822b2aStholo /*else
65679822b2aStholo strippath = xstrdup (REPOS_STRIP); */
6571e72d8d2Sderaadt if (strncmp (rcs, strippath, strlen (strippath)) == 0)
6581e72d8d2Sderaadt rcs += strlen (strippath);
659c56b20e6Stholo free (strippath);
6601e72d8d2Sderaadt if (vers_tag != NULL)
6611e72d8d2Sderaadt {
662c56b20e6Stholo file1 = xmalloc (strlen (finfo->fullname)
663c56b20e6Stholo + strlen (vers_tag)
664c56b20e6Stholo + 10);
6659d9224ffStholo (void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
6661e72d8d2Sderaadt }
6671e72d8d2Sderaadt else
6681e72d8d2Sderaadt {
669c56b20e6Stholo file1 = xstrdup (DEVNULL);
6701e72d8d2Sderaadt }
671c56b20e6Stholo file2 = xmalloc (strlen (finfo->fullname)
672c56b20e6Stholo + (vers_head != NULL ? strlen (vers_head) : 10)
673c56b20e6Stholo + 10);
6749d9224ffStholo (void) sprintf (file2, "%s:%s", finfo->fullname,
6751e72d8d2Sderaadt vers_head ? vers_head : "removed");
6769d9224ffStholo
677b2b690deStholo /* Note that the string "diff" is specified by POSIX (for -c)
678b2b690deStholo and is part of the diff output format, not the name of a
679b2b690deStholo program. */
6801e72d8d2Sderaadt if (unidiff)
6811e72d8d2Sderaadt {
682b2b690deStholo cvs_output ("diff -u ", 0);
683b2b690deStholo cvs_output (file1, 0);
684b2b690deStholo cvs_output (" ", 1);
685b2b690deStholo cvs_output (file2, 0);
686b2b690deStholo cvs_output ("\n", 1);
687b2b690deStholo
688b2b690deStholo cvs_output ("--- ", 0);
689b2b690deStholo cvs_output (file1, 0);
690b2b690deStholo cvs_output (cp1, 0);
691b2b690deStholo cvs_output ("+++ ", 0);
6921e72d8d2Sderaadt }
6931e72d8d2Sderaadt else
6941e72d8d2Sderaadt {
695b2b690deStholo cvs_output ("diff -c ", 0);
696b2b690deStholo cvs_output (file1, 0);
697b2b690deStholo cvs_output (" ", 1);
698b2b690deStholo cvs_output (file2, 0);
699b2b690deStholo cvs_output ("\n", 1);
700b2b690deStholo
701b2b690deStholo cvs_output ("*** ", 0);
702b2b690deStholo cvs_output (file1, 0);
703b2b690deStholo cvs_output (cp1, 0);
704b2b690deStholo cvs_output ("--- ", 0);
7051e72d8d2Sderaadt }
7061e72d8d2Sderaadt
707b2b690deStholo cvs_output (finfo->fullname, 0);
708b2b690deStholo cvs_output (cp2, 0);
709b2b690deStholo
71013571821Stholo /* spew the rest of the diff out */
711b2b690deStholo while ((line_length
712*f9bbbf45Sfgsch = get_line (&line1, &line1_chars_allocated, fp))
713b2b690deStholo >= 0)
714b2b690deStholo cvs_output (line1, 0);
715b2b690deStholo if (line_length < 0 && !feof (fp))
716b2b690deStholo error (0, errno, "cannot read %s", tmpfile3);
717b2b690deStholo
718b2b690deStholo if (fclose (fp) < 0)
719b2b690deStholo error (0, errno, "cannot close %s", tmpfile3);
720c56b20e6Stholo free (file1);
721c56b20e6Stholo free (file2);
7221e72d8d2Sderaadt break;
7231e72d8d2Sderaadt default:
7249d9224ffStholo error (0, 0, "diff failed for %s", finfo->fullname);
7251e72d8d2Sderaadt }
7261e72d8d2Sderaadt out:
72713571821Stholo if (line1)
72813571821Stholo free (line1);
72913571821Stholo if (line2)
73013571821Stholo free (line2);
731b2b690deStholo if (CVS_UNLINK (tmpfile1) < 0)
732b2b690deStholo error (0, errno, "cannot unlink %s", tmpfile1);
733b2b690deStholo if (CVS_UNLINK (tmpfile2) < 0)
734b2b690deStholo error (0, errno, "cannot unlink %s", tmpfile2);
735b2b690deStholo if (CVS_UNLINK (tmpfile3) < 0)
736b2b690deStholo error (0, errno, "cannot unlink %s", tmpfile3);
7377e4d5a85Stholo free (tmpfile1);
7387e4d5a85Stholo free (tmpfile2);
7397e4d5a85Stholo free (tmpfile3);
7407e4d5a85Stholo tmpfile1 = tmpfile2 = tmpfile3 = NULL;
741c56b20e6Stholo
742c56b20e6Stholo out2:
743bde78045Stholo if (vers_tag != NULL)
744bde78045Stholo free (vers_tag);
745bde78045Stholo if (vers_head != NULL)
746bde78045Stholo free (vers_head);
747420bf761Sotto if (rcs_orig)
748420bf761Sotto free (rcs_orig);
7491e72d8d2Sderaadt return (ret);
7501e72d8d2Sderaadt }
7511e72d8d2Sderaadt
7521e72d8d2Sderaadt /*
7531e72d8d2Sderaadt * Print a warm fuzzy message
7541e72d8d2Sderaadt */
7551e72d8d2Sderaadt /* ARGSUSED */
7561e72d8d2Sderaadt static Dtype
patch_dirproc(callerdat,dir,repos,update_dir,entries)7577e4d5a85Stholo patch_dirproc (callerdat, dir, repos, update_dir, entries)
7587e4d5a85Stholo void *callerdat;
7591e72d8d2Sderaadt char *dir;
7601e72d8d2Sderaadt char *repos;
7611e72d8d2Sderaadt char *update_dir;
7627e4d5a85Stholo List *entries;
7631e72d8d2Sderaadt {
7641e72d8d2Sderaadt if (!quiet)
7651e72d8d2Sderaadt error (0, 0, "Diffing %s", update_dir);
7661e72d8d2Sderaadt return (R_PROCESS);
7671e72d8d2Sderaadt }
7681e72d8d2Sderaadt
7691e72d8d2Sderaadt /*
7701e72d8d2Sderaadt * Clean up temporary files
7711e72d8d2Sderaadt */
7721e72d8d2Sderaadt static RETSIGTYPE
patch_cleanup()7731e72d8d2Sderaadt patch_cleanup ()
7741e72d8d2Sderaadt {
7759fe7c2c3Stholo /* Note that the checks for existence_error are because we are
7769fe7c2c3Stholo called from a signal handler, without SIG_begincrsect, so
7779fe7c2c3Stholo we don't know whether the files got created. */
7789fe7c2c3Stholo
7797e4d5a85Stholo if (tmpfile1 != NULL)
7807e4d5a85Stholo {
7819fe7c2c3Stholo if (unlink_file (tmpfile1) < 0
7829fe7c2c3Stholo && !existence_error (errno))
7839fe7c2c3Stholo error (0, errno, "cannot remove %s", tmpfile1);
7847e4d5a85Stholo free (tmpfile1);
7857e4d5a85Stholo }
7867e4d5a85Stholo if (tmpfile2 != NULL)
7877e4d5a85Stholo {
7889fe7c2c3Stholo if (unlink_file (tmpfile2) < 0
7899fe7c2c3Stholo && !existence_error (errno))
7909fe7c2c3Stholo error (0, errno, "cannot remove %s", tmpfile2);
7917e4d5a85Stholo free (tmpfile2);
7927e4d5a85Stholo }
7937e4d5a85Stholo if (tmpfile3 != NULL)
7947e4d5a85Stholo {
7959fe7c2c3Stholo if (unlink_file (tmpfile3) < 0
7969fe7c2c3Stholo && !existence_error (errno))
7979fe7c2c3Stholo error (0, errno, "cannot remove %s", tmpfile3);
7987e4d5a85Stholo free (tmpfile3);
7997e4d5a85Stholo }
8007e4d5a85Stholo tmpfile1 = tmpfile2 = tmpfile3 = NULL;
8011e72d8d2Sderaadt }
802