Merge from vendor branch TNFTP:
[dragonfly.git] / contrib / cvs-1.12 / 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] [-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.\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:cuftsQqlRD: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 = 0;
156                 break;
157             case '?':
158             default:
159                 usage (patch_usage);
160                 break;
161         }
162     }
163     argc -= optind;
164     argv += optind;
165
166     /* Sanity checks */
167     if (argc < 1)
168         usage (patch_usage);
169
170     if (toptwo_diffs && patch_short)
171         error (1, 0, "-t and -s options are mutually exclusive");
172     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
173                          rev1 != NULL || rev2 != NULL))
174         error (1, 0, "must not specify revisions/dates with -t option!");
175
176     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
177                           rev1 == NULL && rev2 == NULL))
178         error (1, 0, "must specify at least one revision/date!");
179     if (date1 != NULL && date2 != NULL)
180         if (RCS_datecmp (date1, date2) >= 0)
181             error (1, 0, "second date must come after first date!");
182
183     /* if options is NULL, make it a NULL string */
184     if (options == NULL)
185         options = xstrdup ("");
186
187 #ifdef CLIENT_SUPPORT
188     if (current_parsed_root->isremote)
189     {
190         /* We're the client side.  Fire up the remote server.  */
191         start_server ();
192         
193         ign_setup ();
194
195         if (local)
196             send_arg("-l");
197         if (!force_tag_match)
198             send_arg("-f");
199         if (toptwo_diffs)
200             send_arg("-t");
201         if (patch_short)
202             send_arg("-s");
203         if (unidiff)
204             send_arg("-u");
205
206         if (rev1)
207             option_with_arg ("-r", rev1);
208         if (date1)
209             client_senddate (date1);
210         if (rev2)
211             option_with_arg ("-r", rev2);
212         if (date2)
213             client_senddate (date2);
214         if (options[0] != '\0')
215             send_arg (options);
216
217         {
218             int i;
219             for (i = 0; i < argc; ++i)
220                 send_arg (argv[i]);
221         }
222
223         send_to_server ("rdiff\012", 0);
224         return get_responses_and_close ();
225     }
226 #endif
227
228     /* clean up if we get a signal */
229 #ifdef SIGABRT
230     (void)SIG_register (SIGABRT, patch_cleanup);
231 #endif
232 #ifdef SIGHUP
233     (void)SIG_register (SIGHUP, patch_cleanup);
234 #endif
235 #ifdef SIGINT
236     (void)SIG_register (SIGINT, patch_cleanup);
237 #endif
238 #ifdef SIGQUIT
239     (void)SIG_register (SIGQUIT, patch_cleanup);
240 #endif
241 #ifdef SIGPIPE
242     (void)SIG_register (SIGPIPE, patch_cleanup);
243 #endif
244 #ifdef SIGTERM
245     (void)SIG_register (SIGTERM, patch_cleanup);
246 #endif
247
248     db = open_module ();
249     for (i = 0; i < argc; i++)
250         err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
251                           NULL, 0, local, 0, 0, NULL);
252     close_module (db);
253     free (options);
254     patch_cleanup (0);
255     return err;
256 }
257
258
259
260 /*
261  * callback proc for doing the real work of patching
262  */
263 /* ARGSUSED */
264 static int
265 patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
266             int shorten, int local_specified, char *mname, char *msg)
267 {
268     char *myargv[2];
269     int err = 0;
270     int which;
271     char *repository;
272     char *where;
273
274     TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )",
275             xwhere ? xwhere : "(null)",
276             mwhere ? mwhere : "(null)",
277             mfile ? mfile : "(null)",
278             shorten, local_specified,
279             mname ? mname : "(null)",
280             msg ? msg : "(null)" );
281
282     repository = xmalloc (strlen (current_parsed_root->directory)
283                           + strlen (argv[0])
284                           + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
285     (void)sprintf (repository, "%s/%s",
286                    current_parsed_root->directory, argv[0]);
287     where = xmalloc (strlen (argv[0])
288                      + (mfile == NULL ? 0 : strlen (mfile) + 1)
289                      + 1);
290     (void)strcpy (where, argv[0]);
291
292     /* if mfile isn't null, we need to set up to do only part of the module */
293     if (mfile != NULL)
294     {
295         char *cp;
296         char *path;
297
298         /* if the portion of the module is a path, put the dir part on repos */
299         if ((cp = strrchr (mfile, '/')) != NULL)
300         {
301             *cp = '\0';
302             (void)strcat (repository, "/");
303             (void)strcat (repository, mfile);
304             (void)strcat (where, "/");
305             (void)strcat (where, mfile);
306             mfile = cp + 1;
307         }
308
309         /* take care of the rest */
310         path = xmalloc (strlen (repository) + strlen (mfile) + 2);
311         (void)sprintf (path, "%s/%s", repository, mfile);
312         if (isdir (path))
313         {
314             /* directory means repository gets the dir tacked on */
315             (void)strcpy (repository, path);
316             (void)strcat (where, "/");
317             (void)strcat (where, mfile);
318         }
319         else
320         {
321             myargv[0] = argv[0];
322             myargv[1] = mfile;
323             argc = 2;
324             argv = myargv;
325         }
326         free (path);
327     }
328
329     /* cd to the starting repository */
330     if (CVS_CHDIR (repository) < 0)
331     {
332         error (0, errno, "cannot chdir to %s", repository);
333         free (repository);
334         free (where);
335         return 1;
336     }
337
338     if (force_tag_match)
339         which = W_REPOS | W_ATTIC;
340     else
341         which = W_REPOS;
342
343     if (rev1 != NULL && !rev1_validated)
344     {
345         tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
346                          repository, false);
347         rev1_validated = 1;
348     }
349     if (rev2 != NULL && !rev2_validated)
350     {
351         tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
352                          repository, false);
353         rev2_validated = 1;
354     }
355
356     /* start the recursion processor */
357     err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL,
358                            argc - 1, argv + 1, local_specified,
359                            which, 0, CVS_LOCK_READ, where, 1, repository );
360     free (repository);
361     free (where);
362
363     return err;
364 }
365
366
367
368 /*
369  * Called to examine a particular RCS file, as appropriate with the options
370  * that were set above.
371  */
372 /* ARGSUSED */
373 static int
374 patch_fileproc (void *callerdat, struct file_info *finfo)
375 {
376     struct utimbuf t;
377     char *vers_tag, *vers_head;
378     char *rcs = NULL;
379     char *rcs_orig = NULL;
380     RCSNode *rcsfile;
381     FILE *fp1, *fp2, *fp3;
382     int ret = 0;
383     int isattic = 0;
384     int retcode = 0;
385     char *file1;
386     char *file2;
387     char *strippath;
388     char *line1, *line2;
389     size_t line1_chars_allocated;
390     size_t line2_chars_allocated;
391     char *cp1, *cp2;
392     FILE *fp;
393     int line_length;
394     int dargc = 0;
395     size_t darg_allocated = 0;
396     char **dargv = NULL;
397
398     line1 = NULL;
399     line1_chars_allocated = 0;
400     line2 = NULL;
401     line2_chars_allocated = 0;
402     vers_tag = vers_head = NULL;
403
404     /* find the parsed rcs file */
405     if ((rcsfile = finfo->rcs) == NULL)
406     {
407         ret = 1;
408         goto out2;
409     }
410     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
411         isattic = 1;
412
413     rcs_orig = rcs = Xasprintf ("%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                                                     NULL, 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                                                     NULL, 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     if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
573     else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
574     switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
575                        tmpfile3))
576     {
577         case -1:                        /* fork/wait failure */
578             error (1, errno, "fork for diff failed on %s", rcs);
579             break;
580         case 0:                         /* nothing to do */
581             break;
582         case 1:
583             /*
584              * The two revisions are really different, so read the first two
585              * lines of the diff output file, and munge them to include more
586              * reasonable file names that "patch" will understand, unless the
587              * user wanted a short patch.  In that case, just output the short
588              * message.
589              */
590             if (patch_short)
591             {
592                 cvs_output ("File ", 0);
593                 cvs_output (finfo->fullname, 0);
594                 cvs_output (" changed from revision ", 0);
595                 cvs_output (vers_tag, 0);
596                 cvs_output (" to ", 0);
597                 cvs_output (vers_head, 0);
598                 cvs_output ("\n", 1);
599                 ret = 0;
600                 goto out;
601             }
602
603             /* Output an "Index:" line for patch to use */
604             cvs_output ("Index: ", 0);
605             cvs_output (finfo->fullname, 0);
606             cvs_output ("\n", 1);
607
608             /* Now the munging. */
609             fp = xfopen (tmpfile3, "r");
610             if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
611                 getline (&line2, &line2_chars_allocated, fp) < 0)
612             {
613                 if (feof (fp))
614                     error (0, 0, "\
615 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
616                 else
617                     error (0, errno,
618                            "failed to read diff file header %s for %s",
619                            tmpfile3, rcs);
620                 ret = 1;
621                 if (fclose (fp) < 0)
622                     error (0, errno, "error closing %s", tmpfile3);
623                 goto out;
624             }
625             if (!unidiff)
626             {
627                 if (strncmp (line1, "*** ", 4) != 0 ||
628                     strncmp (line2, "--- ", 4) != 0 ||
629                     (cp1 = strchr (line1, '\t')) == NULL ||
630                     (cp2 = strchr (line2, '\t')) == NULL)
631                 {
632                     error (0, 0, "invalid diff header for %s", rcs);
633                     ret = 1;
634                     if (fclose (fp) < 0)
635                         error (0, errno, "error closing %s", tmpfile3);
636                     goto out;
637                 }
638             }
639             else
640             {
641                 if (strncmp (line1, "--- ", 4) != 0 ||
642                     strncmp (line2, "+++ ", 4) != 0 ||
643                     (cp1 = strchr (line1, '\t')) == NULL ||
644                     (cp2 = strchr  (line2, '\t')) == NULL)
645                 {
646                     error (0, 0, "invalid unidiff header for %s", rcs);
647                     ret = 1;
648                     if (fclose (fp) < 0)
649                         error (0, errno, "error closing %s", tmpfile3);
650                     goto out;
651                 }
652             }
653             assert (current_parsed_root != NULL);
654             assert (current_parsed_root->directory != NULL);
655
656             strippath = Xasprintf ("%s/", current_parsed_root->directory);
657
658             if (strncmp (rcs, strippath, strlen (strippath)) == 0)
659                 rcs += strlen (strippath);
660             free (strippath);
661             if (vers_tag != NULL)
662                 file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
663             else
664                 file1 = xstrdup (DEVNULL);
665
666             file2 = Xasprintf ("%s:%s", finfo->fullname,
667                                vers_head ? vers_head : "removed");
668
669             /* Note that the string "diff" is specified by POSIX (for -c)
670                and is part of the diff output format, not the name of a
671                program.  */
672             if (unidiff)
673             {
674                 cvs_output ("diff -u ", 0);
675                 cvs_output (file1, 0);
676                 cvs_output (" ", 1);
677                 cvs_output (file2, 0);
678                 cvs_output ("\n", 1);
679
680                 cvs_output ("--- ", 0);
681                 cvs_output (file1, 0);
682                 cvs_output (cp1, 0);
683                 cvs_output ("+++ ", 0);
684             }
685             else
686             {
687                 cvs_output ("diff -c ", 0);
688                 cvs_output (file1, 0);
689                 cvs_output (" ", 1);
690                 cvs_output (file2, 0);
691                 cvs_output ("\n", 1);
692
693                 cvs_output ("*** ", 0);
694                 cvs_output (file1, 0);
695                 cvs_output (cp1, 0);
696                 cvs_output ("--- ", 0);
697             }
698
699             cvs_output (finfo->fullname, 0);
700             cvs_output (cp2, 0);
701
702             /* spew the rest of the diff out */
703             while ((line_length
704                     = getline (&line1, &line1_chars_allocated, fp))
705                    >= 0)
706                 cvs_output (line1, 0);
707             if (line_length < 0 && !feof (fp))
708                 error (0, errno, "cannot read %s", tmpfile3);
709
710             if (fclose (fp) < 0)
711                 error (0, errno, "cannot close %s", tmpfile3);
712             free (file1);
713             free (file2);
714             break;
715         default:
716             error (0, 0, "diff failed for %s", finfo->fullname);
717     }
718   out:
719     if (line1)
720         free (line1);
721     if (line2)
722         free (line2);
723     if (CVS_UNLINK (tmpfile1) < 0)
724         error (0, errno, "cannot unlink %s", tmpfile1);
725     if (CVS_UNLINK (tmpfile2) < 0)
726         error (0, errno, "cannot unlink %s", tmpfile2);
727     if (CVS_UNLINK (tmpfile3) < 0)
728         error (0, errno, "cannot unlink %s", tmpfile3);
729     free (tmpfile1);
730     free (tmpfile2);
731     free (tmpfile3);
732     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
733     if (darg_allocated)
734     {
735         run_arg_free_p (dargc, dargv);
736         free (dargv);
737     }
738
739  out2:
740     if (vers_tag != NULL)
741         free (vers_tag);
742     if (vers_head != NULL)
743         free (vers_head);
744     if (rcs_orig)
745         free (rcs_orig);
746     return ret;
747 }
748
749
750
751 /*
752  * Print a warm fuzzy message
753  */
754 /* ARGSUSED */
755 static Dtype
756 patch_dirproc (void *callerdat, const char *dir, const char *repos,
757                const char *update_dir, List *entries)
758 {
759     if (!quiet)
760         error (0, 0, "Diffing %s", update_dir);
761     return R_PROCESS;
762 }
763
764
765
766 /*
767  * Clean up temporary files
768  */
769 static RETSIGTYPE
770 patch_cleanup (int sig)
771 {
772     /* Note that the checks for existence_error are because we are
773        called from a signal handler, without SIG_begincrsect, so
774        we don't know whether the files got created.  */
775
776     if (tmpfile1 != NULL)
777     {
778         if (unlink_file (tmpfile1) < 0
779             && !existence_error (errno))
780             error (0, errno, "cannot remove %s", tmpfile1);
781         free (tmpfile1);
782     }
783     if (tmpfile2 != NULL)
784     {
785         if (unlink_file (tmpfile2) < 0
786             && !existence_error (errno))
787             error (0, errno, "cannot remove %s", tmpfile2);
788         free (tmpfile2);
789     }
790     if (tmpfile3 != NULL)
791     {
792         if (unlink_file (tmpfile3) < 0
793             && !existence_error (errno))
794             error (0, errno, "cannot remove %s", tmpfile3);
795         free (tmpfile3);
796     }
797     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
798
799     if (sig != 0)
800     {
801         const char *name;
802         char temp[10];
803
804         switch (sig)
805         {
806 #ifdef SIGABRT
807         case SIGABRT:
808             name = "abort";
809             break;
810 #endif
811 #ifdef SIGHUP
812         case SIGHUP:
813             name = "hangup";
814             break;
815 #endif
816 #ifdef SIGINT
817         case SIGINT:
818             name = "interrupt";
819             break;
820 #endif
821 #ifdef SIGQUIT
822         case SIGQUIT:
823             name = "quit";
824             break;
825 #endif
826 #ifdef SIGPIPE
827         case SIGPIPE:
828             name = "broken pipe";
829             break;
830 #endif
831 #ifdef SIGTERM
832         case SIGTERM:
833             name = "termination";
834             break;
835 #endif
836         default:
837             /* This case should never be reached, because we list
838                above all the signals for which we actually establish a
839                signal handler.  */ 
840             sprintf (temp, "%d", sig);
841             name = temp;
842             break;
843         }
844         error (0, 0, "received %s signal", name);
845     }
846 }