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