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