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