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