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