2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
15 * Create a Larry Wall format "patch" file between a previous release and the
16 * current head of a module, or between two releases. Can specify the
17 * release as either a date or a revision number.
23 static RETSIGTYPE patch_cleanup (int);
24 static Dtype patch_dirproc (void *callerdat, const char *dir,
25 const char *repos, const char *update_dir,
27 static int patch_fileproc (void *callerdat, struct file_info *finfo);
28 static int patch_proc (int argc, char **argv, char *xwhere,
29 char *mwhere, char *mfile, int shorten,
30 int local_specified, char *mname, char *msg);
32 static int force_tag_match = 1;
33 static int patch_short = 0;
34 static int toptwo_diffs = 0;
35 static char *options = NULL;
36 static char *rev1 = NULL;
37 static int rev1_validated = 0;
38 static char *rev2 = NULL;
39 static int rev2_validated = 0;
40 static char *date1 = NULL;
41 static char *date2 = NULL;
42 static char *tmpfile1 = NULL;
43 static char *tmpfile2 = NULL;
44 static char *tmpfile3 = NULL;
45 static int unidiff = 0;
47 static const char *const patch_usage[] =
49 "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
50 " -r rev|-D date [-r rev2 | -D date2] modules...\n",
51 "\t-f\tForce a head revision match if tag/date not found.\n",
52 "\t-l\tLocal directory only, not recursive\n",
53 "\t-R\tProcess directories recursively.\n",
54 "\t-c\tContext diffs (default)\n",
55 "\t-u\tUnidiff format.\n",
56 "\t-s\tShort patch - one liner per file.\n",
57 "\t-t\tTop two diffs - last change made to the file.\n",
58 "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
59 "\t-k kopt\tSpecify keyword expansion mode.\n",
61 "\t-r rev\tRevision - symbolic or numeric.\n",
62 "(Specify the --help global option for a list of other help options)\n",
69 patch (int argc, char **argv)
81 while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
87 /* The CVS 1.5 client sends these options (in addition to
88 Global_option requests), so we must ignore them. */
91 "-q or -Q must be specified before \"%s\"",
110 if (rev2 != NULL || date2 != NULL)
112 "no more than two revisions/dates can be specified");
113 if (rev1 != NULL || date1 != NULL)
114 date2 = Make_Date (optarg);
116 date1 = Make_Date (optarg);
119 if (rev2 != NULL || date2 != NULL)
121 "no more than two revisions/dates can be specified");
122 if (rev1 != NULL || date1 != NULL)
130 options = RCS_check_kflag (optarg);
133 /* This option is pretty seriously broken:
134 1. It is not clear what it does (does it change keyword
135 expansion behavior? If so, how? Or does it have
136 something to do with what version of RCS we are using?
137 Or the format we write RCS files in?).
138 2. Because both it and -k use the options variable,
139 specifying both -V and -k doesn't work.
140 3. At least as of CVS 1.9, it doesn't work (failed
141 assertion in RCS_checkout where it asserts that options
142 starts with -k). Few people seem to be complaining.
143 In the future (perhaps the near future), I have in mind
144 removing it entirely, and updating NEWS and cvs.texinfo,
145 but in case it is a good idea to give people more time
146 to complain if they would miss it, I'll just add this
147 quick and dirty error message for now. */
149 "the -V option is obsolete and should not be used");
152 unidiff = 1; /* Unidiff */
154 case 'c': /* Context diff */
170 if (toptwo_diffs && patch_short)
171 error (1, 0, "-t and -s options are mutually exclusive");
172 if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
173 rev1 != NULL || rev2 != NULL))
174 error (1, 0, "must not specify revisions/dates with -t option!");
176 if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
177 rev1 == NULL && rev2 == NULL))
178 error (1, 0, "must specify at least one revision/date!");
179 if (date1 != NULL && date2 != NULL)
180 if (RCS_datecmp (date1, date2) >= 0)
181 error (1, 0, "second date must come after first date!");
183 /* if options is NULL, make it a NULL string */
185 options = xstrdup ("");
187 #ifdef CLIENT_SUPPORT
188 if (current_parsed_root->isremote)
190 /* We're the client side. Fire up the remote server. */
197 if (!force_tag_match)
207 option_with_arg ("-r", rev1);
209 client_senddate (date1);
211 option_with_arg ("-r", rev2);
213 client_senddate (date2);
214 if (options[0] != '\0')
219 for (i = 0; i < argc; ++i)
223 send_to_server ("rdiff\012", 0);
224 return get_responses_and_close ();
228 /* clean up if we get a signal */
230 (void)SIG_register (SIGABRT, patch_cleanup);
233 (void)SIG_register (SIGHUP, patch_cleanup);
236 (void)SIG_register (SIGINT, patch_cleanup);
239 (void)SIG_register (SIGQUIT, patch_cleanup);
242 (void)SIG_register (SIGPIPE, patch_cleanup);
245 (void)SIG_register (SIGTERM, patch_cleanup);
249 for (i = 0; i < argc; i++)
250 err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
251 NULL, 0, local, 0, 0, NULL);
261 * callback proc for doing the real work of patching
265 patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
266 int shorten, int local_specified, char *mname, char *msg)
274 TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )",
275 xwhere ? xwhere : "(null)",
276 mwhere ? mwhere : "(null)",
277 mfile ? mfile : "(null)",
278 shorten, local_specified,
279 mname ? mname : "(null)",
280 msg ? msg : "(null)" );
282 repository = xmalloc (strlen (current_parsed_root->directory)
284 + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
285 (void)sprintf (repository, "%s/%s",
286 current_parsed_root->directory, argv[0]);
287 where = xmalloc (strlen (argv[0])
288 + (mfile == NULL ? 0 : strlen (mfile) + 1)
290 (void)strcpy (where, argv[0]);
292 /* if mfile isn't null, we need to set up to do only part of the module */
298 /* if the portion of the module is a path, put the dir part on repos */
299 if ((cp = strrchr (mfile, '/')) != NULL)
302 (void)strcat (repository, "/");
303 (void)strcat (repository, mfile);
304 (void)strcat (where, "/");
305 (void)strcat (where, mfile);
309 /* take care of the rest */
310 path = xmalloc (strlen (repository) + strlen (mfile) + 2);
311 (void)sprintf (path, "%s/%s", repository, mfile);
314 /* directory means repository gets the dir tacked on */
315 (void)strcpy (repository, path);
316 (void)strcat (where, "/");
317 (void)strcat (where, mfile);
329 /* cd to the starting repository */
330 if (CVS_CHDIR (repository) < 0)
332 error (0, errno, "cannot chdir to %s", repository);
339 which = W_REPOS | W_ATTIC;
343 if (rev1 != NULL && !rev1_validated)
345 tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
349 if (rev2 != NULL && !rev2_validated)
351 tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
356 /* start the recursion processor */
357 err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL,
358 argc - 1, argv + 1, local_specified,
359 which, 0, CVS_LOCK_READ, where, 1, repository );
369 * Called to examine a particular RCS file, as appropriate with the options
370 * that were set above.
374 patch_fileproc (void *callerdat, struct file_info *finfo)
377 char *vers_tag, *vers_head;
379 char *rcs_orig = NULL;
381 FILE *fp1, *fp2, *fp3;
389 size_t line1_chars_allocated;
390 size_t line2_chars_allocated;
395 size_t darg_allocated = 0;
399 line1_chars_allocated = 0;
401 line2_chars_allocated = 0;
402 vers_tag = vers_head = NULL;
404 /* find the parsed rcs file */
405 if ((rcsfile = finfo->rcs) == NULL)
410 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
413 rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT);
415 /* if vers_head is NULL, may have been removed from the release */
416 if (isattic && rev2 == NULL && date2 == NULL)
420 vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
422 if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
431 if (vers_head == NULL)
438 date1 = xmalloc (MAXDATELEN);
440 if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
443 error (0, 0, "cannot find date in rcs file %s revision %s",
449 vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL);
450 if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
456 if ((vers_tag == NULL && vers_head == NULL) ||
457 (vers_tag != NULL && vers_head != NULL &&
458 strcmp (vers_head, vers_tag) == 0))
460 /* Nothing known about specified revs or
461 * not changed between releases.
467 if (patch_short && (vers_tag == NULL || vers_head == NULL))
469 /* For adds & removes with a short patch requested, we can print our
470 * error message now and get out.
472 cvs_output ("File ", 0);
473 cvs_output (finfo->fullname, 0);
474 if (vers_tag == NULL)
476 cvs_output (" is new; ", 0);
477 cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0);
478 cvs_output (" revision ", 0);
479 cvs_output (vers_head, 0);
480 cvs_output ("\n", 1);
484 cvs_output (" is removed; ", 0);
485 cvs_output (rev1 ? rev1 : date1, 0);
486 cvs_output (" revision ", 0);
487 cvs_output (vers_tag, 0);
488 cvs_output ("\n", 1);
494 /* Create 3 empty files. I'm not really sure there is any advantage
495 * to doing so now rather than just waiting until later.
497 * There is - cvs_temp_file opens the file so that it can guarantee that
498 * we have exclusive write access to the file. Unfortunately we spoil that
499 * by closing it and reopening it again. Of course any better solution
500 * requires that the RCS functions accept open file pointers rather than
503 if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
505 error (0, errno, "cannot create temporary file %s", tmpfile1);
510 if (fclose (fp1) < 0)
511 error (0, errno, "warning: cannot close %s", tmpfile1);
512 if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
514 error (0, errno, "cannot create temporary file %s", tmpfile2);
519 if (fclose (fp2) < 0)
520 error (0, errno, "warning: cannot close %s", tmpfile2);
521 if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
523 error (0, errno, "cannot create temporary file %s", tmpfile3);
528 if (fclose (fp3) < 0)
529 error (0, errno, "warning: cannot close %s", tmpfile3);
531 if (vers_tag != NULL)
533 retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options,
534 tmpfile1, NULL, NULL);
538 "cannot check out revision %s of %s", vers_tag, rcs);
542 memset ((char *) &t, 0, sizeof (t));
543 if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
545 /* I believe this timestamp only affects the dates in our diffs,
546 and therefore should be on the server, not the client. */
547 (void)utime (tmpfile1, &t);
549 else if (toptwo_diffs)
554 if (vers_head != NULL)
556 retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options,
557 tmpfile2, NULL, NULL);
561 "cannot check out revision %s of %s", vers_head, rcs);
565 if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
567 /* I believe this timestamp only affects the dates in our diffs,
568 and therefore should be on the server, not the client. */
569 (void)utime (tmpfile2, &t);
572 if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
573 else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
574 switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
577 case -1: /* fork/wait failure */
578 error (1, errno, "fork for diff failed on %s", rcs);
580 case 0: /* nothing to do */
584 * The two revisions are really different, so read the first two
585 * lines of the diff output file, and munge them to include more
586 * reasonable file names that "patch" will understand, unless the
587 * user wanted a short patch. In that case, just output the short
592 cvs_output ("File ", 0);
593 cvs_output (finfo->fullname, 0);
594 cvs_output (" changed from revision ", 0);
595 cvs_output (vers_tag, 0);
596 cvs_output (" to ", 0);
597 cvs_output (vers_head, 0);
598 cvs_output ("\n", 1);
603 /* Output an "Index:" line for patch to use */
604 cvs_output ("Index: ", 0);
605 cvs_output (finfo->fullname, 0);
606 cvs_output ("\n", 1);
608 /* Now the munging. */
609 fp = xfopen (tmpfile3, "r");
610 if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
611 getline (&line2, &line2_chars_allocated, fp) < 0)
615 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
618 "failed to read diff file header %s for %s",
622 error (0, errno, "error closing %s", tmpfile3);
627 if (strncmp (line1, "*** ", 4) != 0 ||
628 strncmp (line2, "--- ", 4) != 0 ||
629 (cp1 = strchr (line1, '\t')) == NULL ||
630 (cp2 = strchr (line2, '\t')) == NULL)
632 error (0, 0, "invalid diff header for %s", rcs);
635 error (0, errno, "error closing %s", tmpfile3);
641 if (strncmp (line1, "--- ", 4) != 0 ||
642 strncmp (line2, "+++ ", 4) != 0 ||
643 (cp1 = strchr (line1, '\t')) == NULL ||
644 (cp2 = strchr (line2, '\t')) == NULL)
646 error (0, 0, "invalid unidiff header for %s", rcs);
649 error (0, errno, "error closing %s", tmpfile3);
653 assert (current_parsed_root != NULL);
654 assert (current_parsed_root->directory != NULL);
656 strippath = Xasprintf ("%s/", current_parsed_root->directory);
658 if (strncmp (rcs, strippath, strlen (strippath)) == 0)
659 rcs += strlen (strippath);
661 if (vers_tag != NULL)
662 file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
664 file1 = xstrdup (DEVNULL);
666 file2 = Xasprintf ("%s:%s", finfo->fullname,
667 vers_head ? vers_head : "removed");
669 /* Note that the string "diff" is specified by POSIX (for -c)
670 and is part of the diff output format, not the name of a
674 cvs_output ("diff -u ", 0);
675 cvs_output (file1, 0);
677 cvs_output (file2, 0);
678 cvs_output ("\n", 1);
680 cvs_output ("--- ", 0);
681 cvs_output (file1, 0);
683 cvs_output ("+++ ", 0);
687 cvs_output ("diff -c ", 0);
688 cvs_output (file1, 0);
690 cvs_output (file2, 0);
691 cvs_output ("\n", 1);
693 cvs_output ("*** ", 0);
694 cvs_output (file1, 0);
696 cvs_output ("--- ", 0);
699 cvs_output (finfo->fullname, 0);
702 /* spew the rest of the diff out */
704 = getline (&line1, &line1_chars_allocated, fp))
706 cvs_output (line1, 0);
707 if (line_length < 0 && !feof (fp))
708 error (0, errno, "cannot read %s", tmpfile3);
711 error (0, errno, "cannot close %s", tmpfile3);
716 error (0, 0, "diff failed for %s", finfo->fullname);
723 if (CVS_UNLINK (tmpfile1) < 0)
724 error (0, errno, "cannot unlink %s", tmpfile1);
725 if (CVS_UNLINK (tmpfile2) < 0)
726 error (0, errno, "cannot unlink %s", tmpfile2);
727 if (CVS_UNLINK (tmpfile3) < 0)
728 error (0, errno, "cannot unlink %s", tmpfile3);
732 tmpfile1 = tmpfile2 = tmpfile3 = NULL;
735 run_arg_free_p (dargc, dargv);
740 if (vers_tag != NULL)
742 if (vers_head != NULL)
752 * Print a warm fuzzy message
756 patch_dirproc (void *callerdat, const char *dir, const char *repos,
757 const char *update_dir, List *entries)
760 error (0, 0, "Diffing %s", update_dir);
767 * Clean up temporary files
770 patch_cleanup (int sig)
772 /* Note that the checks for existence_error are because we are
773 called from a signal handler, without SIG_begincrsect, so
774 we don't know whether the files got created. */
776 if (tmpfile1 != NULL)
778 if (unlink_file (tmpfile1) < 0
779 && !existence_error (errno))
780 error (0, errno, "cannot remove %s", tmpfile1);
783 if (tmpfile2 != NULL)
785 if (unlink_file (tmpfile2) < 0
786 && !existence_error (errno))
787 error (0, errno, "cannot remove %s", tmpfile2);
790 if (tmpfile3 != NULL)
792 if (unlink_file (tmpfile3) < 0
793 && !existence_error (errno))
794 error (0, errno, "cannot remove %s", tmpfile3);
797 tmpfile1 = tmpfile2 = tmpfile3 = NULL;
828 name = "broken pipe";
833 name = "termination";
837 /* This case should never be reached, because we list
838 above all the signals for which we actually establish a
840 sprintf (temp, "%d", sig);
844 error (0, 0, "received %s signal", name);