xref: /openbsd/gnu/usr.bin/cvs/src/patch.c (revision f9bbbf45)
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
patch(argc,argv)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
patch_proc(argc,argv,xwhere,mwhere,mfile,shorten,local_specified,mname,msg)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
patch_fileproc(callerdat,finfo)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     char *rcs_orig = NULL;
379     RCSNode *rcsfile;
380     FILE *fp1, *fp2, *fp3;
381     int ret = 0;
382     int isattic = 0;
383     int retcode = 0;
384     char *file1;
385     char *file2;
386     char *strippath;
387     char *line1, *line2;
388     size_t line1_chars_allocated;
389     size_t line2_chars_allocated;
390     char *cp1, *cp2;
391     FILE *fp;
392     int line_length;
393 
394     line1 = NULL;
395     line1_chars_allocated = 0;
396     line2 = NULL;
397     line2_chars_allocated = 0;
398 
399     /* find the parsed rcs file */
400     if ((rcsfile = finfo->rcs) == NULL)
401     {
402 	ret = 1;
403 	goto out2;
404     }
405     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
406 	isattic = 1;
407 
408     rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
409     (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
410 
411     /* if vers_head is NULL, may have been removed from the release */
412     if (isattic && rev2 == NULL && date2 == NULL)
413 	vers_head = NULL;
414     else
415     {
416 	vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
417 				    (int *) NULL);
418 	if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
419 	{
420 	    free (vers_head);
421 	    vers_head = NULL;
422 	}
423     }
424 
425     if (toptwo_diffs)
426     {
427 	if (vers_head == NULL)
428 	{
429 	    ret = 1;
430 	    goto out2;
431 	}
432 
433 	if (!date1)
434 	    date1 = xmalloc (MAXDATELEN);
435 	*date1 = '\0';
436 	if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
437 	{
438 	    if (!really_quiet)
439 		error (0, 0, "cannot find date in rcs file %s revision %s",
440 		       rcs, vers_head);
441 	    ret = 1;
442 	    goto out2;
443 	}
444     }
445     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
446 			       (int *) NULL);
447     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
448     {
449         free (vers_tag);
450 	vers_tag = NULL;
451     }
452 
453     if (vers_tag == NULL && vers_head == NULL)
454     {
455 	/* Nothing known about specified revs.  */
456 	ret = 0;
457 	goto out2;
458     }
459 
460     if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
461     {
462 	/* Not changed between releases.  */
463 	ret = 0;
464 	goto out2;
465     }
466 
467     if (patch_short)
468     {
469 	cvs_output ("File ", 0);
470 	cvs_output (finfo->fullname, 0);
471 	if (vers_tag == NULL)
472 	{
473 	    cvs_output (" is new; current revision ", 0);
474 	    cvs_output (vers_head, 0);
475 	    cvs_output ("\n", 1);
476 	}
477 	else if (vers_head == NULL)
478 	{
479 	    cvs_output (" is removed; not included in ", 0);
480 	    if (rev2 != NULL)
481 	    {
482 		cvs_output ("release tag ", 0);
483 		cvs_output (rev2, 0);
484 	    }
485 	    else if (date2 != NULL)
486 	    {
487 		cvs_output ("release date ", 0);
488 		cvs_output (date2, 0);
489 	    }
490 	    else
491 		cvs_output ("current release", 0);
492 	    cvs_output ("\n", 1);
493 	}
494 	else
495 	{
496 	    cvs_output (" changed from revision ", 0);
497 	    cvs_output (vers_tag, 0);
498 	    cvs_output (" to ", 0);
499 	    cvs_output (vers_head, 0);
500 	    cvs_output ("\n", 1);
501 	}
502 	ret = 0;
503 	goto out2;
504     }
505 
506     /* Create 3 empty files.  I'm not really sure there is any advantage
507      * to doing so now rather than just waiting until later.
508      *
509      * There is - cvs_temp_file opens the file so that it can guarantee that
510      * we have exclusive write access to the file.  Unfortunately we spoil that
511      * by closing it and reopening it again.  Of course any better solution
512      * requires that the RCS functions accept open file pointers rather than
513      * simple file names.
514      */
515     if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
516     {
517 	error (0, errno, "cannot create temporary file %s", tmpfile1);
518 	ret = 1;
519 	goto out;
520     }
521     else
522 	if (fclose (fp1) < 0)
523 	    error (0, errno, "warning: cannot close %s", tmpfile1);
524     if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
525     {
526 	error (0, errno, "cannot create temporary file %s", tmpfile2);
527 	ret = 1;
528 	goto out;
529     }
530     else
531 	if (fclose (fp2) < 0)
532 	    error (0, errno, "warning: cannot close %s", tmpfile2);
533     if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
534     {
535 	error (0, errno, "cannot create temporary file %s", tmpfile3);
536 	ret = 1;
537 	goto out;
538     }
539     else
540 	if (fclose (fp3) < 0)
541 	    error (0, errno, "warning: cannot close %s", tmpfile3);
542 
543     if (vers_tag != NULL)
544     {
545 	retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag,
546 				rev1, options, tmpfile1,
547 				(RCSCHECKOUTPROC) NULL, (void *) NULL);
548 	if (retcode != 0)
549 	{
550 	    error (0, 0,
551 		   "cannot check out revision %s of %s", vers_tag, rcs);
552 	    ret = 1;
553 	    goto out;
554 	}
555 	memset ((char *) &t, 0, sizeof (t));
556 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
557 						    (char *) 0, 0)) != -1)
558 	    /* I believe this timestamp only affects the dates in our diffs,
559 	       and therefore should be on the server, not the client.  */
560 	    (void) utime (tmpfile1, &t);
561     }
562     else if (toptwo_diffs)
563     {
564 	ret = 1;
565 	goto out;
566     }
567     if (vers_head != NULL)
568     {
569 	retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head,
570 				rev2, options, tmpfile2,
571 				(RCSCHECKOUTPROC) NULL, (void *) NULL);
572 	if (retcode != 0)
573 	{
574 	    error (0, 0,
575 		   "cannot check out revision %s of %s", vers_head, rcs);
576 	    ret = 1;
577 	    goto out;
578 	}
579 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
580 						    (char *) 0, 0)) != -1)
581 	    /* I believe this timestamp only affects the dates in our diffs,
582 	       and therefore should be on the server, not the client.  */
583 	    (void) utime (tmpfile2, &t);
584     }
585 
586     switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, unidiff ? "-u" : "-c", tmpfile3))
587     {
588 	case -1:			/* fork/wait failure */
589 	    error (1, errno, "fork for diff failed on %s", rcs);
590 	    break;
591 	case 0:				/* nothing to do */
592 	    break;
593 	case 1:
594 	    /*
595 	     * The two revisions are really different, so read the first two
596 	     * lines of the diff output file, and munge them to include more
597 	     * reasonable file names that "patch" will understand.
598 	     */
599 
600 	    /* Output an "Index:" line for patch to use */
601 	    cvs_output ("Index: ", 0);
602 	    cvs_output (finfo->fullname, 0);
603 	    cvs_output ("\n", 1);
604 
605 	    fp = open_file (tmpfile3, "r");
606 	    if (get_line (&line1, &line1_chars_allocated, fp) < 0 ||
607 		get_line (&line2, &line2_chars_allocated, fp) < 0)
608 	    {
609 		if (feof (fp))
610 		    error (0, 0, "\
611 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
612 		else
613 		    error (0, errno,
614 			   "failed to read diff file header %s for %s",
615 			   tmpfile3, rcs);
616 		ret = 1;
617 		if (fclose (fp) < 0)
618 		    error (0, errno, "error closing %s", tmpfile3);
619 		goto out;
620 	    }
621 	    if (!unidiff)
622 	    {
623 		if (strncmp (line1, "*** ", 4) != 0 ||
624 		    strncmp (line2, "--- ", 4) != 0 ||
625 		    (cp1 = strchr (line1, '\t')) == NULL ||
626 		    (cp2 = strchr (line2, '\t')) == NULL)
627 		{
628 		    error (0, 0, "invalid diff header for %s", rcs);
629 		    ret = 1;
630 		    if (fclose (fp) < 0)
631 			error (0, errno, "error closing %s", tmpfile3);
632 		    goto out;
633 		}
634 	    }
635 	    else
636 	    {
637 		if (strncmp (line1, "--- ", 4) != 0 ||
638 		    strncmp (line2, "+++ ", 4) != 0 ||
639 		    (cp1 = strchr (line1, '\t')) == NULL ||
640 		    (cp2 = strchr  (line2, '\t')) == NULL)
641 		{
642 		    error (0, 0, "invalid unidiff header for %s", rcs);
643 		    ret = 1;
644 		    if (fclose (fp) < 0)
645 			error (0, errno, "error closing %s", tmpfile3);
646 		    goto out;
647 		}
648 	    }
649 	    assert (current_parsed_root != NULL);
650 	    assert (current_parsed_root->directory != NULL);
651 	    {
652 		strippath = xmalloc (strlen (current_parsed_root->directory) + 2);
653 		(void) sprintf (strippath, "%s/", current_parsed_root->directory);
654 	    }
655 	    /*else
656 		strippath = xstrdup (REPOS_STRIP); */
657 	    if (strncmp (rcs, strippath, strlen (strippath)) == 0)
658 		rcs += strlen (strippath);
659 	    free (strippath);
660 	    if (vers_tag != NULL)
661 	    {
662 		file1 = xmalloc (strlen (finfo->fullname)
663 				 + strlen (vers_tag)
664 				 + 10);
665 		(void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
666 	    }
667 	    else
668 	    {
669 		file1 = xstrdup (DEVNULL);
670 	    }
671 	    file2 = xmalloc (strlen (finfo->fullname)
672 			     + (vers_head != NULL ? strlen (vers_head) : 10)
673 			     + 10);
674 	    (void) sprintf (file2, "%s:%s", finfo->fullname,
675 			    vers_head ? vers_head : "removed");
676 
677 	    /* Note that the string "diff" is specified by POSIX (for -c)
678 	       and is part of the diff output format, not the name of a
679 	       program.  */
680 	    if (unidiff)
681 	    {
682 		cvs_output ("diff -u ", 0);
683 		cvs_output (file1, 0);
684 		cvs_output (" ", 1);
685 		cvs_output (file2, 0);
686 		cvs_output ("\n", 1);
687 
688 		cvs_output ("--- ", 0);
689 		cvs_output (file1, 0);
690 		cvs_output (cp1, 0);
691 		cvs_output ("+++ ", 0);
692 	    }
693 	    else
694 	    {
695 		cvs_output ("diff -c ", 0);
696 		cvs_output (file1, 0);
697 		cvs_output (" ", 1);
698 		cvs_output (file2, 0);
699 		cvs_output ("\n", 1);
700 
701 		cvs_output ("*** ", 0);
702 		cvs_output (file1, 0);
703 		cvs_output (cp1, 0);
704 		cvs_output ("--- ", 0);
705 	    }
706 
707 	    cvs_output (finfo->fullname, 0);
708 	    cvs_output (cp2, 0);
709 
710 	    /* spew the rest of the diff out */
711 	    while ((line_length
712 		    = get_line (&line1, &line1_chars_allocated, fp))
713 		   >= 0)
714 		cvs_output (line1, 0);
715 	    if (line_length < 0 && !feof (fp))
716 		error (0, errno, "cannot read %s", tmpfile3);
717 
718 	    if (fclose (fp) < 0)
719 		error (0, errno, "cannot close %s", tmpfile3);
720 	    free (file1);
721 	    free (file2);
722 	    break;
723 	default:
724 	    error (0, 0, "diff failed for %s", finfo->fullname);
725     }
726   out:
727     if (line1)
728         free (line1);
729     if (line2)
730         free (line2);
731     if (CVS_UNLINK (tmpfile1) < 0)
732 	error (0, errno, "cannot unlink %s", tmpfile1);
733     if (CVS_UNLINK (tmpfile2) < 0)
734 	error (0, errno, "cannot unlink %s", tmpfile2);
735     if (CVS_UNLINK (tmpfile3) < 0)
736 	error (0, errno, "cannot unlink %s", tmpfile3);
737     free (tmpfile1);
738     free (tmpfile2);
739     free (tmpfile3);
740     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
741 
742  out2:
743     if (vers_tag != NULL)
744 	free (vers_tag);
745     if (vers_head != NULL)
746 	free (vers_head);
747     if (rcs_orig)
748 	free (rcs_orig);
749     return (ret);
750 }
751 
752 /*
753  * Print a warm fuzzy message
754  */
755 /* ARGSUSED */
756 static Dtype
patch_dirproc(callerdat,dir,repos,update_dir,entries)757 patch_dirproc (callerdat, dir, repos, update_dir, entries)
758     void *callerdat;
759     char *dir;
760     char *repos;
761     char *update_dir;
762     List *entries;
763 {
764     if (!quiet)
765 	error (0, 0, "Diffing %s", update_dir);
766     return (R_PROCESS);
767 }
768 
769 /*
770  * Clean up temporary files
771  */
772 static RETSIGTYPE
patch_cleanup()773 patch_cleanup ()
774 {
775     /* Note that the checks for existence_error are because we are
776        called from a signal handler, without SIG_begincrsect, so
777        we don't know whether the files got created.  */
778 
779     if (tmpfile1 != NULL)
780     {
781 	if (unlink_file (tmpfile1) < 0
782 	    && !existence_error (errno))
783 	    error (0, errno, "cannot remove %s", tmpfile1);
784 	free (tmpfile1);
785     }
786     if (tmpfile2 != NULL)
787     {
788 	if (unlink_file (tmpfile2) < 0
789 	    && !existence_error (errno))
790 	    error (0, errno, "cannot remove %s", tmpfile2);
791 	free (tmpfile2);
792     }
793     if (tmpfile3 != NULL)
794     {
795 	if (unlink_file (tmpfile3) < 0
796 	    && !existence_error (errno))
797 	    error (0, errno, "cannot remove %s", tmpfile3);
798 	free (tmpfile3);
799     }
800     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
801 }
802