PageRenderTime 70ms CodeModel.GetById 18ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/patch.c

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