xref: /dragonfly/contrib/cvs-1.12/src/patch.c (revision 9348a738)
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 
47 static const char *const patch_usage[] =
48 {
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",
60     "\t-D date\tDate.\n",
61     "\t-r rev\tRevision - symbolic or numeric.\n",
62     "(Specify the --help global option for a list of other help options)\n",
63     NULL
64 };
65 
66 
67 
68 int
69 patch (int argc, char **argv)
70 {
71     register int i;
72     int local = 0;
73     int c;
74     int err = 0;
75     DBM *db;
76 
77     if (argc == -1)
78 	usage (patch_usage);
79 
80     optind = 0;
81     while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
82     {
83 	switch (c)
84 	{
85 	    case 'Q':
86 	    case 'q':
87 		/* The CVS 1.5 client sends these options (in addition to
88 		   Global_option requests), so we must ignore them.  */
89 		if (!server_active)
90 		    error (1, 0,
91 			   "-q or -Q must be specified before \"%s\"",
92 			   cvs_cmd_name);
93 		break;
94 	    case 'f':
95 		force_tag_match = 0;
96 		break;
97 	    case 'l':
98 		local = 1;
99 		break;
100 	    case 'R':
101 		local = 0;
102 		break;
103 	    case 't':
104 		toptwo_diffs = 1;
105 		break;
106 	    case 's':
107 		patch_short = 1;
108 		break;
109 	    case 'D':
110 		if (rev2 != NULL || date2 != NULL)
111 		    error (1, 0,
112 		       "no more than two revisions/dates can be specified");
113 		if (rev1 != NULL || date1 != NULL)
114 		    date2 = Make_Date (optarg);
115 		else
116 		    date1 = Make_Date (optarg);
117 		break;
118 	    case 'r':
119 		if (rev2 != NULL || date2 != NULL)
120 		    error (1, 0,
121 		       "no more than two revisions/dates can be specified");
122 		if (rev1 != NULL || date1 != NULL)
123 		    rev2 = optarg;
124 		else
125 		    rev1 = optarg;
126 		break;
127 	    case 'k':
128 		if (options)
129 		    free (options);
130 		options = RCS_check_kflag (optarg);
131 		break;
132 	    case 'V':
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.  */
148 		error (1, 0,
149 		       "the -V option is obsolete and should not be used");
150 		break;
151 	    case 'u':
152 		unidiff = 1;		/* Unidiff */
153 		break;
154 	    case 'c':			/* Context diff */
155 		unidiff = 0;
156 		break;
157 	    case '?':
158 	    default:
159 		usage (patch_usage);
160 		break;
161 	}
162     }
163     argc -= optind;
164     argv += optind;
165 
166     /* Sanity checks */
167     if (argc < 1)
168 	usage (patch_usage);
169 
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!");
175 
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!");
182 
183     /* if options is NULL, make it a NULL string */
184     if (options == NULL)
185 	options = xstrdup ("");
186 
187 #ifdef CLIENT_SUPPORT
188     if (current_parsed_root->isremote)
189     {
190 	/* We're the client side.  Fire up the remote server.  */
191 	start_server ();
192 
193 	ign_setup ();
194 
195 	if (local)
196 	    send_arg("-l");
197 	if (!force_tag_match)
198 	    send_arg("-f");
199 	if (toptwo_diffs)
200 	    send_arg("-t");
201 	if (patch_short)
202 	    send_arg("-s");
203 	if (unidiff)
204 	    send_arg("-u");
205 
206 	if (rev1)
207 	    option_with_arg ("-r", rev1);
208 	if (date1)
209 	    client_senddate (date1);
210 	if (rev2)
211 	    option_with_arg ("-r", rev2);
212 	if (date2)
213 	    client_senddate (date2);
214 	if (options[0] != '\0')
215 	    send_arg (options);
216 
217 	{
218 	    int i;
219 	    for (i = 0; i < argc; ++i)
220 		send_arg (argv[i]);
221 	}
222 
223 	send_to_server ("rdiff\012", 0);
224         return get_responses_and_close ();
225     }
226 #endif
227 
228     /* clean up if we get a signal */
229 #ifdef SIGABRT
230     (void)SIG_register (SIGABRT, patch_cleanup);
231 #endif
232 #ifdef SIGHUP
233     (void)SIG_register (SIGHUP, patch_cleanup);
234 #endif
235 #ifdef SIGINT
236     (void)SIG_register (SIGINT, patch_cleanup);
237 #endif
238 #ifdef SIGQUIT
239     (void)SIG_register (SIGQUIT, patch_cleanup);
240 #endif
241 #ifdef SIGPIPE
242     (void)SIG_register (SIGPIPE, patch_cleanup);
243 #endif
244 #ifdef SIGTERM
245     (void)SIG_register (SIGTERM, patch_cleanup);
246 #endif
247 
248     db = open_module ();
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);
252     close_module (db);
253     free (options);
254     patch_cleanup (0);
255     return err;
256 }
257 
258 
259 
260 /*
261  * callback proc for doing the real work of patching
262  */
263 /* ARGSUSED */
264 static int
265 patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
266             int shorten, int local_specified, char *mname, char *msg)
267 {
268     char *myargv[2];
269     int err = 0;
270     int which;
271     char *repository;
272     char *where;
273 
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)" );
281 
282     repository = xmalloc (strlen (current_parsed_root->directory)
283                           + strlen (argv[0])
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)
289 		     + 1);
290     (void)strcpy (where, argv[0]);
291 
292     /* if mfile isn't null, we need to set up to do only part of the module */
293     if (mfile != NULL)
294     {
295 	char *cp;
296 	char *path;
297 
298 	/* if the portion of the module is a path, put the dir part on repos */
299 	if ((cp = strrchr (mfile, '/')) != NULL)
300 	{
301 	    *cp = '\0';
302 	    (void)strcat (repository, "/");
303 	    (void)strcat (repository, mfile);
304 	    (void)strcat (where, "/");
305 	    (void)strcat (where, mfile);
306 	    mfile = cp + 1;
307 	}
308 
309 	/* take care of the rest */
310 	path = xmalloc (strlen (repository) + strlen (mfile) + 2);
311 	(void)sprintf (path, "%s/%s", repository, mfile);
312 	if (isdir (path))
313 	{
314 	    /* directory means repository gets the dir tacked on */
315 	    (void)strcpy (repository, path);
316 	    (void)strcat (where, "/");
317 	    (void)strcat (where, mfile);
318 	}
319 	else
320 	{
321 	    myargv[0] = argv[0];
322 	    myargv[1] = mfile;
323 	    argc = 2;
324 	    argv = myargv;
325 	}
326 	free (path);
327     }
328 
329     /* cd to the starting repository */
330     if (CVS_CHDIR (repository) < 0)
331     {
332 	error (0, errno, "cannot chdir to %s", repository);
333 	free (repository);
334 	free (where);
335 	return 1;
336     }
337 
338     if (force_tag_match)
339 	which = W_REPOS | W_ATTIC;
340     else
341 	which = W_REPOS;
342 
343     if (rev1 != NULL && !rev1_validated)
344     {
345 	tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
346 			 repository, false);
347 	rev1_validated = 1;
348     }
349     if (rev2 != NULL && !rev2_validated)
350     {
351 	tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
352 			 repository, false);
353 	rev2_validated = 1;
354     }
355 
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 );
360     free (repository);
361     free (where);
362 
363     return err;
364 }
365 
366 
367 
368 /*
369  * Called to examine a particular RCS file, as appropriate with the options
370  * that were set above.
371  */
372 /* ARGSUSED */
373 static int
374 patch_fileproc (void *callerdat, struct file_info *finfo)
375 {
376     struct utimbuf t;
377     char *vers_tag, *vers_head;
378     char *rcs = NULL;
379     char *rcs_orig = NULL;
380     RCSNode *rcsfile;
381     FILE *fp1, *fp2, *fp3;
382     int ret = 0;
383     int isattic = 0;
384     int retcode = 0;
385     char *file1;
386     char *file2;
387     char *strippath;
388     char *line1, *line2;
389     size_t line1_chars_allocated;
390     size_t line2_chars_allocated;
391     char *cp1, *cp2;
392     FILE *fp;
393     int line_length;
394     int dargc = 0;
395     size_t darg_allocated = 0;
396     char **dargv = NULL;
397 
398     line1 = NULL;
399     line1_chars_allocated = 0;
400     line2 = NULL;
401     line2_chars_allocated = 0;
402     vers_tag = vers_head = NULL;
403 
404     /* find the parsed rcs file */
405     if ((rcsfile = finfo->rcs) == NULL)
406     {
407 	ret = 1;
408 	goto out2;
409     }
410     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
411 	isattic = 1;
412 
413     rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT);
414 
415     /* if vers_head is NULL, may have been removed from the release */
416     if (isattic && rev2 == NULL && date2 == NULL)
417 	vers_head = NULL;
418     else
419     {
420 	vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
421 				    NULL);
422 	if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
423 	{
424 	    free (vers_head);
425 	    vers_head = NULL;
426 	}
427     }
428 
429     if (toptwo_diffs)
430     {
431 	if (vers_head == NULL)
432 	{
433 	    ret = 1;
434 	    goto out2;
435 	}
436 
437 	if (!date1)
438 	    date1 = xmalloc (MAXDATELEN);
439 	*date1 = '\0';
440 	if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
441 	{
442 	    if (!really_quiet)
443 		error (0, 0, "cannot find date in rcs file %s revision %s",
444 		       rcs, vers_head);
445 	    ret = 1;
446 	    goto out2;
447 	}
448     }
449     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL);
450     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
451     {
452         free (vers_tag);
453 	vers_tag = NULL;
454     }
455 
456     if ((vers_tag == NULL && vers_head == NULL) ||
457         (vers_tag != NULL && vers_head != NULL &&
458 	 strcmp (vers_head, vers_tag) == 0))
459     {
460 	/* Nothing known about specified revs or
461 	 * not changed between releases.
462 	 */
463 	ret = 0;
464 	goto out2;
465     }
466 
467     if (patch_short && (vers_tag == NULL || vers_head == NULL))
468     {
469 	/* For adds & removes with a short patch requested, we can print our
470 	 * error message now and get out.
471 	 */
472 	cvs_output ("File ", 0);
473 	cvs_output (finfo->fullname, 0);
474 	if (vers_tag == NULL)
475 	{
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);
481 	}
482 	else
483 	{
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);
489 	}
490 	ret = 0;
491 	goto out2;
492     }
493 
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.
496      *
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
501      * simple file names.
502      */
503     if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
504     {
505 	error (0, errno, "cannot create temporary file %s", tmpfile1);
506 	ret = 1;
507 	goto out;
508     }
509     else
510 	if (fclose (fp1) < 0)
511 	    error (0, errno, "warning: cannot close %s", tmpfile1);
512     if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
513     {
514 	error (0, errno, "cannot create temporary file %s", tmpfile2);
515 	ret = 1;
516 	goto out;
517     }
518     else
519 	if (fclose (fp2) < 0)
520 	    error (0, errno, "warning: cannot close %s", tmpfile2);
521     if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
522     {
523 	error (0, errno, "cannot create temporary file %s", tmpfile3);
524 	ret = 1;
525 	goto out;
526     }
527     else
528 	if (fclose (fp3) < 0)
529 	    error (0, errno, "warning: cannot close %s", tmpfile3);
530 
531     if (vers_tag != NULL)
532     {
533 	retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options,
534                                 tmpfile1, NULL, NULL);
535 	if (retcode != 0)
536 	{
537 	    error (0, 0,
538 		   "cannot check out revision %s of %s", vers_tag, rcs);
539 	    ret = 1;
540 	    goto out;
541 	}
542 	memset ((char *) &t, 0, sizeof (t));
543 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
544 						    NULL, 0)) != -1)
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);
548     }
549     else if (toptwo_diffs)
550     {
551 	ret = 1;
552 	goto out;
553     }
554     if (vers_head != NULL)
555     {
556 	retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options,
557                                 tmpfile2, NULL, NULL);
558 	if (retcode != 0)
559 	{
560 	    error (0, 0,
561 		   "cannot check out revision %s of %s", vers_head, rcs);
562 	    ret = 1;
563 	    goto out;
564 	}
565 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
566 						    NULL, 0)) != -1)
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);
570     }
571 
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,
575 		       tmpfile3))
576     {
577 	case -1:			/* fork/wait failure */
578 	    error (1, errno, "fork for diff failed on %s", rcs);
579 	    break;
580 	case 0:				/* nothing to do */
581 	    break;
582 	case 1:
583 	    /*
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
588 	     * message.
589 	     */
590 	    if (patch_short)
591 	    {
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);
599 		ret = 0;
600 		goto out;
601 	    }
602 
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);
607 
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)
612 	    {
613 		if (feof (fp))
614 		    error (0, 0, "\
615 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
616 		else
617 		    error (0, errno,
618 			   "failed to read diff file header %s for %s",
619 			   tmpfile3, rcs);
620 		ret = 1;
621 		if (fclose (fp) < 0)
622 		    error (0, errno, "error closing %s", tmpfile3);
623 		goto out;
624 	    }
625 	    if (!unidiff)
626 	    {
627 		if (strncmp (line1, "*** ", 4) != 0 ||
628 		    strncmp (line2, "--- ", 4) != 0 ||
629 		    (cp1 = strchr (line1, '\t')) == NULL ||
630 		    (cp2 = strchr (line2, '\t')) == NULL)
631 		{
632 		    error (0, 0, "invalid diff header for %s", rcs);
633 		    ret = 1;
634 		    if (fclose (fp) < 0)
635 			error (0, errno, "error closing %s", tmpfile3);
636 		    goto out;
637 		}
638 	    }
639 	    else
640 	    {
641 		if (strncmp (line1, "--- ", 4) != 0 ||
642 		    strncmp (line2, "+++ ", 4) != 0 ||
643 		    (cp1 = strchr (line1, '\t')) == NULL ||
644 		    (cp2 = strchr  (line2, '\t')) == NULL)
645 		{
646 		    error (0, 0, "invalid unidiff header for %s", rcs);
647 		    ret = 1;
648 		    if (fclose (fp) < 0)
649 			error (0, errno, "error closing %s", tmpfile3);
650 		    goto out;
651 		}
652 	    }
653 	    assert (current_parsed_root != NULL);
654 	    assert (current_parsed_root->directory != NULL);
655 
656 	    strippath = Xasprintf ("%s/", current_parsed_root->directory);
657 
658 	    if (strncmp (rcs, strippath, strlen (strippath)) == 0)
659 		rcs += strlen (strippath);
660 	    free (strippath);
661 	    if (vers_tag != NULL)
662 		file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
663 	    else
664 		file1 = xstrdup (DEVNULL);
665 
666 	    file2 = Xasprintf ("%s:%s", finfo->fullname,
667 			       vers_head ? vers_head : "removed");
668 
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
671 	       program.  */
672 	    if (unidiff)
673 	    {
674 		cvs_output ("diff -u ", 0);
675 		cvs_output (file1, 0);
676 		cvs_output (" ", 1);
677 		cvs_output (file2, 0);
678 		cvs_output ("\n", 1);
679 
680 		cvs_output ("--- ", 0);
681 		cvs_output (file1, 0);
682 		cvs_output (cp1, 0);
683 		cvs_output ("+++ ", 0);
684 	    }
685 	    else
686 	    {
687 		cvs_output ("diff -c ", 0);
688 		cvs_output (file1, 0);
689 		cvs_output (" ", 1);
690 		cvs_output (file2, 0);
691 		cvs_output ("\n", 1);
692 
693 		cvs_output ("*** ", 0);
694 		cvs_output (file1, 0);
695 		cvs_output (cp1, 0);
696 		cvs_output ("--- ", 0);
697 	    }
698 
699 	    cvs_output (finfo->fullname, 0);
700 	    cvs_output (cp2, 0);
701 
702 	    /* spew the rest of the diff out */
703 	    while ((line_length
704 		    = getline (&line1, &line1_chars_allocated, fp))
705 		   >= 0)
706 		cvs_output (line1, 0);
707 	    if (line_length < 0 && !feof (fp))
708 		error (0, errno, "cannot read %s", tmpfile3);
709 
710 	    if (fclose (fp) < 0)
711 		error (0, errno, "cannot close %s", tmpfile3);
712 	    free (file1);
713 	    free (file2);
714 	    break;
715 	default:
716 	    error (0, 0, "diff failed for %s", finfo->fullname);
717     }
718   out:
719     if (line1)
720         free (line1);
721     if (line2)
722         free (line2);
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);
729     free (tmpfile1);
730     free (tmpfile2);
731     free (tmpfile3);
732     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
733     if (darg_allocated)
734     {
735 	run_arg_free_p (dargc, dargv);
736 	free (dargv);
737     }
738 
739  out2:
740     if (vers_tag != NULL)
741 	free (vers_tag);
742     if (vers_head != NULL)
743 	free (vers_head);
744     if (rcs_orig)
745 	free (rcs_orig);
746     return ret;
747 }
748 
749 
750 
751 /*
752  * Print a warm fuzzy message
753  */
754 /* ARGSUSED */
755 static Dtype
756 patch_dirproc (void *callerdat, const char *dir, const char *repos,
757                const char *update_dir, List *entries)
758 {
759     if (!quiet)
760 	error (0, 0, "Diffing %s", update_dir);
761     return R_PROCESS;
762 }
763 
764 
765 
766 /*
767  * Clean up temporary files
768  */
769 static RETSIGTYPE
770 patch_cleanup (int sig)
771 {
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.  */
775 
776     if (tmpfile1 != NULL)
777     {
778 	if (unlink_file (tmpfile1) < 0
779 	    && !existence_error (errno))
780 	    error (0, errno, "cannot remove %s", tmpfile1);
781 	free (tmpfile1);
782     }
783     if (tmpfile2 != NULL)
784     {
785 	if (unlink_file (tmpfile2) < 0
786 	    && !existence_error (errno))
787 	    error (0, errno, "cannot remove %s", tmpfile2);
788 	free (tmpfile2);
789     }
790     if (tmpfile3 != NULL)
791     {
792 	if (unlink_file (tmpfile3) < 0
793 	    && !existence_error (errno))
794 	    error (0, errno, "cannot remove %s", tmpfile3);
795 	free (tmpfile3);
796     }
797     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
798 
799     if (sig != 0)
800     {
801 	const char *name;
802 	char temp[10];
803 
804 	switch (sig)
805 	{
806 #ifdef SIGABRT
807 	case SIGABRT:
808 	    name = "abort";
809 	    break;
810 #endif
811 #ifdef SIGHUP
812 	case SIGHUP:
813 	    name = "hangup";
814 	    break;
815 #endif
816 #ifdef SIGINT
817 	case SIGINT:
818 	    name = "interrupt";
819 	    break;
820 #endif
821 #ifdef SIGQUIT
822 	case SIGQUIT:
823 	    name = "quit";
824 	    break;
825 #endif
826 #ifdef SIGPIPE
827 	case SIGPIPE:
828 	    name = "broken pipe";
829 	    break;
830 #endif
831 #ifdef SIGTERM
832 	case SIGTERM:
833 	    name = "termination";
834 	    break;
835 #endif
836 	default:
837 	    /* This case should never be reached, because we list
838 	       above all the signals for which we actually establish a
839 	       signal handler.  */
840 	    sprintf (temp, "%d", sig);
841 	    name = temp;
842 	    break;
843 	}
844 	error (0, 0, "received %s signal", name);
845     }
846 }
847