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
19static RETSIGTYPE patch_cleanup PROTO((void));
20static Dtype patch_dirproc PROTO ((void *callerdat, char *dir,
21				   char *repos, char *update_dir,
22				   List *entries));
23static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
24static 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
28static int force_tag_match = 1;
29static int patch_short = 0;
30static int toptwo_diffs = 0;
31static int local = 0;
32static char *options = NULL;
33static char *rev1 = NULL;
34static int rev1_validated = 0;
35static char *rev2 = NULL;
36static int rev2_validated = 0;
37static char *date1 = NULL;
38static char *date2 = NULL;
39static char *tmpfile1 = NULL;
40static char *tmpfile2 = NULL;
41static char *tmpfile3 = NULL;
42static int unidiff = 0;
43
44static 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
62int
63patch (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 */
267static int
268patch_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 */
370static int
371patch_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, "\
611failed 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 */
756static Dtype
757patch_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 */
772static RETSIGTYPE
773patch_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