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