xref: /netbsd/external/gpl2/xcvs/dist/src/patch.c (revision 3cd63638)
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