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