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