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