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