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