xref: /netbsd-src/external/gpl2/xcvs/dist/src/patch.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  *
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.
12  *
13  * Patch
14  *
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.
18  */
19 
20 #include "cvs.h"
21 #include "getline.h"
22 
23 static RETSIGTYPE patch_cleanup (int);
24 static Dtype patch_dirproc (void *callerdat, const char *dir,
25                             const char *repos, const char *update_dir,
26                             List *entries);
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);
31 
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;
46 static int show_c_func = 0;
47 
48 static const char *const patch_usage[] =
49 {
50     "Usage: %s %s [-flpR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
51     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
52     "\t-f\tForce a head revision match if tag/date not found.\n",
53     "\t-l\tLocal directory only, not recursive\n",
54     "\t-R\tProcess directories recursively.\n",
55     "\t-c\tContext diffs (default)\n",
56     "\t-u\tUnidiff format.\n",
57     "\t-p\tShow which C function each change is in.\n",	/* --show-c-function */
58     "\t-s\tShort patch - one liner per file.\n",
59     "\t-t\tTop two diffs - last change made to the file.\n",
60     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
61     "\t-k kopt\tSpecify keyword expansion mode.\n",
62     "\t-D date\tDate.\n",
63     "\t-r rev\tRevision - symbolic or numeric.\n",
64     "(Specify the --help global option for a list of other help options)\n",
65     NULL
66 };
67 
68 
69 
70 int
71 patch (int argc, char **argv)
72 {
73     register int i;
74     int local = 0;
75     int c;
76     int err = 0;
77     DBM *db;
78 
79     if (argc == -1)
80 	usage (patch_usage);
81 
82     getoptreset ();
83     while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:p")) != -1)
84     {
85 	switch (c)
86 	{
87 	    case 'Q':
88 	    case 'q':
89 		/* The CVS 1.5 client sends these options (in addition to
90 		   Global_option requests), so we must ignore them.  */
91 		if (!server_active)
92 		    error (1, 0,
93 			   "-q or -Q must be specified before \"%s\"",
94 			   cvs_cmd_name);
95 		break;
96 	    case 'f':
97 		force_tag_match = 0;
98 		break;
99 	    case 'l':
100 		local = 1;
101 		break;
102 	    case 'R':
103 		local = 0;
104 		break;
105 	    case 't':
106 		toptwo_diffs = 1;
107 		break;
108 	    case 's':
109 		patch_short = 1;
110 		break;
111 	    case 'D':
112 		if (rev2 != NULL || date2 != NULL)
113 		    error (1, 0,
114 		       "no more than two revisions/dates can be specified");
115 		if (rev1 != NULL || date1 != NULL)
116 		    date2 = Make_Date (optarg);
117 		else
118 		    date1 = Make_Date (optarg);
119 		break;
120 	    case 'r':
121 		if (rev2 != NULL || date2 != NULL)
122 		    error (1, 0,
123 		       "no more than two revisions/dates can be specified");
124 		if (rev1 != NULL || date1 != NULL)
125 		    rev2 = optarg;
126 		else
127 		    rev1 = optarg;
128 		break;
129 	    case 'k':
130 		if (options)
131 		    free (options);
132 		options = RCS_check_kflag (optarg);
133 		break;
134 	    case 'V':
135 		/* This option is pretty seriously broken:
136 		   1.  It is not clear what it does (does it change keyword
137 		   expansion behavior?  If so, how?  Or does it have
138 		   something to do with what version of RCS we are using?
139 		   Or the format we write RCS files in?).
140 		   2.  Because both it and -k use the options variable,
141 		   specifying both -V and -k doesn't work.
142 		   3.  At least as of CVS 1.9, it doesn't work (failed
143 		   assertion in RCS_checkout where it asserts that options
144 		   starts with -k).  Few people seem to be complaining.
145 		   In the future (perhaps the near future), I have in mind
146 		   removing it entirely, and updating NEWS and cvs.texinfo,
147 		   but in case it is a good idea to give people more time
148 		   to complain if they would miss it, I'll just add this
149 		   quick and dirty error message for now.  */
150 		error (1, 0,
151 		       "the -V option is obsolete and should not be used");
152 		break;
153 	    case 'u':
154 		unidiff = 1;		/* Unidiff */
155 		break;
156 	    case 'c':			/* Context diff */
157 		unidiff = 0;
158 		break;
159 	    case 'p':
160 		show_c_func = 1;
161 		break;
162 	    case '?':
163 	    default:
164 		usage (patch_usage);
165 		break;
166 	}
167     }
168     argc -= optind;
169     argv += optind;
170 
171     /* Sanity checks */
172     if (argc < 1)
173 	usage (patch_usage);
174 
175     if (toptwo_diffs && patch_short)
176 	error (1, 0, "-t and -s options are mutually exclusive");
177     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
178 			 rev1 != NULL || rev2 != NULL))
179 	error (1, 0, "must not specify revisions/dates with -t option!");
180 
181     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
182 			  rev1 == NULL && rev2 == NULL))
183 	error (1, 0, "must specify at least one revision/date!");
184     if (date1 != NULL && date2 != NULL)
185 	if (RCS_datecmp (date1, date2) >= 0)
186 	    error (1, 0, "second date must come after first date!");
187 
188     /* if options is NULL, make it a NULL string */
189     if (options == NULL)
190 	options = xstrdup ("");
191 
192 #ifdef CLIENT_SUPPORT
193     if (current_parsed_root->isremote)
194     {
195 	/* We're the client side.  Fire up the remote server.  */
196 	start_server ();
197 
198 	ign_setup ();
199 
200 	if (local)
201 	    send_arg("-l");
202 	if (!force_tag_match)
203 	    send_arg("-f");
204 	if (toptwo_diffs)
205 	    send_arg("-t");
206 	if (patch_short)
207 	    send_arg("-s");
208 	if (unidiff)
209 	    send_arg("-u");
210 	if (show_c_func)
211 	    send_arg("-p");
212 
213 	if (rev1)
214 	    option_with_arg ("-r", rev1);
215 	if (date1)
216 	    client_senddate (date1);
217 	if (rev2)
218 	    option_with_arg ("-r", rev2);
219 	if (date2)
220 	    client_senddate (date2);
221 	if (options[0] != '\0')
222 	    send_arg (options);
223 
224 	{
225 	    int i;
226 	    for (i = 0; i < argc; ++i)
227 		send_arg (argv[i]);
228 	}
229 
230 	send_to_server ("rdiff\012", 0);
231         return get_responses_and_close ();
232     }
233 #endif
234 
235     /* clean up if we get a signal */
236 #ifdef SIGABRT
237     (void)SIG_register (SIGABRT, patch_cleanup);
238 #endif
239 #ifdef SIGHUP
240     (void)SIG_register (SIGHUP, patch_cleanup);
241 #endif
242 #ifdef SIGINT
243     (void)SIG_register (SIGINT, patch_cleanup);
244 #endif
245 #ifdef SIGQUIT
246     (void)SIG_register (SIGQUIT, patch_cleanup);
247 #endif
248 #ifdef SIGPIPE
249     (void)SIG_register (SIGPIPE, patch_cleanup);
250 #endif
251 #ifdef SIGTERM
252     (void)SIG_register (SIGTERM, patch_cleanup);
253 #endif
254 
255     db = open_module ();
256     for (i = 0; i < argc; i++)
257 	err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
258 			  NULL, 0, local, 0, 0, NULL);
259     close_module (db);
260     free (options);
261     patch_cleanup (0);
262     return err;
263 }
264 
265 
266 
267 /*
268  * callback proc for doing the real work of patching
269  */
270 /* ARGSUSED */
271 static int
272 patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
273             int shorten, int local_specified, char *mname, char *msg)
274 {
275     char *myargv[2];
276     int err = 0;
277     int which;
278     char *repository;
279     char *where;
280 
281     TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )",
282 	    xwhere ? xwhere : "(null)",
283 	    mwhere ? mwhere : "(null)",
284 	    mfile ? mfile : "(null)",
285 	    shorten, local_specified,
286 	    mname ? mname : "(null)",
287 	    msg ? msg : "(null)" );
288 
289     repository = xmalloc (strlen (current_parsed_root->directory)
290                           + strlen (argv[0])
291                           + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
292     (void)sprintf (repository, "%s/%s",
293                    current_parsed_root->directory, argv[0]);
294     where = xmalloc (strlen (argv[0])
295                      + (mfile == NULL ? 0 : strlen (mfile) + 1)
296 		     + 1);
297     (void)strcpy (where, argv[0]);
298 
299     /* if mfile isn't null, we need to set up to do only part of the module */
300     if (mfile != NULL)
301     {
302 	char *cp;
303 	char *path;
304 
305 	/* if the portion of the module is a path, put the dir part on repos */
306 	if ((cp = strrchr (mfile, '/')) != NULL)
307 	{
308 	    *cp = '\0';
309 	    (void)strcat (repository, "/");
310 	    (void)strcat (repository, mfile);
311 	    (void)strcat (where, "/");
312 	    (void)strcat (where, mfile);
313 	    mfile = cp + 1;
314 	}
315 
316 	/* take care of the rest */
317 	path = xmalloc (strlen (repository) + strlen (mfile) + 2);
318 	(void)sprintf (path, "%s/%s", repository, mfile);
319 	if (isdir (path))
320 	{
321 	    /* directory means repository gets the dir tacked on */
322 	    (void)strcpy (repository, path);
323 	    (void)strcat (where, "/");
324 	    (void)strcat (where, mfile);
325 	}
326 	else
327 	{
328 	    myargv[0] = argv[0];
329 	    myargv[1] = mfile;
330 	    argc = 2;
331 	    argv = myargv;
332 	}
333 	free (path);
334     }
335 
336     /* cd to the starting repository */
337     if (CVS_CHDIR (repository) < 0)
338     {
339 	error (0, errno, "cannot chdir to %s", repository);
340 	free (repository);
341 	free (where);
342 	return 1;
343     }
344 
345     if (force_tag_match)
346 	which = W_REPOS | W_ATTIC;
347     else
348 	which = W_REPOS;
349 
350     if (rev1 != NULL && !rev1_validated)
351     {
352 	tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
353 			 repository, false);
354 	rev1_validated = 1;
355     }
356     if (rev2 != NULL && !rev2_validated)
357     {
358 	tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
359 			 repository, false);
360 	rev2_validated = 1;
361     }
362 
363     /* start the recursion processor */
364     err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL,
365 			   argc - 1, argv + 1, local_specified,
366 			   which, 0, CVS_LOCK_READ, where, 1, repository );
367     free (repository);
368     free (where);
369 
370     return err;
371 }
372 
373 
374 
375 /*
376  * Called to examine a particular RCS file, as appropriate with the options
377  * that were set above.
378  */
379 /* ARGSUSED */
380 static int
381 patch_fileproc (void *callerdat, struct file_info *finfo)
382 {
383     struct utimbuf t;
384     char *vers_tag, *vers_head;
385     char *rcs = NULL;
386     char *rcs_orig = NULL;
387     RCSNode *rcsfile;
388     FILE *fp1, *fp2, *fp3;
389     int ret = 0;
390     int isattic = 0;
391     int retcode = 0;
392     char *file1;
393     char *file2;
394     char *strippath;
395     char *line1, *line2;
396     size_t line1_chars_allocated;
397     size_t line2_chars_allocated;
398     char *cp1, *cp2;
399     FILE *fp;
400     int line_length;
401     int dargc = 0;
402     size_t darg_allocated = 0;
403     char **dargv = NULL;
404 
405     line1 = NULL;
406     line1_chars_allocated = 0;
407     line2 = NULL;
408     line2_chars_allocated = 0;
409     vers_tag = vers_head = NULL;
410 
411     /* find the parsed rcs file */
412     if ((rcsfile = finfo->rcs) == NULL)
413     {
414 	ret = 1;
415 	goto out2;
416     }
417     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
418 	isattic = 1;
419 
420     rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT);
421 
422     /* if vers_head is NULL, may have been removed from the release */
423     if (isattic && rev2 == NULL && date2 == NULL)
424 	vers_head = NULL;
425     else
426     {
427 	vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
428 				    NULL);
429 	if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
430 	{
431 	    free (vers_head);
432 	    vers_head = NULL;
433 	}
434     }
435 
436     if (toptwo_diffs)
437     {
438 	if (vers_head == NULL)
439 	{
440 	    ret = 1;
441 	    goto out2;
442 	}
443 
444 	if (!date1)
445 	    date1 = xmalloc (MAXDATELEN);
446 	*date1 = '\0';
447 	if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
448 	{
449 	    if (!really_quiet)
450 		error (0, 0, "cannot find date in rcs file %s revision %s",
451 		       rcs, vers_head);
452 	    ret = 1;
453 	    goto out2;
454 	}
455     }
456     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL);
457     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
458     {
459         free (vers_tag);
460 	vers_tag = NULL;
461     }
462 
463     if ((vers_tag == NULL && vers_head == NULL) ||
464         (vers_tag != NULL && vers_head != NULL &&
465 	 strcmp (vers_head, vers_tag) == 0))
466     {
467 	/* Nothing known about specified revs or
468 	 * not changed between releases.
469 	 */
470 	ret = 0;
471 	goto out2;
472     }
473 
474     if (patch_short && (vers_tag == NULL || vers_head == NULL))
475     {
476 	/* For adds & removes with a short patch requested, we can print our
477 	 * error message now and get out.
478 	 */
479 	cvs_output ("File ", 0);
480 	cvs_output (finfo->fullname, 0);
481 	if (vers_tag == NULL)
482 	{
483 	    cvs_output (" is new; ", 0);
484 	    cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0);
485 	    cvs_output (" revision ", 0);
486 	    cvs_output (vers_head, 0);
487 	    cvs_output ("\n", 1);
488 	}
489 	else
490 	{
491 	    cvs_output (" is removed; ", 0);
492 	    cvs_output (rev1 ? rev1 : date1, 0);
493 	    cvs_output (" revision ", 0);
494 	    cvs_output (vers_tag, 0);
495 	    cvs_output ("\n", 1);
496 	}
497 	ret = 0;
498 	goto out2;
499     }
500 
501     /* Create 3 empty files.  I'm not really sure there is any advantage
502      * to doing so now rather than just waiting until later.
503      *
504      * There is - cvs_temp_file opens the file so that it can guarantee that
505      * we have exclusive write access to the file.  Unfortunately we spoil that
506      * by closing it and reopening it again.  Of course any better solution
507      * requires that the RCS functions accept open file pointers rather than
508      * simple file names.
509      */
510     if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
511     {
512 	error (0, errno, "cannot create temporary file %s", tmpfile1);
513 	ret = 1;
514 	goto out;
515     }
516     else
517 	if (fclose (fp1) < 0)
518 	    error (0, errno, "warning: cannot close %s", tmpfile1);
519     if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
520     {
521 	error (0, errno, "cannot create temporary file %s", tmpfile2);
522 	ret = 1;
523 	goto out;
524     }
525     else
526 	if (fclose (fp2) < 0)
527 	    error (0, errno, "warning: cannot close %s", tmpfile2);
528     if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
529     {
530 	error (0, errno, "cannot create temporary file %s", tmpfile3);
531 	ret = 1;
532 	goto out;
533     }
534     else
535 	if (fclose (fp3) < 0)
536 	    error (0, errno, "warning: cannot close %s", tmpfile3);
537 
538     if (vers_tag != NULL)
539     {
540 	retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options,
541                                 tmpfile1, NULL, NULL);
542 	if (retcode != 0)
543 	{
544 	    error (0, 0,
545 		   "cannot check out revision %s of %s", vers_tag, rcs);
546 	    ret = 1;
547 	    goto out;
548 	}
549 	memset ((char *) &t, 0, sizeof (t));
550 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
551 						    NULL, 0)) != -1)
552 	    /* I believe this timestamp only affects the dates in our diffs,
553 	       and therefore should be on the server, not the client.  */
554 	    (void)utime (tmpfile1, &t);
555     }
556     else if (toptwo_diffs)
557     {
558 	ret = 1;
559 	goto out;
560     }
561     if (vers_head != NULL)
562     {
563 	retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options,
564                                 tmpfile2, NULL, NULL);
565 	if (retcode != 0)
566 	{
567 	    error (0, 0,
568 		   "cannot check out revision %s of %s", vers_head, rcs);
569 	    ret = 1;
570 	    goto out;
571 	}
572 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
573 						    NULL, 0)) != -1)
574 	    /* I believe this timestamp only affects the dates in our diffs,
575 	       and therefore should be on the server, not the client.  */
576 	    (void)utime (tmpfile2, &t);
577     }
578 
579     if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
580     else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
581     if (show_c_func)
582         run_add_arg_p (&dargc, &darg_allocated, &dargv, "-p");
583     switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
584 		       tmpfile3))
585     {
586 	case -1:			/* fork/wait failure */
587 	    error (1, errno, "fork for diff failed on %s", rcs);
588 	    break;
589 	case 0:				/* nothing to do */
590 	    break;
591 	case 1:
592 	    /*
593 	     * The two revisions are really different, so read the first two
594 	     * lines of the diff output file, and munge them to include more
595 	     * reasonable file names that "patch" will understand, unless the
596 	     * user wanted a short patch.  In that case, just output the short
597 	     * message.
598 	     */
599 	    if (patch_short)
600 	    {
601 		cvs_output ("File ", 0);
602 		cvs_output (finfo->fullname, 0);
603 		cvs_output (" changed from revision ", 0);
604 		cvs_output (vers_tag, 0);
605 		cvs_output (" to ", 0);
606 		cvs_output (vers_head, 0);
607 		cvs_output ("\n", 1);
608 		ret = 0;
609 		goto out;
610 	    }
611 
612 	    /* Output an "Index:" line for patch to use */
613 	    cvs_output ("Index: ", 0);
614 	    cvs_output (finfo->fullname, 0);
615 	    cvs_output ("\n", 1);
616 
617 	    /* Now the munging. */
618 	    fp = xfopen (tmpfile3, "r");
619 	    if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
620 		getline (&line2, &line2_chars_allocated, fp) < 0)
621 	    {
622 		if (feof (fp))
623 		    error (0, 0, "\
624 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
625 		else
626 		    error (0, errno,
627 			   "failed to read diff file header %s for %s",
628 			   tmpfile3, rcs);
629 		ret = 1;
630 		if (fclose (fp) < 0)
631 		    error (0, errno, "error closing %s", tmpfile3);
632 		goto out;
633 	    }
634 	    if (!unidiff)
635 	    {
636 		if (strncmp (line1, "*** ", 4) != 0 ||
637 		    strncmp (line2, "--- ", 4) != 0 ||
638 		    (cp1 = strchr (line1, '\t')) == NULL ||
639 		    (cp2 = strchr (line2, '\t')) == NULL)
640 		{
641 		    error (0, 0, "invalid diff header for %s", rcs);
642 		    ret = 1;
643 		    if (fclose (fp) < 0)
644 			error (0, errno, "error closing %s", tmpfile3);
645 		    goto out;
646 		}
647 	    }
648 	    else
649 	    {
650 		if (strncmp (line1, "--- ", 4) != 0 ||
651 		    strncmp (line2, "+++ ", 4) != 0 ||
652 		    (cp1 = strchr (line1, '\t')) == NULL ||
653 		    (cp2 = strchr  (line2, '\t')) == NULL)
654 		{
655 		    error (0, 0, "invalid unidiff header for %s", rcs);
656 		    ret = 1;
657 		    if (fclose (fp) < 0)
658 			error (0, errno, "error closing %s", tmpfile3);
659 		    goto out;
660 		}
661 	    }
662 	    assert (current_parsed_root != NULL);
663 	    assert (current_parsed_root->directory != NULL);
664 
665 	    strippath = Xasprintf ("%s/", current_parsed_root->directory);
666 
667 	    if (strncmp (rcs, strippath, strlen (strippath)) == 0)
668 		rcs += strlen (strippath);
669 	    free (strippath);
670 	    if (vers_tag != NULL)
671 		file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
672 	    else
673 		file1 = xstrdup (DEVNULL);
674 
675 	    file2 = Xasprintf ("%s:%s", finfo->fullname,
676 			       vers_head ? vers_head : "removed");
677 
678 	    /* Note that the string "diff" is specified by POSIX (for -c)
679 	       and is part of the diff output format, not the name of a
680 	       program.  */
681 	    if (unidiff)
682 	    {
683 		cvs_output ("diff -u ", 0);
684 		cvs_output (file1, 0);
685 		cvs_output (" ", 1);
686 		cvs_output (file2, 0);
687 		cvs_output ("\n", 1);
688 
689 		cvs_output ("--- ", 0);
690 		cvs_output (file1, 0);
691 		cvs_output (cp1, 0);
692 		cvs_output ("+++ ", 0);
693 	    }
694 	    else
695 	    {
696 		cvs_output ("diff -c ", 0);
697 		cvs_output (file1, 0);
698 		cvs_output (" ", 1);
699 		cvs_output (file2, 0);
700 		cvs_output ("\n", 1);
701 
702 		cvs_output ("*** ", 0);
703 		cvs_output (file1, 0);
704 		cvs_output (cp1, 0);
705 		cvs_output ("--- ", 0);
706 	    }
707 
708 	    cvs_output (finfo->fullname, 0);
709 	    cvs_output (cp2, 0);
710 
711 	    /* spew the rest of the diff out */
712 	    while ((line_length
713 		    = getline (&line1, &line1_chars_allocated, fp))
714 		   >= 0)
715 		cvs_output (line1, 0);
716 	    if (line_length < 0 && !feof (fp))
717 		error (0, errno, "cannot read %s", tmpfile3);
718 
719 	    if (fclose (fp) < 0)
720 		error (0, errno, "cannot close %s", tmpfile3);
721 	    free (file1);
722 	    free (file2);
723 	    break;
724 	default:
725 	    error (0, 0, "diff failed for %s", finfo->fullname);
726     }
727   out:
728     if (line1)
729         free (line1);
730     if (line2)
731         free (line2);
732     if (CVS_UNLINK (tmpfile1) < 0)
733 	error (0, errno, "cannot unlink %s", tmpfile1);
734     if (CVS_UNLINK (tmpfile2) < 0)
735 	error (0, errno, "cannot unlink %s", tmpfile2);
736     if (CVS_UNLINK (tmpfile3) < 0)
737 	error (0, errno, "cannot unlink %s", tmpfile3);
738     free (tmpfile1);
739     free (tmpfile2);
740     free (tmpfile3);
741     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
742     if (darg_allocated)
743     {
744 	run_arg_free_p (dargc, dargv);
745 	free (dargv);
746     }
747 
748  out2:
749     if (vers_tag != NULL)
750 	free (vers_tag);
751     if (vers_head != NULL)
752 	free (vers_head);
753     if (rcs_orig)
754 	free (rcs_orig);
755     return ret;
756 }
757 
758 
759 
760 /*
761  * Print a warm fuzzy message
762  */
763 /* ARGSUSED */
764 static Dtype
765 patch_dirproc (void *callerdat, const char *dir, const char *repos,
766                const char *update_dir, List *entries)
767 {
768     if (!quiet)
769 	error (0, 0, "Diffing %s", update_dir);
770     return R_PROCESS;
771 }
772 
773 
774 
775 /*
776  * Clean up temporary files
777  */
778 static RETSIGTYPE
779 patch_cleanup (int sig)
780 {
781     /* Note that the checks for existence_error are because we are
782        called from a signal handler, without SIG_begincrsect, so
783        we don't know whether the files got created.  */
784 
785     static int reenter = 0;
786 
787     if (reenter++)
788 	_exit(1);
789 
790     if (tmpfile1 != NULL)
791     {
792 	if (unlink_file (tmpfile1) < 0
793 	    && !existence_error (errno))
794 	    error (0, errno, "cannot remove %s", tmpfile1);
795 	free (tmpfile1);
796     }
797     if (tmpfile2 != NULL)
798     {
799 	if (unlink_file (tmpfile2) < 0
800 	    && !existence_error (errno))
801 	    error (0, errno, "cannot remove %s", tmpfile2);
802 	free (tmpfile2);
803     }
804     if (tmpfile3 != NULL)
805     {
806 	if (unlink_file (tmpfile3) < 0
807 	    && !existence_error (errno))
808 	    error (0, errno, "cannot remove %s", tmpfile3);
809 	free (tmpfile3);
810     }
811     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
812 
813     if (sig != 0)
814     {
815 	const char *name;
816 	char temp[10];
817 
818 	switch (sig)
819 	{
820 #ifdef SIGABRT
821 	case SIGABRT:
822 	    name = "abort";
823 	    break;
824 #endif
825 #ifdef SIGHUP
826 	case SIGHUP:
827 	    name = "hangup";
828 	    break;
829 #endif
830 #ifdef SIGINT
831 	case SIGINT:
832 	    name = "interrupt";
833 	    break;
834 #endif
835 #ifdef SIGQUIT
836 	case SIGQUIT:
837 	    name = "quit";
838 	    break;
839 #endif
840 #ifdef SIGPIPE
841 	case SIGPIPE:
842 	    name = "broken pipe";
843 	    break;
844 #endif
845 #ifdef SIGTERM
846 	case SIGTERM:
847 	    name = "termination";
848 	    break;
849 #endif
850 	default:
851 	    /* This case should never be reached, because we list
852 	       above all the signals for which we actually establish a
853 	       signal handler.  */
854 	    sprintf (temp, "%d", sig);
855 	    name = temp;
856 	    break;
857 	}
858 	error (0, 0, "received %s signal", name);
859     }
860 }
861