xref: /dflybsd-src/contrib/cvs-1.12/src/update.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  * "update" updates the version in the present directory with respect to the RCS
14*86d7f5d3SJohn Marino  * repository.  The present version must have been created by "checkout". The
15*86d7f5d3SJohn Marino  * user can keep up-to-date by calling "update" whenever he feels like it.
16*86d7f5d3SJohn Marino  *
17*86d7f5d3SJohn Marino  * The present version can be committed by "commit", but this keeps the version
18*86d7f5d3SJohn Marino  * in tact.
19*86d7f5d3SJohn Marino  *
20*86d7f5d3SJohn Marino  * Arguments following the options are taken to be file names to be updated,
21*86d7f5d3SJohn Marino  * rather than updating the entire directory.
22*86d7f5d3SJohn Marino  *
23*86d7f5d3SJohn Marino  * Modified or non-existent RCS files are checked out and reported as U
24*86d7f5d3SJohn Marino  * <user_file>
25*86d7f5d3SJohn Marino  *
26*86d7f5d3SJohn Marino  * Modified user files are reported as M <user_file>.  If both the RCS file and
27*86d7f5d3SJohn Marino  * the user file have been modified, the user file is replaced by the result
28*86d7f5d3SJohn Marino  * of rcsmerge, and a backup file is written for the user in .#file.version.
29*86d7f5d3SJohn Marino  * If this throws up irreconcilable differences, the file is reported as C
30*86d7f5d3SJohn Marino  * <user_file>, and as M <user_file> otherwise.
31*86d7f5d3SJohn Marino  *
32*86d7f5d3SJohn Marino  * Files added but not yet committed are reported as A <user_file>. Files
33*86d7f5d3SJohn Marino  * removed but not yet committed are reported as R <user_file>.
34*86d7f5d3SJohn Marino  *
35*86d7f5d3SJohn Marino  * If the current directory contains subdirectories that hold concurrent
36*86d7f5d3SJohn Marino  * versions, these are updated too.  If the -d option was specified, new
37*86d7f5d3SJohn Marino  * directories added to the repository are automatically created and updated
38*86d7f5d3SJohn Marino  * as well.
39*86d7f5d3SJohn Marino  */
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino #include "cvs.h"
42*86d7f5d3SJohn Marino #include <assert.h>
43*86d7f5d3SJohn Marino #include "save-cwd.h"
44*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
45*86d7f5d3SJohn Marino # include "md5.h"
46*86d7f5d3SJohn Marino #endif
47*86d7f5d3SJohn Marino #include "watch.h"
48*86d7f5d3SJohn Marino #include "fileattr.h"
49*86d7f5d3SJohn Marino #include "edit.h"
50*86d7f5d3SJohn Marino #include "getline.h"
51*86d7f5d3SJohn Marino #include "buffer.h"
52*86d7f5d3SJohn Marino #include "hardlink.h"
53*86d7f5d3SJohn Marino 
54*86d7f5d3SJohn Marino static int checkout_file (struct file_info *finfo, Vers_TS *vers_ts,
55*86d7f5d3SJohn Marino 				 int adding, int merging, int update_server);
56*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
57*86d7f5d3SJohn Marino static void checkout_to_buffer (void *, const char *, size_t);
58*86d7f5d3SJohn Marino static int patch_file (struct file_info *finfo,
59*86d7f5d3SJohn Marino                        Vers_TS *vers_ts,
60*86d7f5d3SJohn Marino                        int *docheckout, struct stat *file_info,
61*86d7f5d3SJohn Marino                        unsigned char *checksum);
62*86d7f5d3SJohn Marino static void patch_file_write (void *, const char *, size_t);
63*86d7f5d3SJohn Marino #endif
64*86d7f5d3SJohn Marino static int merge_file (struct file_info *finfo, Vers_TS *vers);
65*86d7f5d3SJohn Marino static int scratch_file (struct file_info *finfo, Vers_TS *vers);
66*86d7f5d3SJohn Marino static Dtype update_dirent_proc (void *callerdat, const char *dir,
67*86d7f5d3SJohn Marino                                  const char *repository,
68*86d7f5d3SJohn Marino                                  const char *update_dir,
69*86d7f5d3SJohn Marino                                  List *entries);
70*86d7f5d3SJohn Marino static int update_dirleave_proc (void *callerdat, const char *dir,
71*86d7f5d3SJohn Marino                                  int err, const char *update_dir,
72*86d7f5d3SJohn Marino                                  List *entries);
73*86d7f5d3SJohn Marino static int update_fileproc (void *callerdat, struct file_info *);
74*86d7f5d3SJohn Marino static int update_filesdone_proc (void *callerdat, int err,
75*86d7f5d3SJohn Marino                                   const char *repository,
76*86d7f5d3SJohn Marino                                   const char *update_dir, List *entries);
77*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
78*86d7f5d3SJohn Marino static int get_linkinfo_proc( void *_callerdat, struct _finfo * );
79*86d7f5d3SJohn Marino #endif
80*86d7f5d3SJohn Marino static void join_file (struct file_info *finfo, Vers_TS *vers_ts);
81*86d7f5d3SJohn Marino 
82*86d7f5d3SJohn Marino static char *options = NULL;
83*86d7f5d3SJohn Marino static char *tag = NULL;
84*86d7f5d3SJohn Marino static char *date = NULL;
85*86d7f5d3SJohn Marino /* This is a bit of a kludge.  We call WriteTag at the beginning
86*86d7f5d3SJohn Marino    before we know whether nonbranch is set or not.  And then at the
87*86d7f5d3SJohn Marino    end, once we have the right value for nonbranch, we call WriteTag
88*86d7f5d3SJohn Marino    again.  I don't know whether the first call is necessary or not.
89*86d7f5d3SJohn Marino    rewrite_tag is nonzero if we are going to have to make that second
90*86d7f5d3SJohn Marino    call.  warned is nonzero if we've already warned the user that the
91*86d7f5d3SJohn Marino    tag occurs as both a revision tag and a branch tag.  */
92*86d7f5d3SJohn Marino static int rewrite_tag;
93*86d7f5d3SJohn Marino static int nonbranch;
94*86d7f5d3SJohn Marino static int warned;
95*86d7f5d3SJohn Marino 
96*86d7f5d3SJohn Marino /* If we set the tag or date for a subdirectory, we use this to undo
97*86d7f5d3SJohn Marino    the setting.  See update_dirent_proc.  */
98*86d7f5d3SJohn Marino static char *tag_update_dir;
99*86d7f5d3SJohn Marino 
100*86d7f5d3SJohn Marino static char *join_rev1, *join_date1;
101*86d7f5d3SJohn Marino static char *join_rev2, *join_date2;
102*86d7f5d3SJohn Marino static int aflag = 0;
103*86d7f5d3SJohn Marino static int toss_local_changes = 0;
104*86d7f5d3SJohn Marino static int force_tag_match = 1;
105*86d7f5d3SJohn Marino static int update_build_dirs = 0;
106*86d7f5d3SJohn Marino static int update_prune_dirs = 0;
107*86d7f5d3SJohn Marino static int pipeout = 0;
108*86d7f5d3SJohn Marino static int dotemplate = 0;
109*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
110*86d7f5d3SJohn Marino static int patches = 0;
111*86d7f5d3SJohn Marino static int rcs_diff_patches = 0;
112*86d7f5d3SJohn Marino #endif
113*86d7f5d3SJohn Marino static List *ignlist = NULL;
114*86d7f5d3SJohn Marino static time_t last_register_time;
115*86d7f5d3SJohn Marino static const char *const update_usage[] =
116*86d7f5d3SJohn Marino {
117*86d7f5d3SJohn Marino     "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n",
118*86d7f5d3SJohn Marino     "    [-I ign] [-W spec] [files...]\n",
119*86d7f5d3SJohn Marino     "\t-A\tReset any sticky tags/date/kopts.\n",
120*86d7f5d3SJohn Marino     "\t-P\tPrune empty directories.\n",
121*86d7f5d3SJohn Marino     "\t-C\tOverwrite locally modified files with clean repository copies.\n",
122*86d7f5d3SJohn Marino     "\t-d\tBuild directories, like checkout does.\n",
123*86d7f5d3SJohn Marino     "\t-f\tForce a head revision match if tag/date not found.\n",
124*86d7f5d3SJohn Marino     "\t-l\tLocal directory only, no recursion.\n",
125*86d7f5d3SJohn Marino     "\t-R\tProcess directories recursively.\n",
126*86d7f5d3SJohn Marino     "\t-p\tSend updates to standard output (avoids stickiness).\n",
127*86d7f5d3SJohn Marino     "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
128*86d7f5d3SJohn Marino     "\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
129*86d7f5d3SJohn Marino     "\t-D date\tSet date to update from (is sticky).\n",
130*86d7f5d3SJohn Marino     "\t-j rev\tMerge in changes made between current revision and rev.\n",
131*86d7f5d3SJohn Marino     "\t-I ign\tMore files to ignore (! to reset).\n",
132*86d7f5d3SJohn Marino     "\t-W spec\tWrappers specification line.\n",
133*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
134*86d7f5d3SJohn Marino     NULL
135*86d7f5d3SJohn Marino };
136*86d7f5d3SJohn Marino 
137*86d7f5d3SJohn Marino 
138*86d7f5d3SJohn Marino 
139*86d7f5d3SJohn Marino /*
140*86d7f5d3SJohn Marino  * update is the argv,argc based front end for arg parsing
141*86d7f5d3SJohn Marino  */
142*86d7f5d3SJohn Marino int
update(int argc,char ** argv)143*86d7f5d3SJohn Marino update (int argc, char **argv)
144*86d7f5d3SJohn Marino {
145*86d7f5d3SJohn Marino     int c, err;
146*86d7f5d3SJohn Marino     int local = 0;			/* recursive by default */
147*86d7f5d3SJohn Marino     int which;				/* where to look for files and dirs */
148*86d7f5d3SJohn Marino     char *xjoin_rev1, *xjoin_date1,
149*86d7f5d3SJohn Marino 	 *xjoin_rev2, *xjoin_date2,
150*86d7f5d3SJohn Marino 	 *join_orig1, *join_orig2;
151*86d7f5d3SJohn Marino 
152*86d7f5d3SJohn Marino     if (argc == -1)
153*86d7f5d3SJohn Marino 	usage (update_usage);
154*86d7f5d3SJohn Marino 
155*86d7f5d3SJohn Marino     xjoin_rev1 = xjoin_date1 = xjoin_rev2 = xjoin_date2 = join_orig1 =
156*86d7f5d3SJohn Marino 	         join_orig2 = NULL;
157*86d7f5d3SJohn Marino 
158*86d7f5d3SJohn Marino     ign_setup ();
159*86d7f5d3SJohn Marino     wrap_setup ();
160*86d7f5d3SJohn Marino 
161*86d7f5d3SJohn Marino     /* parse the args */
162*86d7f5d3SJohn Marino     optind = 0;
163*86d7f5d3SJohn Marino     while ((c = getopt (argc, argv, "+ApCPflRQqduk:r:D:j:I:W:")) != -1)
164*86d7f5d3SJohn Marino     {
165*86d7f5d3SJohn Marino 	switch (c)
166*86d7f5d3SJohn Marino 	{
167*86d7f5d3SJohn Marino 	    case 'A':
168*86d7f5d3SJohn Marino 		aflag = 1;
169*86d7f5d3SJohn Marino 		break;
170*86d7f5d3SJohn Marino 	    case 'C':
171*86d7f5d3SJohn Marino 		toss_local_changes = 1;
172*86d7f5d3SJohn Marino 		break;
173*86d7f5d3SJohn Marino 	    case 'I':
174*86d7f5d3SJohn Marino 		ign_add (optarg, 0);
175*86d7f5d3SJohn Marino 		break;
176*86d7f5d3SJohn Marino 	    case 'W':
177*86d7f5d3SJohn Marino 		wrap_add (optarg, 0);
178*86d7f5d3SJohn Marino 		break;
179*86d7f5d3SJohn Marino 	    case 'k':
180*86d7f5d3SJohn Marino 		if (options)
181*86d7f5d3SJohn Marino 		    free (options);
182*86d7f5d3SJohn Marino 		options = RCS_check_kflag (optarg);
183*86d7f5d3SJohn Marino 		break;
184*86d7f5d3SJohn Marino 	    case 'l':
185*86d7f5d3SJohn Marino 		local = 1;
186*86d7f5d3SJohn Marino 		break;
187*86d7f5d3SJohn Marino 	    case 'R':
188*86d7f5d3SJohn Marino 		local = 0;
189*86d7f5d3SJohn Marino 		break;
190*86d7f5d3SJohn Marino 	    case 'Q':
191*86d7f5d3SJohn Marino 	    case 'q':
192*86d7f5d3SJohn Marino 		/* The CVS 1.5 client sends these options (in addition to
193*86d7f5d3SJohn Marino 		   Global_option requests), so we must ignore them.  */
194*86d7f5d3SJohn Marino 		if (!server_active)
195*86d7f5d3SJohn Marino 		    error (1, 0,
196*86d7f5d3SJohn Marino 			   "-q or -Q must be specified before \"%s\"",
197*86d7f5d3SJohn Marino 			   cvs_cmd_name);
198*86d7f5d3SJohn Marino 		break;
199*86d7f5d3SJohn Marino 	    case 'd':
200*86d7f5d3SJohn Marino 		update_build_dirs = 1;
201*86d7f5d3SJohn Marino 		break;
202*86d7f5d3SJohn Marino 	    case 'f':
203*86d7f5d3SJohn Marino 		force_tag_match = 0;
204*86d7f5d3SJohn Marino 		break;
205*86d7f5d3SJohn Marino 	    case 'r':
206*86d7f5d3SJohn Marino 		parse_tagdate (&tag, &date, optarg);
207*86d7f5d3SJohn Marino 		break;
208*86d7f5d3SJohn Marino 	    case 'D':
209*86d7f5d3SJohn Marino 		if (date) free (date);
210*86d7f5d3SJohn Marino 		date = Make_Date (optarg);
211*86d7f5d3SJohn Marino 		break;
212*86d7f5d3SJohn Marino 	    case 'P':
213*86d7f5d3SJohn Marino 		update_prune_dirs = 1;
214*86d7f5d3SJohn Marino 		break;
215*86d7f5d3SJohn Marino 	    case 'p':
216*86d7f5d3SJohn Marino 		pipeout = 1;
217*86d7f5d3SJohn Marino 		noexec = 1;		/* so no locks will be created */
218*86d7f5d3SJohn Marino 		break;
219*86d7f5d3SJohn Marino 	    case 'j':
220*86d7f5d3SJohn Marino 		if (join_orig2)
221*86d7f5d3SJohn Marino 		    error (1, 0, "only two -j options can be specified");
222*86d7f5d3SJohn Marino 		if (join_orig1)
223*86d7f5d3SJohn Marino 		{
224*86d7f5d3SJohn Marino 		    join_orig2 = xstrdup (optarg);
225*86d7f5d3SJohn Marino 		    parse_tagdate (&xjoin_rev2, &xjoin_date2, optarg);
226*86d7f5d3SJohn Marino 		}
227*86d7f5d3SJohn Marino 		else
228*86d7f5d3SJohn Marino 		{
229*86d7f5d3SJohn Marino 		    join_orig1 = xstrdup (optarg);
230*86d7f5d3SJohn Marino 		    parse_tagdate (&xjoin_rev1, &xjoin_date1, optarg);
231*86d7f5d3SJohn Marino 		}
232*86d7f5d3SJohn Marino 		break;
233*86d7f5d3SJohn Marino 	    case 'u':
234*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
235*86d7f5d3SJohn Marino 		if (server_active)
236*86d7f5d3SJohn Marino 		{
237*86d7f5d3SJohn Marino 		    patches = 1;
238*86d7f5d3SJohn Marino 		    rcs_diff_patches = server_use_rcs_diff ();
239*86d7f5d3SJohn Marino 		}
240*86d7f5d3SJohn Marino 		else
241*86d7f5d3SJohn Marino #endif
242*86d7f5d3SJohn Marino 		    usage (update_usage);
243*86d7f5d3SJohn Marino 		break;
244*86d7f5d3SJohn Marino 	    case '?':
245*86d7f5d3SJohn Marino 	    default:
246*86d7f5d3SJohn Marino 		usage (update_usage);
247*86d7f5d3SJohn Marino 		break;
248*86d7f5d3SJohn Marino 	}
249*86d7f5d3SJohn Marino     }
250*86d7f5d3SJohn Marino     argc -= optind;
251*86d7f5d3SJohn Marino     argv += optind;
252*86d7f5d3SJohn Marino 
253*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
254*86d7f5d3SJohn Marino     if (current_parsed_root->isremote)
255*86d7f5d3SJohn Marino     {
256*86d7f5d3SJohn Marino 	int pass;
257*86d7f5d3SJohn Marino 
258*86d7f5d3SJohn Marino 	/* The first pass does the regular update.  If we receive at least
259*86d7f5d3SJohn Marino 	   one patch which failed, we do a second pass and just fetch
260*86d7f5d3SJohn Marino 	   those files whose patches failed.  */
261*86d7f5d3SJohn Marino 	pass = 1;
262*86d7f5d3SJohn Marino 	do
263*86d7f5d3SJohn Marino 	{
264*86d7f5d3SJohn Marino 	    int status;
265*86d7f5d3SJohn Marino 
266*86d7f5d3SJohn Marino 	    start_server ();
267*86d7f5d3SJohn Marino 
268*86d7f5d3SJohn Marino 	    if (local)
269*86d7f5d3SJohn Marino 		send_arg("-l");
270*86d7f5d3SJohn Marino 	    if (update_build_dirs)
271*86d7f5d3SJohn Marino 		send_arg("-d");
272*86d7f5d3SJohn Marino 	    if (pipeout)
273*86d7f5d3SJohn Marino 		send_arg("-p");
274*86d7f5d3SJohn Marino 	    if (!force_tag_match)
275*86d7f5d3SJohn Marino 		send_arg("-f");
276*86d7f5d3SJohn Marino 	    if (aflag)
277*86d7f5d3SJohn Marino 		send_arg("-A");
278*86d7f5d3SJohn Marino 	    if (toss_local_changes)
279*86d7f5d3SJohn Marino 		send_arg("-C");
280*86d7f5d3SJohn Marino 	    if (update_prune_dirs)
281*86d7f5d3SJohn Marino 		send_arg("-P");
282*86d7f5d3SJohn Marino 	    client_prune_dirs = update_prune_dirs;
283*86d7f5d3SJohn Marino 	    option_with_arg ("-r", tag);
284*86d7f5d3SJohn Marino 	    if (options && options[0] != '\0')
285*86d7f5d3SJohn Marino 		send_arg (options);
286*86d7f5d3SJohn Marino 	    if (date)
287*86d7f5d3SJohn Marino 		client_senddate (date);
288*86d7f5d3SJohn Marino 	    if (join_orig1)
289*86d7f5d3SJohn Marino 		option_with_arg ("-j", join_orig1);
290*86d7f5d3SJohn Marino 	    if (join_orig2)
291*86d7f5d3SJohn Marino 		option_with_arg ("-j", join_orig2);
292*86d7f5d3SJohn Marino 	    wrap_send ();
293*86d7f5d3SJohn Marino 
294*86d7f5d3SJohn Marino 	    if (failed_patches_count == 0)
295*86d7f5d3SJohn Marino 	    {
296*86d7f5d3SJohn Marino                 unsigned int flags = 0;
297*86d7f5d3SJohn Marino 
298*86d7f5d3SJohn Marino 		/* If the server supports the command "update-patches", that
299*86d7f5d3SJohn Marino 		   means that it knows how to handle the -u argument to update,
300*86d7f5d3SJohn Marino 		   which means to send patches instead of complete files.
301*86d7f5d3SJohn Marino 
302*86d7f5d3SJohn Marino 		   We don't send -u if failed_patches != NULL, so that the
303*86d7f5d3SJohn Marino 		   server doesn't try to send patches which will just fail
304*86d7f5d3SJohn Marino 		   again.  At least currently, the client also clobbers the
305*86d7f5d3SJohn Marino 		   file and tells the server it is lost, which also will get
306*86d7f5d3SJohn Marino 		   a full file instead of a patch, but it seems clean to omit
307*86d7f5d3SJohn Marino 		   -u.  */
308*86d7f5d3SJohn Marino 		if (supported_request ("update-patches"))
309*86d7f5d3SJohn Marino 		    send_arg ("-u");
310*86d7f5d3SJohn Marino 
311*86d7f5d3SJohn Marino 		send_arg ("--");
312*86d7f5d3SJohn Marino 
313*86d7f5d3SJohn Marino                 if (update_build_dirs)
314*86d7f5d3SJohn Marino                     flags |= SEND_BUILD_DIRS;
315*86d7f5d3SJohn Marino 
316*86d7f5d3SJohn Marino                 if (toss_local_changes) {
317*86d7f5d3SJohn Marino                     flags |= SEND_NO_CONTENTS;
318*86d7f5d3SJohn Marino                     flags |= BACKUP_MODIFIED_FILES;
319*86d7f5d3SJohn Marino                 }
320*86d7f5d3SJohn Marino 
321*86d7f5d3SJohn Marino 		/* If noexec, probably could be setting SEND_NO_CONTENTS.
322*86d7f5d3SJohn Marino 		   Same caveats as for "cvs status" apply.  */
323*86d7f5d3SJohn Marino 
324*86d7f5d3SJohn Marino 		send_files (argc, argv, local, aflag, flags);
325*86d7f5d3SJohn Marino 		send_file_names (argc, argv, SEND_EXPAND_WILD);
326*86d7f5d3SJohn Marino 	    }
327*86d7f5d3SJohn Marino 	    else
328*86d7f5d3SJohn Marino 	    {
329*86d7f5d3SJohn Marino 		int i;
330*86d7f5d3SJohn Marino 
331*86d7f5d3SJohn Marino 		(void) printf ("%s client: refetching unpatchable files\n",
332*86d7f5d3SJohn Marino 			       program_name);
333*86d7f5d3SJohn Marino 
334*86d7f5d3SJohn Marino 		if (toplevel_wd != NULL
335*86d7f5d3SJohn Marino 		    && CVS_CHDIR (toplevel_wd) < 0)
336*86d7f5d3SJohn Marino 		{
337*86d7f5d3SJohn Marino 		    error (1, errno, "could not chdir to %s", toplevel_wd);
338*86d7f5d3SJohn Marino 		}
339*86d7f5d3SJohn Marino 
340*86d7f5d3SJohn Marino 		send_arg ("--");
341*86d7f5d3SJohn Marino 
342*86d7f5d3SJohn Marino 		for (i = 0; i < failed_patches_count; i++)
343*86d7f5d3SJohn Marino 		    if (unlink_file (failed_patches[i]) < 0
344*86d7f5d3SJohn Marino 			&& !existence_error (errno))
345*86d7f5d3SJohn Marino 			error (0, errno, "cannot remove %s",
346*86d7f5d3SJohn Marino 			       failed_patches[i]);
347*86d7f5d3SJohn Marino 		send_files (failed_patches_count, failed_patches, local,
348*86d7f5d3SJohn Marino 			    aflag, update_build_dirs ? SEND_BUILD_DIRS : 0);
349*86d7f5d3SJohn Marino 		send_file_names (failed_patches_count, failed_patches, 0);
350*86d7f5d3SJohn Marino 		free_names (&failed_patches_count, failed_patches);
351*86d7f5d3SJohn Marino 	    }
352*86d7f5d3SJohn Marino 
353*86d7f5d3SJohn Marino 	    send_to_server ("update\012", 0);
354*86d7f5d3SJohn Marino 
355*86d7f5d3SJohn Marino 	    status = get_responses_and_close ();
356*86d7f5d3SJohn Marino 
357*86d7f5d3SJohn Marino 	    /* If there are any conflicts, the server will return a
358*86d7f5d3SJohn Marino                non-zero exit status.  If any patches failed, we still
359*86d7f5d3SJohn Marino                want to run the update again.  We use a pass count to
360*86d7f5d3SJohn Marino                avoid an endless loop.  */
361*86d7f5d3SJohn Marino 
362*86d7f5d3SJohn Marino 	    /* Notes: (1) assuming that status != 0 implies a
363*86d7f5d3SJohn Marino 	       potential conflict is the best we can cleanly do given
364*86d7f5d3SJohn Marino 	       the current protocol.  I suppose that trying to
365*86d7f5d3SJohn Marino 	       re-fetch in cases where there was a more serious error
366*86d7f5d3SJohn Marino 	       is probably more or less harmless, but it isn't really
367*86d7f5d3SJohn Marino 	       ideal.  (2) it would be nice to have a testsuite case for the
368*86d7f5d3SJohn Marino 	       conflict-and-patch-failed case.  */
369*86d7f5d3SJohn Marino 
370*86d7f5d3SJohn Marino 	    if (status != 0
371*86d7f5d3SJohn Marino 		&& (failed_patches_count == 0 || pass > 1))
372*86d7f5d3SJohn Marino 	    {
373*86d7f5d3SJohn Marino 		if (failed_patches_count > 0)
374*86d7f5d3SJohn Marino 		    free_names (&failed_patches_count, failed_patches);
375*86d7f5d3SJohn Marino 		return status;
376*86d7f5d3SJohn Marino 	    }
377*86d7f5d3SJohn Marino 
378*86d7f5d3SJohn Marino 	    ++pass;
379*86d7f5d3SJohn Marino 	} while (failed_patches_count > 0);
380*86d7f5d3SJohn Marino 
381*86d7f5d3SJohn Marino 	return 0;
382*86d7f5d3SJohn Marino     }
383*86d7f5d3SJohn Marino #endif
384*86d7f5d3SJohn Marino 
385*86d7f5d3SJohn Marino     if (tag != NULL)
386*86d7f5d3SJohn Marino 	tag_check_valid (tag, argc, argv, local, aflag, "", false);
387*86d7f5d3SJohn Marino     if (join_rev1 != NULL)
388*86d7f5d3SJohn Marino 	tag_check_valid (xjoin_rev1, argc, argv, local, aflag, "", false);
389*86d7f5d3SJohn Marino     if (join_rev2 != NULL)
390*86d7f5d3SJohn Marino 	tag_check_valid (xjoin_rev2, argc, argv, local, aflag, "", false);
391*86d7f5d3SJohn Marino 
392*86d7f5d3SJohn Marino     /*
393*86d7f5d3SJohn Marino      * If we are updating the entire directory (for real) and building dirs
394*86d7f5d3SJohn Marino      * as we go, we make sure there is no static entries file and write the
395*86d7f5d3SJohn Marino      * tag file as appropriate
396*86d7f5d3SJohn Marino      */
397*86d7f5d3SJohn Marino     if (argc <= 0 && !pipeout)
398*86d7f5d3SJohn Marino     {
399*86d7f5d3SJohn Marino 	if (update_build_dirs)
400*86d7f5d3SJohn Marino 	{
401*86d7f5d3SJohn Marino 	    if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
402*86d7f5d3SJohn Marino 		error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
403*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
404*86d7f5d3SJohn Marino 	    if (server_active)
405*86d7f5d3SJohn Marino 	    {
406*86d7f5d3SJohn Marino 		char *repos = Name_Repository (NULL, NULL);
407*86d7f5d3SJohn Marino 		server_clear_entstat (".", repos);
408*86d7f5d3SJohn Marino 		free (repos);
409*86d7f5d3SJohn Marino 	    }
410*86d7f5d3SJohn Marino #endif
411*86d7f5d3SJohn Marino 	}
412*86d7f5d3SJohn Marino 
413*86d7f5d3SJohn Marino 	/* keep the CVS/Tag file current with the specified arguments */
414*86d7f5d3SJohn Marino 	if (aflag || tag || date)
415*86d7f5d3SJohn Marino 	{
416*86d7f5d3SJohn Marino 	    char *repos = Name_Repository (NULL, NULL);
417*86d7f5d3SJohn Marino 	    WriteTag (NULL, tag, date, 0, ".", repos);
418*86d7f5d3SJohn Marino 	    free (repos);
419*86d7f5d3SJohn Marino 	    rewrite_tag = 1;
420*86d7f5d3SJohn Marino 	    nonbranch = -1;
421*86d7f5d3SJohn Marino 	    warned = 0;
422*86d7f5d3SJohn Marino 	}
423*86d7f5d3SJohn Marino     }
424*86d7f5d3SJohn Marino 
425*86d7f5d3SJohn Marino     /* look for files/dirs locally and in the repository */
426*86d7f5d3SJohn Marino     which = W_LOCAL | W_REPOS;
427*86d7f5d3SJohn Marino 
428*86d7f5d3SJohn Marino     /* look in the attic too if a tag or date is specified */
429*86d7f5d3SJohn Marino     if (tag || date || join_orig1)
430*86d7f5d3SJohn Marino     {
431*86d7f5d3SJohn Marino 	TRACE (TRACE_DATA, "update: searching attic");
432*86d7f5d3SJohn Marino 	which |= W_ATTIC;
433*86d7f5d3SJohn Marino     }
434*86d7f5d3SJohn Marino 
435*86d7f5d3SJohn Marino     /* call the command line interface */
436*86d7f5d3SJohn Marino     err = do_update (argc, argv, options, tag, date, force_tag_match,
437*86d7f5d3SJohn Marino 		     local, update_build_dirs, aflag, update_prune_dirs,
438*86d7f5d3SJohn Marino 		     pipeout, which, xjoin_rev1, xjoin_date1, xjoin_rev2,
439*86d7f5d3SJohn Marino 		     xjoin_date2, NULL, 1, NULL);
440*86d7f5d3SJohn Marino 
441*86d7f5d3SJohn Marino     /* Free the space allocated for tags and dates, if necessary.  */
442*86d7f5d3SJohn Marino     if (tag) free (tag);
443*86d7f5d3SJohn Marino     if (date) free (date);
444*86d7f5d3SJohn Marino 
445*86d7f5d3SJohn Marino     return err;
446*86d7f5d3SJohn Marino }
447*86d7f5d3SJohn Marino 
448*86d7f5d3SJohn Marino 
449*86d7f5d3SJohn Marino 
450*86d7f5d3SJohn Marino /*
451*86d7f5d3SJohn Marino  * Command line interface to update (used by checkout)
452*86d7f5d3SJohn Marino  *
453*86d7f5d3SJohn Marino  * repository = cvsroot->repository + update_dir.  This is necessary for
454*86d7f5d3SJohn Marino  * checkout so that start_recursion can determine our repository.  In the
455*86d7f5d3SJohn Marino  * update case, start_recursion can use the CVS/Root & CVS/Repository file
456*86d7f5d3SJohn Marino  * to determine this value.
457*86d7f5d3SJohn Marino  */
458*86d7f5d3SJohn Marino int
do_update(int argc,char ** argv,char * xoptions,char * xtag,char * xdate,int xforce,int local,int xbuild,int xaflag,int xprune,int xpipeout,int which,char * xjoin_rev1,char * xjoin_date1,char * xjoin_rev2,char * xjoin_date2,char * preload_update_dir,int xdotemplate,char * repository)459*86d7f5d3SJohn Marino do_update (int argc, char **argv, char *xoptions, char *xtag, char *xdate,
460*86d7f5d3SJohn Marino            int xforce, int local, int xbuild, int xaflag, int xprune,
461*86d7f5d3SJohn Marino            int xpipeout, int which, char *xjoin_rev1, char *xjoin_date1,
462*86d7f5d3SJohn Marino 	   char *xjoin_rev2, char *xjoin_date2,
463*86d7f5d3SJohn Marino            char *preload_update_dir, int xdotemplate, char *repository)
464*86d7f5d3SJohn Marino {
465*86d7f5d3SJohn Marino     int err = 0;
466*86d7f5d3SJohn Marino 
467*86d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION,
468*86d7f5d3SJohn Marino "do_update (%s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s, %d, %s)",
469*86d7f5d3SJohn Marino            xoptions ? xoptions : "(null)", xtag ? xtag : "(null)",
470*86d7f5d3SJohn Marino 	   xdate ? xdate : "(null)", xforce, local, xbuild, xaflag, xprune,
471*86d7f5d3SJohn Marino 	   xpipeout, which, xjoin_rev1 ? xjoin_rev1 : "(null)",
472*86d7f5d3SJohn Marino 	   xjoin_date1 ? xjoin_date1 : "(null)",
473*86d7f5d3SJohn Marino 	   xjoin_rev2 ? xjoin_rev2 : "(null)",
474*86d7f5d3SJohn Marino 	   xjoin_date2 ? xjoin_date2 : "(null)",
475*86d7f5d3SJohn Marino 	   preload_update_dir ? preload_update_dir : "(null)", xdotemplate,
476*86d7f5d3SJohn Marino 	   repository ? repository : "(null)");
477*86d7f5d3SJohn Marino 
478*86d7f5d3SJohn Marino     /* fill in the statics */
479*86d7f5d3SJohn Marino     options = xoptions;
480*86d7f5d3SJohn Marino     tag = xtag;
481*86d7f5d3SJohn Marino     date = xdate;
482*86d7f5d3SJohn Marino     force_tag_match = xforce;
483*86d7f5d3SJohn Marino     update_build_dirs = xbuild;
484*86d7f5d3SJohn Marino     aflag = xaflag;
485*86d7f5d3SJohn Marino     update_prune_dirs = xprune;
486*86d7f5d3SJohn Marino     pipeout = xpipeout;
487*86d7f5d3SJohn Marino     dotemplate = xdotemplate;
488*86d7f5d3SJohn Marino 
489*86d7f5d3SJohn Marino     /* setup the join support */
490*86d7f5d3SJohn Marino     join_rev1 = xjoin_rev1;
491*86d7f5d3SJohn Marino     join_date1 = xjoin_date1;
492*86d7f5d3SJohn Marino     join_rev2 = xjoin_rev2;
493*86d7f5d3SJohn Marino     join_date2 = xjoin_date2;
494*86d7f5d3SJohn Marino 
495*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
496*86d7f5d3SJohn Marino     if (preserve_perms)
497*86d7f5d3SJohn Marino     {
498*86d7f5d3SJohn Marino 	/* We need to do an extra recursion, bleah.  It's to make sure
499*86d7f5d3SJohn Marino 	   that we know as much as possible about file linkage. */
500*86d7f5d3SJohn Marino 	hardlist = getlist();
501*86d7f5d3SJohn Marino 	working_dir = xgetcwd ();		/* save top-level working dir */
502*86d7f5d3SJohn Marino 
503*86d7f5d3SJohn Marino 	/* FIXME-twp: the arguments to start_recursion make me dizzy.  This
504*86d7f5d3SJohn Marino 	   function call was copied from the update_fileproc call that
505*86d7f5d3SJohn Marino 	   follows it; someone should make sure that I did it right. */
506*86d7f5d3SJohn Marino 	err = start_recursion
507*86d7f5d3SJohn Marino 	    (get_linkinfo_proc, NULL, NULL, NULL, NULL,
508*86d7f5d3SJohn Marino 	     argc, argv, local, which, aflag, CVS_LOCK_READ,
509*86d7f5d3SJohn Marino 	     preload_update_dir, 1, NULL);
510*86d7f5d3SJohn Marino 	if (err)
511*86d7f5d3SJohn Marino 	    return err;
512*86d7f5d3SJohn Marino 
513*86d7f5d3SJohn Marino 	/* FIXME-twp: at this point we should walk the hardlist
514*86d7f5d3SJohn Marino 	   and update the `links' field of each hardlink_info struct
515*86d7f5d3SJohn Marino 	   to list the files that are linked on dist.  That would make
516*86d7f5d3SJohn Marino 	   it easier & more efficient to compare the disk linkage with
517*86d7f5d3SJohn Marino 	   the repository linkage (a simple strcmp). */
518*86d7f5d3SJohn Marino     }
519*86d7f5d3SJohn Marino #endif
520*86d7f5d3SJohn Marino 
521*86d7f5d3SJohn Marino     /* call the recursion processor */
522*86d7f5d3SJohn Marino     err = start_recursion (update_fileproc, update_filesdone_proc,
523*86d7f5d3SJohn Marino 			   update_dirent_proc, update_dirleave_proc, NULL,
524*86d7f5d3SJohn Marino 			   argc, argv, local, which, aflag, CVS_LOCK_READ,
525*86d7f5d3SJohn Marino 			   preload_update_dir, 1, repository);
526*86d7f5d3SJohn Marino 
527*86d7f5d3SJohn Marino     /* see if we need to sleep before returning to avoid time-stamp races */
528*86d7f5d3SJohn Marino     if (!server_active && last_register_time)
529*86d7f5d3SJohn Marino     {
530*86d7f5d3SJohn Marino 	sleep_past (last_register_time);
531*86d7f5d3SJohn Marino     }
532*86d7f5d3SJohn Marino 
533*86d7f5d3SJohn Marino     return err;
534*86d7f5d3SJohn Marino }
535*86d7f5d3SJohn Marino 
536*86d7f5d3SJohn Marino 
537*86d7f5d3SJohn Marino 
538*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
539*86d7f5d3SJohn Marino /*
540*86d7f5d3SJohn Marino  * The get_linkinfo_proc callback adds each file to the hardlist
541*86d7f5d3SJohn Marino  * (see hardlink.c).
542*86d7f5d3SJohn Marino  */
543*86d7f5d3SJohn Marino 
544*86d7f5d3SJohn Marino static int
get_linkinfo_proc(void * callerdat,struct file_info * finfo)545*86d7f5d3SJohn Marino get_linkinfo_proc (void *callerdat, struct file_info *finfo)
546*86d7f5d3SJohn Marino {
547*86d7f5d3SJohn Marino     char *fullpath;
548*86d7f5d3SJohn Marino     Node *linkp;
549*86d7f5d3SJohn Marino     struct hardlink_info *hlinfo;
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino     /* Get the full pathname of the current file. */
552*86d7f5d3SJohn Marino     fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
553*86d7f5d3SJohn Marino 
554*86d7f5d3SJohn Marino     /* To permit recursing into subdirectories, files
555*86d7f5d3SJohn Marino        are keyed on the full pathname and not on the basename. */
556*86d7f5d3SJohn Marino     linkp = lookup_file_by_inode (fullpath);
557*86d7f5d3SJohn Marino     if (linkp == NULL)
558*86d7f5d3SJohn Marino     {
559*86d7f5d3SJohn Marino 	/* The file isn't on disk; we are probably restoring
560*86d7f5d3SJohn Marino 	   a file that was removed. */
561*86d7f5d3SJohn Marino 	return 0;
562*86d7f5d3SJohn Marino     }
563*86d7f5d3SJohn Marino 
564*86d7f5d3SJohn Marino     /* Create a new, empty hardlink_info node. */
565*86d7f5d3SJohn Marino     hlinfo = xmalloc (sizeof (struct hardlink_info));
566*86d7f5d3SJohn Marino 
567*86d7f5d3SJohn Marino     hlinfo->status = (Ctype) 0;	/* is this dumb? */
568*86d7f5d3SJohn Marino     hlinfo->checked_out = 0;
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino     linkp->data = hlinfo;
571*86d7f5d3SJohn Marino 
572*86d7f5d3SJohn Marino     return 0;
573*86d7f5d3SJohn Marino }
574*86d7f5d3SJohn Marino #endif
575*86d7f5d3SJohn Marino 
576*86d7f5d3SJohn Marino 
577*86d7f5d3SJohn Marino 
578*86d7f5d3SJohn Marino /*
579*86d7f5d3SJohn Marino  * This is the callback proc for update.  It is called for each file in each
580*86d7f5d3SJohn Marino  * directory by the recursion code.  The current directory is the local
581*86d7f5d3SJohn Marino  * instantiation.  file is the file name we are to operate on. update_dir is
582*86d7f5d3SJohn Marino  * set to the path relative to where we started (for pretty printing).
583*86d7f5d3SJohn Marino  * repository is the repository. entries and srcfiles are the pre-parsed
584*86d7f5d3SJohn Marino  * entries and source control files.
585*86d7f5d3SJohn Marino  *
586*86d7f5d3SJohn Marino  * This routine decides what needs to be done for each file and does the
587*86d7f5d3SJohn Marino  * appropriate magic for checkout
588*86d7f5d3SJohn Marino  */
589*86d7f5d3SJohn Marino static int
update_fileproc(void * callerdat,struct file_info * finfo)590*86d7f5d3SJohn Marino update_fileproc (void *callerdat, struct file_info *finfo)
591*86d7f5d3SJohn Marino {
592*86d7f5d3SJohn Marino     int retval, nb;
593*86d7f5d3SJohn Marino     Ctype status;
594*86d7f5d3SJohn Marino     Vers_TS *vers;
595*86d7f5d3SJohn Marino 
596*86d7f5d3SJohn Marino     status = Classify_File (finfo, tag, date, options, force_tag_match,
597*86d7f5d3SJohn Marino 			    aflag, &vers, pipeout);
598*86d7f5d3SJohn Marino 
599*86d7f5d3SJohn Marino     /* Keep track of whether TAG is a branch tag.
600*86d7f5d3SJohn Marino        Note that if it is a branch tag in some files and a nonbranch tag
601*86d7f5d3SJohn Marino        in others, treat it as a nonbranch tag.  */
602*86d7f5d3SJohn Marino     if (rewrite_tag
603*86d7f5d3SJohn Marino 	&& tag != NULL
604*86d7f5d3SJohn Marino 	&& finfo->rcs != NULL)
605*86d7f5d3SJohn Marino     {
606*86d7f5d3SJohn Marino 	char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL);
607*86d7f5d3SJohn Marino 	if (rev != NULL
608*86d7f5d3SJohn Marino 	    && nonbranch != (nb = !RCS_nodeisbranch (finfo->rcs, tag)))
609*86d7f5d3SJohn Marino 	{
610*86d7f5d3SJohn Marino 	    if (nonbranch >= 0 && !warned && !quiet)
611*86d7f5d3SJohn Marino 	    {
612*86d7f5d3SJohn Marino 		error (0, 0,
613*86d7f5d3SJohn Marino "warning: %s is a branch tag in some files and a revision tag in others.",
614*86d7f5d3SJohn Marino 			tag);
615*86d7f5d3SJohn Marino 		warned = 1;
616*86d7f5d3SJohn Marino 	    }
617*86d7f5d3SJohn Marino 	    if (nonbranch < nb) nonbranch = nb;
618*86d7f5d3SJohn Marino 	}
619*86d7f5d3SJohn Marino 	if (rev != NULL)
620*86d7f5d3SJohn Marino 	    free (rev);
621*86d7f5d3SJohn Marino     }
622*86d7f5d3SJohn Marino 
623*86d7f5d3SJohn Marino     if (pipeout)
624*86d7f5d3SJohn Marino     {
625*86d7f5d3SJohn Marino 	/*
626*86d7f5d3SJohn Marino 	 * We just return success without doing anything if any of the really
627*86d7f5d3SJohn Marino 	 * funky cases occur
628*86d7f5d3SJohn Marino 	 *
629*86d7f5d3SJohn Marino 	 * If there is still a valid RCS file, do a regular checkout type
630*86d7f5d3SJohn Marino 	 * operation
631*86d7f5d3SJohn Marino 	 */
632*86d7f5d3SJohn Marino 	switch (status)
633*86d7f5d3SJohn Marino 	{
634*86d7f5d3SJohn Marino 	    case T_UNKNOWN:		/* unknown file was explicitly asked
635*86d7f5d3SJohn Marino 					 * about */
636*86d7f5d3SJohn Marino 	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
637*86d7f5d3SJohn Marino 	    case T_ADDED:		/* added but not committed */
638*86d7f5d3SJohn Marino 		retval = 0;
639*86d7f5d3SJohn Marino 		break;
640*86d7f5d3SJohn Marino 	    case T_CONFLICT:		/* old punt-type errors */
641*86d7f5d3SJohn Marino 		retval = 1;
642*86d7f5d3SJohn Marino 		break;
643*86d7f5d3SJohn Marino 	    case T_UPTODATE:		/* file was already up-to-date */
644*86d7f5d3SJohn Marino 	    case T_NEEDS_MERGE:		/* needs merging */
645*86d7f5d3SJohn Marino 	    case T_MODIFIED:		/* locally modified */
646*86d7f5d3SJohn Marino 	    case T_REMOVED:		/* removed but not committed */
647*86d7f5d3SJohn Marino 	    case T_CHECKOUT:		/* needs checkout */
648*86d7f5d3SJohn Marino 	    case T_PATCH:		/* needs patch */
649*86d7f5d3SJohn Marino 		retval = checkout_file (finfo, vers, 0, 0, 0);
650*86d7f5d3SJohn Marino 		break;
651*86d7f5d3SJohn Marino 
652*86d7f5d3SJohn Marino 	    default:			/* can't ever happen :-) */
653*86d7f5d3SJohn Marino 		error (0, 0,
654*86d7f5d3SJohn Marino 		       "unknown file status %d for file %s", status, finfo->file);
655*86d7f5d3SJohn Marino 		retval = 0;
656*86d7f5d3SJohn Marino 		break;
657*86d7f5d3SJohn Marino 	}
658*86d7f5d3SJohn Marino     }
659*86d7f5d3SJohn Marino     else
660*86d7f5d3SJohn Marino     {
661*86d7f5d3SJohn Marino 	switch (status)
662*86d7f5d3SJohn Marino 	{
663*86d7f5d3SJohn Marino 	    case T_UNKNOWN:		/* unknown file was explicitly asked
664*86d7f5d3SJohn Marino 					 * about */
665*86d7f5d3SJohn Marino 	    case T_UPTODATE:		/* file was already up-to-date */
666*86d7f5d3SJohn Marino 		retval = 0;
667*86d7f5d3SJohn Marino 		break;
668*86d7f5d3SJohn Marino 	    case T_CONFLICT:		/* old punt-type errors */
669*86d7f5d3SJohn Marino 		retval = 1;
670*86d7f5d3SJohn Marino 		write_letter (finfo, 'C');
671*86d7f5d3SJohn Marino 		break;
672*86d7f5d3SJohn Marino 	    case T_NEEDS_MERGE:		/* needs merging */
673*86d7f5d3SJohn Marino 		if (! toss_local_changes)
674*86d7f5d3SJohn Marino 		{
675*86d7f5d3SJohn Marino 		    retval = merge_file (finfo, vers);
676*86d7f5d3SJohn Marino 		    break;
677*86d7f5d3SJohn Marino 		}
678*86d7f5d3SJohn Marino 		/* else FALL THROUGH */
679*86d7f5d3SJohn Marino 	    case T_MODIFIED:		/* locally modified */
680*86d7f5d3SJohn Marino 		retval = 0;
681*86d7f5d3SJohn Marino                 if (toss_local_changes)
682*86d7f5d3SJohn Marino                 {
683*86d7f5d3SJohn Marino                     char *bakname;
684*86d7f5d3SJohn Marino                     bakname = backup_file (finfo->file, vers->vn_user);
685*86d7f5d3SJohn Marino                     /* This behavior is sufficiently unexpected to
686*86d7f5d3SJohn Marino                        justify overinformativeness, I think. */
687*86d7f5d3SJohn Marino                     if (!really_quiet && !server_active)
688*86d7f5d3SJohn Marino                         (void) printf ("(Locally modified %s moved to %s)\n",
689*86d7f5d3SJohn Marino                                        finfo->file, bakname);
690*86d7f5d3SJohn Marino                     free (bakname);
691*86d7f5d3SJohn Marino 
692*86d7f5d3SJohn Marino                     /* The locally modified file is still present, but
693*86d7f5d3SJohn Marino                        it will be overwritten by the repository copy
694*86d7f5d3SJohn Marino                        after this. */
695*86d7f5d3SJohn Marino                     status = T_CHECKOUT;
696*86d7f5d3SJohn Marino                     retval = checkout_file (finfo, vers, 0, 0, 1);
697*86d7f5d3SJohn Marino                 }
698*86d7f5d3SJohn Marino                 else
699*86d7f5d3SJohn Marino                 {
700*86d7f5d3SJohn Marino                     if (vers->ts_conflict)
701*86d7f5d3SJohn Marino                     {
702*86d7f5d3SJohn Marino 			if (file_has_markers (finfo))
703*86d7f5d3SJohn Marino                         {
704*86d7f5d3SJohn Marino                             write_letter (finfo, 'C');
705*86d7f5d3SJohn Marino                             retval = 1;
706*86d7f5d3SJohn Marino                         }
707*86d7f5d3SJohn Marino                         else
708*86d7f5d3SJohn Marino                         {
709*86d7f5d3SJohn Marino                             /* Reregister to clear conflict flag. */
710*86d7f5d3SJohn Marino                             Register (finfo->entries, finfo->file,
711*86d7f5d3SJohn Marino                                       vers->vn_rcs, vers->ts_rcs,
712*86d7f5d3SJohn Marino                                       vers->options, vers->tag,
713*86d7f5d3SJohn Marino                                       vers->date, NULL);
714*86d7f5d3SJohn Marino                         }
715*86d7f5d3SJohn Marino                     }
716*86d7f5d3SJohn Marino                     if (!retval)
717*86d7f5d3SJohn Marino                         write_letter (finfo, 'M');
718*86d7f5d3SJohn Marino                 }
719*86d7f5d3SJohn Marino 		break;
720*86d7f5d3SJohn Marino 	    case T_PATCH:		/* needs patch */
721*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
722*86d7f5d3SJohn Marino 		if (patches)
723*86d7f5d3SJohn Marino 		{
724*86d7f5d3SJohn Marino 		    int docheckout;
725*86d7f5d3SJohn Marino 		    struct stat file_info;
726*86d7f5d3SJohn Marino 		    unsigned char checksum[16];
727*86d7f5d3SJohn Marino 
728*86d7f5d3SJohn Marino 		    retval = patch_file (finfo,
729*86d7f5d3SJohn Marino 					 vers, &docheckout,
730*86d7f5d3SJohn Marino 					 &file_info, checksum);
731*86d7f5d3SJohn Marino 		    if (! docheckout)
732*86d7f5d3SJohn Marino 		    {
733*86d7f5d3SJohn Marino 		        if (server_active && retval == 0)
734*86d7f5d3SJohn Marino 			    server_updated (finfo, vers,
735*86d7f5d3SJohn Marino 					    (rcs_diff_patches
736*86d7f5d3SJohn Marino 					     ? SERVER_RCS_DIFF
737*86d7f5d3SJohn Marino 					     : SERVER_PATCHED),
738*86d7f5d3SJohn Marino 					    file_info.st_mode, checksum,
739*86d7f5d3SJohn Marino 					    NULL);
740*86d7f5d3SJohn Marino 			break;
741*86d7f5d3SJohn Marino 		    }
742*86d7f5d3SJohn Marino 		}
743*86d7f5d3SJohn Marino #endif
744*86d7f5d3SJohn Marino 		/* If we're not running as a server, just check the
745*86d7f5d3SJohn Marino 		   file out.  It's simpler and faster than producing
746*86d7f5d3SJohn Marino 		   and applying patches.  */
747*86d7f5d3SJohn Marino 		/* Fall through.  */
748*86d7f5d3SJohn Marino 	    case T_CHECKOUT:		/* needs checkout */
749*86d7f5d3SJohn Marino 		retval = checkout_file (finfo, vers, 0, 0, 1);
750*86d7f5d3SJohn Marino 		break;
751*86d7f5d3SJohn Marino 	    case T_ADDED:		/* added but not committed */
752*86d7f5d3SJohn Marino 		write_letter (finfo, 'A');
753*86d7f5d3SJohn Marino 		retval = 0;
754*86d7f5d3SJohn Marino 		break;
755*86d7f5d3SJohn Marino 	    case T_REMOVED:		/* removed but not committed */
756*86d7f5d3SJohn Marino 		write_letter (finfo, 'R');
757*86d7f5d3SJohn Marino 		retval = 0;
758*86d7f5d3SJohn Marino 		break;
759*86d7f5d3SJohn Marino 	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
760*86d7f5d3SJohn Marino 		retval = scratch_file (finfo, vers);
761*86d7f5d3SJohn Marino 		break;
762*86d7f5d3SJohn Marino 	    default:			/* can't ever happen :-) */
763*86d7f5d3SJohn Marino 		error (0, 0,
764*86d7f5d3SJohn Marino 		       "unknown file status %d for file %s", status, finfo->file);
765*86d7f5d3SJohn Marino 		retval = 0;
766*86d7f5d3SJohn Marino 		break;
767*86d7f5d3SJohn Marino 	}
768*86d7f5d3SJohn Marino     }
769*86d7f5d3SJohn Marino 
770*86d7f5d3SJohn Marino     /* only try to join if things have gone well thus far */
771*86d7f5d3SJohn Marino     if (retval == 0 && join_rev1)
772*86d7f5d3SJohn Marino 	join_file (finfo, vers);
773*86d7f5d3SJohn Marino 
774*86d7f5d3SJohn Marino     /* if this directory has an ignore list, add this file to it */
775*86d7f5d3SJohn Marino     if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL))
776*86d7f5d3SJohn Marino     {
777*86d7f5d3SJohn Marino 	Node *p;
778*86d7f5d3SJohn Marino 
779*86d7f5d3SJohn Marino 	p = getnode ();
780*86d7f5d3SJohn Marino 	p->type = FILES;
781*86d7f5d3SJohn Marino 	p->key = xstrdup (finfo->file);
782*86d7f5d3SJohn Marino 	if (addnode (ignlist, p) != 0)
783*86d7f5d3SJohn Marino 	    freenode (p);
784*86d7f5d3SJohn Marino     }
785*86d7f5d3SJohn Marino 
786*86d7f5d3SJohn Marino     freevers_ts (&vers);
787*86d7f5d3SJohn Marino     return retval;
788*86d7f5d3SJohn Marino }
789*86d7f5d3SJohn Marino 
790*86d7f5d3SJohn Marino 
791*86d7f5d3SJohn Marino 
792*86d7f5d3SJohn Marino static void
update_ignproc(const char * file,const char * dir)793*86d7f5d3SJohn Marino update_ignproc (const char *file, const char *dir)
794*86d7f5d3SJohn Marino {
795*86d7f5d3SJohn Marino     struct file_info finfo;
796*86d7f5d3SJohn Marino     char *tmp;
797*86d7f5d3SJohn Marino 
798*86d7f5d3SJohn Marino     memset (&finfo, 0, sizeof (finfo));
799*86d7f5d3SJohn Marino     finfo.file = file;
800*86d7f5d3SJohn Marino     finfo.update_dir = dir;
801*86d7f5d3SJohn Marino 
802*86d7f5d3SJohn Marino     finfo.fullname = tmp = Xasprintf ("%s%s%s",
803*86d7f5d3SJohn Marino 				      dir[0] == '\0' ? "" : dir,
804*86d7f5d3SJohn Marino 				      dir[0] == '\0' ? "" : "/",
805*86d7f5d3SJohn Marino 				      file);
806*86d7f5d3SJohn Marino     write_letter (&finfo, '?');
807*86d7f5d3SJohn Marino     free (tmp);
808*86d7f5d3SJohn Marino }
809*86d7f5d3SJohn Marino 
810*86d7f5d3SJohn Marino 
811*86d7f5d3SJohn Marino 
812*86d7f5d3SJohn Marino /* ARGSUSED */
813*86d7f5d3SJohn Marino static int
update_filesdone_proc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)814*86d7f5d3SJohn Marino update_filesdone_proc (void *callerdat, int err, const char *repository,
815*86d7f5d3SJohn Marino                        const char *update_dir, List *entries)
816*86d7f5d3SJohn Marino {
817*86d7f5d3SJohn Marino     if (nonbranch < 0) nonbranch = 0;
818*86d7f5d3SJohn Marino     if (rewrite_tag)
819*86d7f5d3SJohn Marino     {
820*86d7f5d3SJohn Marino 	WriteTag (NULL, tag, date, nonbranch, update_dir, repository);
821*86d7f5d3SJohn Marino 	rewrite_tag = 0;
822*86d7f5d3SJohn Marino     }
823*86d7f5d3SJohn Marino 
824*86d7f5d3SJohn Marino     /* if this directory has an ignore list, process it then free it */
825*86d7f5d3SJohn Marino     if (ignlist)
826*86d7f5d3SJohn Marino     {
827*86d7f5d3SJohn Marino 	ignore_files (ignlist, entries, update_dir, update_ignproc);
828*86d7f5d3SJohn Marino 	dellist (&ignlist);
829*86d7f5d3SJohn Marino     }
830*86d7f5d3SJohn Marino 
831*86d7f5d3SJohn Marino     /* Clean up CVS admin dirs if we are export */
832*86d7f5d3SJohn Marino     if (strcmp (cvs_cmd_name, "export") == 0)
833*86d7f5d3SJohn Marino     {
834*86d7f5d3SJohn Marino 	/* I'm not sure the existence_error is actually possible (except
835*86d7f5d3SJohn Marino 	   in cases where we really should print a message), but since
836*86d7f5d3SJohn Marino 	   this code used to ignore all errors, I'll play it safe.  */
837*86d7f5d3SJohn Marino 	if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
838*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s directory", CVSADM);
839*86d7f5d3SJohn Marino     }
840*86d7f5d3SJohn Marino     else if (!server_active && !pipeout)
841*86d7f5d3SJohn Marino     {
842*86d7f5d3SJohn Marino         /* If there is no CVS/Root file, add one */
843*86d7f5d3SJohn Marino         if (!isfile (CVSADM_ROOT))
844*86d7f5d3SJohn Marino 	    Create_Root (NULL, original_parsed_root->original);
845*86d7f5d3SJohn Marino     }
846*86d7f5d3SJohn Marino 
847*86d7f5d3SJohn Marino     return err;
848*86d7f5d3SJohn Marino }
849*86d7f5d3SJohn Marino 
850*86d7f5d3SJohn Marino 
851*86d7f5d3SJohn Marino 
852*86d7f5d3SJohn Marino /*
853*86d7f5d3SJohn Marino  * update_dirent_proc () is called back by the recursion processor before a
854*86d7f5d3SJohn Marino  * sub-directory is processed for update.  In this case, update_dirent proc
855*86d7f5d3SJohn Marino  * will probably create the directory unless -d isn't specified and this is a
856*86d7f5d3SJohn Marino  * new directory.  A return code of 0 indicates the directory should be
857*86d7f5d3SJohn Marino  * processed by the recursion code.  A return of non-zero indicates the
858*86d7f5d3SJohn Marino  * recursion code should skip this directory.
859*86d7f5d3SJohn Marino  */
860*86d7f5d3SJohn Marino static Dtype
update_dirent_proc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)861*86d7f5d3SJohn Marino update_dirent_proc (void *callerdat, const char *dir, const char *repository,
862*86d7f5d3SJohn Marino                     const char *update_dir, List *entries)
863*86d7f5d3SJohn Marino {
864*86d7f5d3SJohn Marino     if (ignore_directory (update_dir))
865*86d7f5d3SJohn Marino     {
866*86d7f5d3SJohn Marino 	/* print the warm fuzzy message */
867*86d7f5d3SJohn Marino 	if (!quiet)
868*86d7f5d3SJohn Marino 	  error (0, 0, "Ignoring %s", update_dir);
869*86d7f5d3SJohn Marino         return R_SKIP_ALL;
870*86d7f5d3SJohn Marino     }
871*86d7f5d3SJohn Marino 
872*86d7f5d3SJohn Marino     if (!isdir (dir))
873*86d7f5d3SJohn Marino     {
874*86d7f5d3SJohn Marino 	/* if we aren't building dirs, blow it off */
875*86d7f5d3SJohn Marino 	if (!update_build_dirs)
876*86d7f5d3SJohn Marino 	    return R_SKIP_ALL;
877*86d7f5d3SJohn Marino 
878*86d7f5d3SJohn Marino 	/* Various CVS administrators are in the habit of removing
879*86d7f5d3SJohn Marino 	   the repository directory for things they don't want any
880*86d7f5d3SJohn Marino 	   more.  I've even been known to do it myself (on rare
881*86d7f5d3SJohn Marino 	   occasions).  Not the usual recommended practice, but we
882*86d7f5d3SJohn Marino 	   want to try to come up with some kind of
883*86d7f5d3SJohn Marino 	   reasonable/documented/sensible behavior.  Generally
884*86d7f5d3SJohn Marino 	   the behavior is to just skip over that directory (see
885*86d7f5d3SJohn Marino 	   dirs test in sanity.sh; the case which reaches here
886*86d7f5d3SJohn Marino 	   is when update -d is specified, and the working directory
887*86d7f5d3SJohn Marino 	   is gone but the subdirectory is still mentioned in
888*86d7f5d3SJohn Marino 	   CVS/Entries).  */
889*86d7f5d3SJohn Marino 	/* In the remote case, the client should refrain from
890*86d7f5d3SJohn Marino 	   sending us the directory in the first place.  So we
891*86d7f5d3SJohn Marino 	   want to continue to give an error, so clients make
892*86d7f5d3SJohn Marino 	   sure to do this.  */
893*86d7f5d3SJohn Marino 	if (!server_active && !isdir (repository))
894*86d7f5d3SJohn Marino 	    return R_SKIP_ALL;
895*86d7f5d3SJohn Marino 
896*86d7f5d3SJohn Marino 	if (noexec)
897*86d7f5d3SJohn Marino 	{
898*86d7f5d3SJohn Marino 	    /* ignore the missing dir if -n is specified */
899*86d7f5d3SJohn Marino 	    error (0, 0, "New directory `%s' -- ignored", update_dir);
900*86d7f5d3SJohn Marino 	    return R_SKIP_ALL;
901*86d7f5d3SJohn Marino 	}
902*86d7f5d3SJohn Marino 	else
903*86d7f5d3SJohn Marino 	{
904*86d7f5d3SJohn Marino 	    /* otherwise, create the dir and appropriate adm files */
905*86d7f5d3SJohn Marino 
906*86d7f5d3SJohn Marino 	    /* If no tag or date were specified on the command line,
907*86d7f5d3SJohn Marino                and we're not using -A, we want the subdirectory to use
908*86d7f5d3SJohn Marino                the tag and date, if any, of the current directory.
909*86d7f5d3SJohn Marino                That way, update -d will work correctly when working on
910*86d7f5d3SJohn Marino                a branch.
911*86d7f5d3SJohn Marino 
912*86d7f5d3SJohn Marino 	       We use TAG_UPDATE_DIR to undo the tag setting in
913*86d7f5d3SJohn Marino 	       update_dirleave_proc.  If we did not do this, we would
914*86d7f5d3SJohn Marino 	       not correctly handle a working directory with multiple
915*86d7f5d3SJohn Marino 	       tags (and maybe we should prohibit such working
916*86d7f5d3SJohn Marino 	       directories, but they work now and we shouldn't make
917*86d7f5d3SJohn Marino 	       them stop working without more thought).  */
918*86d7f5d3SJohn Marino 	    if ((tag == NULL && date == NULL) && ! aflag)
919*86d7f5d3SJohn Marino 	    {
920*86d7f5d3SJohn Marino 		ParseTag (&tag, &date, &nonbranch);
921*86d7f5d3SJohn Marino 		if (tag != NULL || date != NULL)
922*86d7f5d3SJohn Marino 		    tag_update_dir = xstrdup (update_dir);
923*86d7f5d3SJohn Marino 	    }
924*86d7f5d3SJohn Marino 
925*86d7f5d3SJohn Marino 	    make_directory (dir);
926*86d7f5d3SJohn Marino 	    Create_Admin (dir, update_dir, repository, tag, date,
927*86d7f5d3SJohn Marino 			  /* This is a guess.  We will rewrite it later
928*86d7f5d3SJohn Marino 			     via WriteTag.  */
929*86d7f5d3SJohn Marino 			  0,
930*86d7f5d3SJohn Marino 			  0,
931*86d7f5d3SJohn Marino 			  dotemplate);
932*86d7f5d3SJohn Marino 	    rewrite_tag = 1;
933*86d7f5d3SJohn Marino 	    nonbranch = -1;
934*86d7f5d3SJohn Marino 	    warned = 0;
935*86d7f5d3SJohn Marino 	    Subdir_Register (entries, NULL, dir);
936*86d7f5d3SJohn Marino 	}
937*86d7f5d3SJohn Marino     }
938*86d7f5d3SJohn Marino     /* Do we need to check noexec here? */
939*86d7f5d3SJohn Marino     else if (!pipeout)
940*86d7f5d3SJohn Marino     {
941*86d7f5d3SJohn Marino 	char *cvsadmdir;
942*86d7f5d3SJohn Marino 
943*86d7f5d3SJohn Marino 	/* The directory exists.  Check to see if it has a CVS
944*86d7f5d3SJohn Marino 	   subdirectory.  */
945*86d7f5d3SJohn Marino 
946*86d7f5d3SJohn Marino 	cvsadmdir = Xasprintf ("%s/%s", dir, CVSADM);
947*86d7f5d3SJohn Marino 
948*86d7f5d3SJohn Marino 	if (!isdir (cvsadmdir))
949*86d7f5d3SJohn Marino 	{
950*86d7f5d3SJohn Marino 	    /* We cannot successfully recurse into a directory without a CVS
951*86d7f5d3SJohn Marino 	       subdirectory.  Generally we will have already printed
952*86d7f5d3SJohn Marino 	       "? foo".  */
953*86d7f5d3SJohn Marino 	    free (cvsadmdir);
954*86d7f5d3SJohn Marino 	    return R_SKIP_ALL;
955*86d7f5d3SJohn Marino 	}
956*86d7f5d3SJohn Marino 	free (cvsadmdir);
957*86d7f5d3SJohn Marino     }
958*86d7f5d3SJohn Marino 
959*86d7f5d3SJohn Marino     /*
960*86d7f5d3SJohn Marino      * If we are building dirs and not going to stdout, we make sure there is
961*86d7f5d3SJohn Marino      * no static entries file and write the tag file as appropriate
962*86d7f5d3SJohn Marino      */
963*86d7f5d3SJohn Marino     if (!pipeout)
964*86d7f5d3SJohn Marino     {
965*86d7f5d3SJohn Marino 	if (update_build_dirs)
966*86d7f5d3SJohn Marino 	{
967*86d7f5d3SJohn Marino 	    char *tmp = Xasprintf ("%s/%s", dir, CVSADM_ENTSTAT);
968*86d7f5d3SJohn Marino 
969*86d7f5d3SJohn Marino 	    if (unlink_file (tmp) < 0 && ! existence_error (errno))
970*86d7f5d3SJohn Marino 		error (1, errno, "cannot remove file %s", tmp);
971*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
972*86d7f5d3SJohn Marino 	    if (server_active)
973*86d7f5d3SJohn Marino 		server_clear_entstat (update_dir, repository);
974*86d7f5d3SJohn Marino #endif
975*86d7f5d3SJohn Marino 	    free (tmp);
976*86d7f5d3SJohn Marino 	}
977*86d7f5d3SJohn Marino 
978*86d7f5d3SJohn Marino 	/* keep the CVS/Tag file current with the specified arguments */
979*86d7f5d3SJohn Marino 	if (aflag || tag || date)
980*86d7f5d3SJohn Marino 	{
981*86d7f5d3SJohn Marino 	    WriteTag (dir, tag, date, 0, update_dir, repository);
982*86d7f5d3SJohn Marino 	    rewrite_tag = 1;
983*86d7f5d3SJohn Marino 	    nonbranch = -1;
984*86d7f5d3SJohn Marino 	    warned = 0;
985*86d7f5d3SJohn Marino 	}
986*86d7f5d3SJohn Marino 
987*86d7f5d3SJohn Marino 	WriteTemplate (update_dir, dotemplate, repository);
988*86d7f5d3SJohn Marino 
989*86d7f5d3SJohn Marino 	/* initialize the ignore list for this directory */
990*86d7f5d3SJohn Marino 	ignlist = getlist ();
991*86d7f5d3SJohn Marino     }
992*86d7f5d3SJohn Marino 
993*86d7f5d3SJohn Marino     /* print the warm fuzzy message */
994*86d7f5d3SJohn Marino     if (!quiet)
995*86d7f5d3SJohn Marino 	error (0, 0, "Updating %s", update_dir);
996*86d7f5d3SJohn Marino 
997*86d7f5d3SJohn Marino     return R_PROCESS;
998*86d7f5d3SJohn Marino }
999*86d7f5d3SJohn Marino 
1000*86d7f5d3SJohn Marino 
1001*86d7f5d3SJohn Marino 
1002*86d7f5d3SJohn Marino /*
1003*86d7f5d3SJohn Marino  * update_dirleave_proc () is called back by the recursion code upon leaving
1004*86d7f5d3SJohn Marino  * a directory.  It will prune empty directories if needed and will execute
1005*86d7f5d3SJohn Marino  * any appropriate update programs.
1006*86d7f5d3SJohn Marino  */
1007*86d7f5d3SJohn Marino /* ARGSUSED */
1008*86d7f5d3SJohn Marino static int
update_dirleave_proc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)1009*86d7f5d3SJohn Marino update_dirleave_proc (void *callerdat, const char *dir, int err,
1010*86d7f5d3SJohn Marino                       const char *update_dir, List *entries)
1011*86d7f5d3SJohn Marino {
1012*86d7f5d3SJohn Marino     /* Delete the ignore list if it hasn't already been done.  */
1013*86d7f5d3SJohn Marino     if (ignlist)
1014*86d7f5d3SJohn Marino 	dellist (&ignlist);
1015*86d7f5d3SJohn Marino 
1016*86d7f5d3SJohn Marino     /* If we set the tag or date for a new subdirectory in
1017*86d7f5d3SJohn Marino        update_dirent_proc, and we're now done with that subdirectory,
1018*86d7f5d3SJohn Marino        undo the tag/date setting.  Note that we know that the tag and
1019*86d7f5d3SJohn Marino        date were both originally NULL in this case.  */
1020*86d7f5d3SJohn Marino     if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
1021*86d7f5d3SJohn Marino     {
1022*86d7f5d3SJohn Marino 	if (tag != NULL)
1023*86d7f5d3SJohn Marino 	{
1024*86d7f5d3SJohn Marino 	    free (tag);
1025*86d7f5d3SJohn Marino 	    tag = NULL;
1026*86d7f5d3SJohn Marino 	}
1027*86d7f5d3SJohn Marino 	if (date != NULL)
1028*86d7f5d3SJohn Marino 	{
1029*86d7f5d3SJohn Marino 	    free (date);
1030*86d7f5d3SJohn Marino 	    date = NULL;
1031*86d7f5d3SJohn Marino 	}
1032*86d7f5d3SJohn Marino 	nonbranch = -1;
1033*86d7f5d3SJohn Marino 	warned = 0;
1034*86d7f5d3SJohn Marino 	free (tag_update_dir);
1035*86d7f5d3SJohn Marino 	tag_update_dir = NULL;
1036*86d7f5d3SJohn Marino     }
1037*86d7f5d3SJohn Marino 
1038*86d7f5d3SJohn Marino     if (strchr (dir, '/') == NULL)
1039*86d7f5d3SJohn Marino     {
1040*86d7f5d3SJohn Marino 	/* FIXME: chdir ("..") loses with symlinks.  */
1041*86d7f5d3SJohn Marino 	/* Prune empty dirs on the way out - if necessary */
1042*86d7f5d3SJohn Marino 	(void) CVS_CHDIR ("..");
1043*86d7f5d3SJohn Marino 	if (update_prune_dirs && isemptydir (dir, 0))
1044*86d7f5d3SJohn Marino 	{
1045*86d7f5d3SJohn Marino 	    /* I'm not sure the existence_error is actually possible (except
1046*86d7f5d3SJohn Marino 	       in cases where we really should print a message), but since
1047*86d7f5d3SJohn Marino 	       this code used to ignore all errors, I'll play it safe.	*/
1048*86d7f5d3SJohn Marino 	    if (unlink_file_dir (dir) < 0 && !existence_error (errno))
1049*86d7f5d3SJohn Marino 		error (0, errno, "cannot remove %s directory", dir);
1050*86d7f5d3SJohn Marino 	    Subdir_Deregister (entries, NULL, dir);
1051*86d7f5d3SJohn Marino 	}
1052*86d7f5d3SJohn Marino     }
1053*86d7f5d3SJohn Marino 
1054*86d7f5d3SJohn Marino     return err;
1055*86d7f5d3SJohn Marino }
1056*86d7f5d3SJohn Marino 
1057*86d7f5d3SJohn Marino 
1058*86d7f5d3SJohn Marino 
1059*86d7f5d3SJohn Marino /* Returns 1 if the file indicated by node has been removed.  */
1060*86d7f5d3SJohn Marino static int
isremoved(Node * node,void * closure)1061*86d7f5d3SJohn Marino isremoved (Node *node, void *closure)
1062*86d7f5d3SJohn Marino {
1063*86d7f5d3SJohn Marino     Entnode *entdata = node->data;
1064*86d7f5d3SJohn Marino 
1065*86d7f5d3SJohn Marino     /* If the first character of the version is a '-', the file has been
1066*86d7f5d3SJohn Marino        removed. */
1067*86d7f5d3SJohn Marino     return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
1068*86d7f5d3SJohn Marino }
1069*86d7f5d3SJohn Marino 
1070*86d7f5d3SJohn Marino 
1071*86d7f5d3SJohn Marino 
1072*86d7f5d3SJohn Marino /* Returns 1 if the argument directory is completely empty, other than the
1073*86d7f5d3SJohn Marino    existence of the CVS directory entry.  Zero otherwise.  If MIGHT_NOT_EXIST
1074*86d7f5d3SJohn Marino    and the directory doesn't exist, then just return 0.  */
1075*86d7f5d3SJohn Marino int
isemptydir(const char * dir,int might_not_exist)1076*86d7f5d3SJohn Marino isemptydir (const char *dir, int might_not_exist)
1077*86d7f5d3SJohn Marino {
1078*86d7f5d3SJohn Marino     DIR *dirp;
1079*86d7f5d3SJohn Marino     struct dirent *dp;
1080*86d7f5d3SJohn Marino 
1081*86d7f5d3SJohn Marino     if ((dirp = CVS_OPENDIR (dir)) == NULL)
1082*86d7f5d3SJohn Marino     {
1083*86d7f5d3SJohn Marino 	if (might_not_exist && existence_error (errno))
1084*86d7f5d3SJohn Marino 	    return 0;
1085*86d7f5d3SJohn Marino 	error (0, errno, "cannot open directory %s for empty check", dir);
1086*86d7f5d3SJohn Marino 	return 0;
1087*86d7f5d3SJohn Marino     }
1088*86d7f5d3SJohn Marino     errno = 0;
1089*86d7f5d3SJohn Marino     while ((dp = CVS_READDIR (dirp)) != NULL)
1090*86d7f5d3SJohn Marino     {
1091*86d7f5d3SJohn Marino 	if (strcmp (dp->d_name, ".") != 0
1092*86d7f5d3SJohn Marino 	    && strcmp (dp->d_name, "..") != 0)
1093*86d7f5d3SJohn Marino 	{
1094*86d7f5d3SJohn Marino 	    if (strcmp (dp->d_name, CVSADM) != 0)
1095*86d7f5d3SJohn Marino 	    {
1096*86d7f5d3SJohn Marino 		/* An entry other than the CVS directory.  The directory
1097*86d7f5d3SJohn Marino 		   is certainly not empty. */
1098*86d7f5d3SJohn Marino 		(void) CVS_CLOSEDIR (dirp);
1099*86d7f5d3SJohn Marino 		return 0;
1100*86d7f5d3SJohn Marino 	    }
1101*86d7f5d3SJohn Marino 	    else
1102*86d7f5d3SJohn Marino 	    {
1103*86d7f5d3SJohn Marino 		/* The CVS directory entry.  We don't have to worry about
1104*86d7f5d3SJohn Marino 		   this unless the Entries file indicates that files have
1105*86d7f5d3SJohn Marino 		   been removed, but not committed, in this directory.
1106*86d7f5d3SJohn Marino 		   (Removing the directory would prevent people from
1107*86d7f5d3SJohn Marino 		   comitting the fact that they removed the files!) */
1108*86d7f5d3SJohn Marino 		List *l;
1109*86d7f5d3SJohn Marino 		int files_removed;
1110*86d7f5d3SJohn Marino 		struct saved_cwd cwd;
1111*86d7f5d3SJohn Marino 
1112*86d7f5d3SJohn Marino 		if (save_cwd (&cwd))
1113*86d7f5d3SJohn Marino 		    error (1, errno, "Failed to save current directory.");
1114*86d7f5d3SJohn Marino 
1115*86d7f5d3SJohn Marino 		if (CVS_CHDIR (dir) < 0)
1116*86d7f5d3SJohn Marino 		    error (1, errno, "cannot change directory to %s", dir);
1117*86d7f5d3SJohn Marino 		l = Entries_Open (0, NULL);
1118*86d7f5d3SJohn Marino 		files_removed = walklist (l, isremoved, 0);
1119*86d7f5d3SJohn Marino 		Entries_Close (l);
1120*86d7f5d3SJohn Marino 
1121*86d7f5d3SJohn Marino 		if (restore_cwd (&cwd))
1122*86d7f5d3SJohn Marino 		    error (1, errno,
1123*86d7f5d3SJohn Marino 		           "Failed to restore current directory, `%s'.",
1124*86d7f5d3SJohn Marino 		           cwd.name);
1125*86d7f5d3SJohn Marino 		free_cwd (&cwd);
1126*86d7f5d3SJohn Marino 
1127*86d7f5d3SJohn Marino 		if (files_removed != 0)
1128*86d7f5d3SJohn Marino 		{
1129*86d7f5d3SJohn Marino 		    /* There are files that have been removed, but not
1130*86d7f5d3SJohn Marino 		       committed!  Do not consider the directory empty. */
1131*86d7f5d3SJohn Marino 		    (void) CVS_CLOSEDIR (dirp);
1132*86d7f5d3SJohn Marino 		    return 0;
1133*86d7f5d3SJohn Marino 		}
1134*86d7f5d3SJohn Marino 	    }
1135*86d7f5d3SJohn Marino 	}
1136*86d7f5d3SJohn Marino 	errno = 0;
1137*86d7f5d3SJohn Marino     }
1138*86d7f5d3SJohn Marino     if (errno != 0)
1139*86d7f5d3SJohn Marino     {
1140*86d7f5d3SJohn Marino 	error (0, errno, "cannot read directory %s", dir);
1141*86d7f5d3SJohn Marino 	(void) CVS_CLOSEDIR (dirp);
1142*86d7f5d3SJohn Marino 	return 0;
1143*86d7f5d3SJohn Marino     }
1144*86d7f5d3SJohn Marino     (void) CVS_CLOSEDIR (dirp);
1145*86d7f5d3SJohn Marino     return 1;
1146*86d7f5d3SJohn Marino }
1147*86d7f5d3SJohn Marino 
1148*86d7f5d3SJohn Marino 
1149*86d7f5d3SJohn Marino 
1150*86d7f5d3SJohn Marino /*
1151*86d7f5d3SJohn Marino  * scratch the Entries file entry associated with a file
1152*86d7f5d3SJohn Marino  */
1153*86d7f5d3SJohn Marino static int
scratch_file(struct file_info * finfo,Vers_TS * vers)1154*86d7f5d3SJohn Marino scratch_file (struct file_info *finfo, Vers_TS *vers)
1155*86d7f5d3SJohn Marino {
1156*86d7f5d3SJohn Marino     history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
1157*86d7f5d3SJohn Marino     Scratch_Entry (finfo->entries, finfo->file);
1158*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1159*86d7f5d3SJohn Marino     if (server_active)
1160*86d7f5d3SJohn Marino     {
1161*86d7f5d3SJohn Marino 	if (vers->ts_user == NULL)
1162*86d7f5d3SJohn Marino 	    server_scratch_entry_only ();
1163*86d7f5d3SJohn Marino 	server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, NULL, NULL);
1164*86d7f5d3SJohn Marino     }
1165*86d7f5d3SJohn Marino #endif
1166*86d7f5d3SJohn Marino     if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1167*86d7f5d3SJohn Marino 	error (0, errno, "unable to remove %s", finfo->fullname);
1168*86d7f5d3SJohn Marino     else if (!server_active)
1169*86d7f5d3SJohn Marino     {
1170*86d7f5d3SJohn Marino 	/* skip this step when the server is running since
1171*86d7f5d3SJohn Marino 	 * server_updated should have handled it */
1172*86d7f5d3SJohn Marino 	/* keep the vers structure up to date in case we do a join
1173*86d7f5d3SJohn Marino 	 * - if there isn't a file, it can't very well have a version number, can it?
1174*86d7f5d3SJohn Marino 	 */
1175*86d7f5d3SJohn Marino 	if (vers->vn_user != NULL)
1176*86d7f5d3SJohn Marino 	{
1177*86d7f5d3SJohn Marino 	    free (vers->vn_user);
1178*86d7f5d3SJohn Marino 	    vers->vn_user = NULL;
1179*86d7f5d3SJohn Marino 	}
1180*86d7f5d3SJohn Marino 	if (vers->ts_user != NULL)
1181*86d7f5d3SJohn Marino 	{
1182*86d7f5d3SJohn Marino 	    free (vers->ts_user);
1183*86d7f5d3SJohn Marino 	    vers->ts_user = NULL;
1184*86d7f5d3SJohn Marino 	}
1185*86d7f5d3SJohn Marino     }
1186*86d7f5d3SJohn Marino     return 0;
1187*86d7f5d3SJohn Marino }
1188*86d7f5d3SJohn Marino 
1189*86d7f5d3SJohn Marino 
1190*86d7f5d3SJohn Marino 
1191*86d7f5d3SJohn Marino /*
1192*86d7f5d3SJohn Marino  * Check out a file.
1193*86d7f5d3SJohn Marino  */
1194*86d7f5d3SJohn Marino static int
checkout_file(struct file_info * finfo,Vers_TS * vers_ts,int adding,int merging,int update_server)1195*86d7f5d3SJohn Marino checkout_file (struct file_info *finfo, Vers_TS *vers_ts, int adding,
1196*86d7f5d3SJohn Marino                int merging, int update_server)
1197*86d7f5d3SJohn Marino {
1198*86d7f5d3SJohn Marino     char *backup;
1199*86d7f5d3SJohn Marino     int set_time, retval = 0;
1200*86d7f5d3SJohn Marino     int status;
1201*86d7f5d3SJohn Marino     int file_is_dead;
1202*86d7f5d3SJohn Marino     struct buffer *revbuf;
1203*86d7f5d3SJohn Marino 
1204*86d7f5d3SJohn Marino     backup = NULL;
1205*86d7f5d3SJohn Marino     revbuf = NULL;
1206*86d7f5d3SJohn Marino 
1207*86d7f5d3SJohn Marino     /* Don't screw with backup files if we're going to stdout, or if
1208*86d7f5d3SJohn Marino        we are the server.  */
1209*86d7f5d3SJohn Marino     if (!pipeout && !server_active)
1210*86d7f5d3SJohn Marino     {
1211*86d7f5d3SJohn Marino 	backup = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1212*86d7f5d3SJohn Marino 	if (isfile (finfo->file))
1213*86d7f5d3SJohn Marino 	    rename_file (finfo->file, backup);
1214*86d7f5d3SJohn Marino 	else
1215*86d7f5d3SJohn Marino 	{
1216*86d7f5d3SJohn Marino 	    /* If -f/-t wrappers are being used to wrap up a directory,
1217*86d7f5d3SJohn Marino 	       then backup might be a directory instead of just a file.  */
1218*86d7f5d3SJohn Marino 	    if (unlink_file_dir (backup) < 0)
1219*86d7f5d3SJohn Marino 	    {
1220*86d7f5d3SJohn Marino 		/* Not sure if the existence_error check is needed here.  */
1221*86d7f5d3SJohn Marino 		if (!existence_error (errno))
1222*86d7f5d3SJohn Marino 		    /* FIXME: should include update_dir in message.  */
1223*86d7f5d3SJohn Marino 		    error (0, errno, "error removing %s", backup);
1224*86d7f5d3SJohn Marino 	    }
1225*86d7f5d3SJohn Marino 	    free (backup);
1226*86d7f5d3SJohn Marino 	    backup = NULL;
1227*86d7f5d3SJohn Marino 	}
1228*86d7f5d3SJohn Marino     }
1229*86d7f5d3SJohn Marino 
1230*86d7f5d3SJohn Marino     file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
1231*86d7f5d3SJohn Marino 
1232*86d7f5d3SJohn Marino     if (!file_is_dead)
1233*86d7f5d3SJohn Marino     {
1234*86d7f5d3SJohn Marino 	/*
1235*86d7f5d3SJohn Marino 	 * if we are checking out to stdout, print a nice message to
1236*86d7f5d3SJohn Marino 	 * stderr, and add the -p flag to the command */
1237*86d7f5d3SJohn Marino 	if (pipeout)
1238*86d7f5d3SJohn Marino 	{
1239*86d7f5d3SJohn Marino 	    if (!quiet)
1240*86d7f5d3SJohn Marino 	    {
1241*86d7f5d3SJohn Marino 		cvs_outerr ("\
1242*86d7f5d3SJohn Marino ===================================================================\n\
1243*86d7f5d3SJohn Marino Checking out ", 0);
1244*86d7f5d3SJohn Marino 		cvs_outerr (finfo->fullname, 0);
1245*86d7f5d3SJohn Marino 		cvs_outerr ("\n\
1246*86d7f5d3SJohn Marino RCS:  ", 0);
1247*86d7f5d3SJohn Marino 		cvs_outerr (vers_ts->srcfile->print_path, 0);
1248*86d7f5d3SJohn Marino 		cvs_outerr ("\n\
1249*86d7f5d3SJohn Marino VERS: ", 0);
1250*86d7f5d3SJohn Marino 		cvs_outerr (vers_ts->vn_rcs, 0);
1251*86d7f5d3SJohn Marino 		cvs_outerr ("\n***************\n", 0);
1252*86d7f5d3SJohn Marino 	    }
1253*86d7f5d3SJohn Marino 	}
1254*86d7f5d3SJohn Marino 
1255*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1256*86d7f5d3SJohn Marino 	if (update_server
1257*86d7f5d3SJohn Marino 	    && server_active
1258*86d7f5d3SJohn Marino 	    && ! pipeout
1259*86d7f5d3SJohn Marino 	    && ! file_gzip_level
1260*86d7f5d3SJohn Marino 	    && ! joining ()
1261*86d7f5d3SJohn Marino 	    && ! wrap_name_has (finfo->file, WRAP_FROMCVS))
1262*86d7f5d3SJohn Marino 	{
1263*86d7f5d3SJohn Marino 	    revbuf = buf_nonio_initialize (NULL);
1264*86d7f5d3SJohn Marino 	    status = RCS_checkout (vers_ts->srcfile, NULL,
1265*86d7f5d3SJohn Marino 				   vers_ts->vn_rcs, vers_ts->tag,
1266*86d7f5d3SJohn Marino 				   vers_ts->options, RUN_TTY,
1267*86d7f5d3SJohn Marino 				   checkout_to_buffer, revbuf);
1268*86d7f5d3SJohn Marino 	}
1269*86d7f5d3SJohn Marino 	else
1270*86d7f5d3SJohn Marino #endif
1271*86d7f5d3SJohn Marino 	    status = RCS_checkout (vers_ts->srcfile,
1272*86d7f5d3SJohn Marino 				   pipeout ? NULL : finfo->file,
1273*86d7f5d3SJohn Marino 				   vers_ts->vn_rcs, vers_ts->tag,
1274*86d7f5d3SJohn Marino 				   vers_ts->options, RUN_TTY, NULL, NULL);
1275*86d7f5d3SJohn Marino     }
1276*86d7f5d3SJohn Marino     if (file_is_dead || status == 0)
1277*86d7f5d3SJohn Marino     {
1278*86d7f5d3SJohn Marino 	mode_t mode;
1279*86d7f5d3SJohn Marino 
1280*86d7f5d3SJohn Marino 	mode = (mode_t) -1;
1281*86d7f5d3SJohn Marino 
1282*86d7f5d3SJohn Marino 	if (!pipeout)
1283*86d7f5d3SJohn Marino 	{
1284*86d7f5d3SJohn Marino 	    Vers_TS *xvers_ts;
1285*86d7f5d3SJohn Marino 
1286*86d7f5d3SJohn Marino 	    if (revbuf != NULL && !noexec)
1287*86d7f5d3SJohn Marino 	    {
1288*86d7f5d3SJohn Marino 		struct stat sb;
1289*86d7f5d3SJohn Marino 
1290*86d7f5d3SJohn Marino 		/* FIXME: We should have RCS_checkout return the mode.
1291*86d7f5d3SJohn Marino 		   That would also fix the kludge with noexec, above, which
1292*86d7f5d3SJohn Marino 		   is here only because noexec doesn't write srcfile->path
1293*86d7f5d3SJohn Marino 		   for us to stat.  */
1294*86d7f5d3SJohn Marino 		if (stat (vers_ts->srcfile->path, &sb) < 0)
1295*86d7f5d3SJohn Marino 		{
1296*86d7f5d3SJohn Marino #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
1297*86d7f5d3SJohn Marino 		    buf_free (revbuf);
1298*86d7f5d3SJohn Marino #endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
1299*86d7f5d3SJohn Marino 		    error (1, errno, "cannot stat %s",
1300*86d7f5d3SJohn Marino 			   vers_ts->srcfile->path);
1301*86d7f5d3SJohn Marino 		}
1302*86d7f5d3SJohn Marino 		mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
1303*86d7f5d3SJohn Marino 	    }
1304*86d7f5d3SJohn Marino 
1305*86d7f5d3SJohn Marino 	    if (cvswrite
1306*86d7f5d3SJohn Marino 		&& !file_is_dead
1307*86d7f5d3SJohn Marino 		&& !fileattr_get (finfo->file, "_watched"))
1308*86d7f5d3SJohn Marino 	    {
1309*86d7f5d3SJohn Marino 		if (revbuf == NULL)
1310*86d7f5d3SJohn Marino 		    xchmod (finfo->file, 1);
1311*86d7f5d3SJohn Marino 		else
1312*86d7f5d3SJohn Marino 		{
1313*86d7f5d3SJohn Marino 		    /* We know that we are the server here, so
1314*86d7f5d3SJohn Marino                        although xchmod checks umask, we don't bother.  */
1315*86d7f5d3SJohn Marino 		    mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
1316*86d7f5d3SJohn Marino 			     | ((mode & S_IRGRP) ? S_IWGRP : 0)
1317*86d7f5d3SJohn Marino 			     | ((mode & S_IROTH) ? S_IWOTH : 0));
1318*86d7f5d3SJohn Marino 		}
1319*86d7f5d3SJohn Marino 	    }
1320*86d7f5d3SJohn Marino 
1321*86d7f5d3SJohn Marino 	    {
1322*86d7f5d3SJohn Marino 		/* A newly checked out file is never under the spell
1323*86d7f5d3SJohn Marino 		   of "cvs edit".  If we think we were editing it
1324*86d7f5d3SJohn Marino 		   from a previous life, clean up.  Would be better to
1325*86d7f5d3SJohn Marino 		   check for same the working directory instead of
1326*86d7f5d3SJohn Marino 		   same user, but that is hairy.  */
1327*86d7f5d3SJohn Marino 
1328*86d7f5d3SJohn Marino 		struct addremove_args args;
1329*86d7f5d3SJohn Marino 
1330*86d7f5d3SJohn Marino 		editor_set (finfo->file, getcaller (), NULL);
1331*86d7f5d3SJohn Marino 
1332*86d7f5d3SJohn Marino 		memset (&args, 0, sizeof args);
1333*86d7f5d3SJohn Marino 		args.remove_temp = 1;
1334*86d7f5d3SJohn Marino 		watch_modify_watchers (finfo->file, &args);
1335*86d7f5d3SJohn Marino 	    }
1336*86d7f5d3SJohn Marino 
1337*86d7f5d3SJohn Marino 	    /* set the time from the RCS file iff it was unknown before */
1338*86d7f5d3SJohn Marino 	    set_time =
1339*86d7f5d3SJohn Marino 		(!noexec
1340*86d7f5d3SJohn Marino 		 && (vers_ts->vn_user == NULL ||
1341*86d7f5d3SJohn Marino 		     strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
1342*86d7f5d3SJohn Marino 		 && !file_is_dead);
1343*86d7f5d3SJohn Marino 
1344*86d7f5d3SJohn Marino 	    wrap_fromcvs_process_file (finfo->file);
1345*86d7f5d3SJohn Marino 
1346*86d7f5d3SJohn Marino 	    xvers_ts = Version_TS (finfo, options, tag, date,
1347*86d7f5d3SJohn Marino 				   force_tag_match, set_time);
1348*86d7f5d3SJohn Marino 	    if (strcmp (xvers_ts->options, "-V4") == 0)
1349*86d7f5d3SJohn Marino 		xvers_ts->options[0] = '\0';
1350*86d7f5d3SJohn Marino 
1351*86d7f5d3SJohn Marino 	    if (revbuf != NULL)
1352*86d7f5d3SJohn Marino 	    {
1353*86d7f5d3SJohn Marino 		/* If we stored the file data into a buffer, then we
1354*86d7f5d3SJohn Marino                    didn't create a file at all, so xvers_ts->ts_user
1355*86d7f5d3SJohn Marino                    is wrong.  The correct value is to have it be the
1356*86d7f5d3SJohn Marino                    same as xvers_ts->ts_rcs, meaning that the working
1357*86d7f5d3SJohn Marino                    file is unchanged from the RCS file.
1358*86d7f5d3SJohn Marino 
1359*86d7f5d3SJohn Marino 		   FIXME: We should tell Version_TS not to waste time
1360*86d7f5d3SJohn Marino 		   statting the nonexistent file.
1361*86d7f5d3SJohn Marino 
1362*86d7f5d3SJohn Marino 		   FIXME: Actually, I don't think the ts_user value
1363*86d7f5d3SJohn Marino 		   matters at all here.  The only use I know of is
1364*86d7f5d3SJohn Marino 		   that it is printed in a trace message by
1365*86d7f5d3SJohn Marino 		   Server_Register.  */
1366*86d7f5d3SJohn Marino 
1367*86d7f5d3SJohn Marino 		if (xvers_ts->ts_user != NULL)
1368*86d7f5d3SJohn Marino 		    free (xvers_ts->ts_user);
1369*86d7f5d3SJohn Marino 		xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
1370*86d7f5d3SJohn Marino 	    }
1371*86d7f5d3SJohn Marino 
1372*86d7f5d3SJohn Marino 	    (void) time (&last_register_time);
1373*86d7f5d3SJohn Marino 
1374*86d7f5d3SJohn Marino 	    if (file_is_dead)
1375*86d7f5d3SJohn Marino 	    {
1376*86d7f5d3SJohn Marino 		if (xvers_ts->vn_user != NULL)
1377*86d7f5d3SJohn Marino 		{
1378*86d7f5d3SJohn Marino 		    error (0, 0,
1379*86d7f5d3SJohn Marino 			   "warning: %s is not (any longer) pertinent",
1380*86d7f5d3SJohn Marino  			   finfo->fullname);
1381*86d7f5d3SJohn Marino 		}
1382*86d7f5d3SJohn Marino 		Scratch_Entry (finfo->entries, finfo->file);
1383*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1384*86d7f5d3SJohn Marino 		if (server_active && xvers_ts->ts_user == NULL)
1385*86d7f5d3SJohn Marino 		    server_scratch_entry_only ();
1386*86d7f5d3SJohn Marino #endif
1387*86d7f5d3SJohn Marino 		/* FIXME: Rather than always unlink'ing, and ignoring the
1388*86d7f5d3SJohn Marino 		   existence_error, we should do the unlink only if
1389*86d7f5d3SJohn Marino 		   vers_ts->ts_user is non-NULL.  Then there would be no
1390*86d7f5d3SJohn Marino 		   need to ignore an existence_error (for example, if the
1391*86d7f5d3SJohn Marino 		   user removes the file while we are running).  */
1392*86d7f5d3SJohn Marino 		if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1393*86d7f5d3SJohn Marino 		{
1394*86d7f5d3SJohn Marino 		    error (0, errno, "cannot remove %s", finfo->fullname);
1395*86d7f5d3SJohn Marino 		}
1396*86d7f5d3SJohn Marino 	    }
1397*86d7f5d3SJohn Marino 	    else
1398*86d7f5d3SJohn Marino 		Register (finfo->entries, finfo->file,
1399*86d7f5d3SJohn Marino 			  adding ? "0" : xvers_ts->vn_rcs,
1400*86d7f5d3SJohn Marino 			  xvers_ts->ts_user, xvers_ts->options,
1401*86d7f5d3SJohn Marino 			  xvers_ts->tag, xvers_ts->date,
1402*86d7f5d3SJohn Marino 			  NULL); /* Clear conflict flag on fresh checkout */
1403*86d7f5d3SJohn Marino 
1404*86d7f5d3SJohn Marino 	    /* fix up the vers structure, in case it is used by join */
1405*86d7f5d3SJohn Marino 	    if (join_rev1)
1406*86d7f5d3SJohn Marino 	    {
1407*86d7f5d3SJohn Marino 		/* FIXME: Throwing away the original revision info is almost
1408*86d7f5d3SJohn Marino 		   certainly wrong -- what if join_rev1 is "BASE"?  */
1409*86d7f5d3SJohn Marino 		if (vers_ts->vn_user != NULL)
1410*86d7f5d3SJohn Marino 		    free (vers_ts->vn_user);
1411*86d7f5d3SJohn Marino 		if (vers_ts->vn_rcs != NULL)
1412*86d7f5d3SJohn Marino 		    free (vers_ts->vn_rcs);
1413*86d7f5d3SJohn Marino 		vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1414*86d7f5d3SJohn Marino 		vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1415*86d7f5d3SJohn Marino 	    }
1416*86d7f5d3SJohn Marino 
1417*86d7f5d3SJohn Marino 	    /* If this is really Update and not Checkout, recode history */
1418*86d7f5d3SJohn Marino 	    if (strcmp (cvs_cmd_name, "update") == 0)
1419*86d7f5d3SJohn Marino 		history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1420*86d7f5d3SJohn Marino 			       finfo->repository);
1421*86d7f5d3SJohn Marino 
1422*86d7f5d3SJohn Marino 	    freevers_ts (&xvers_ts);
1423*86d7f5d3SJohn Marino 
1424*86d7f5d3SJohn Marino 	    if (!really_quiet && !file_is_dead)
1425*86d7f5d3SJohn Marino 	    {
1426*86d7f5d3SJohn Marino 		write_letter (finfo, 'U');
1427*86d7f5d3SJohn Marino 	    }
1428*86d7f5d3SJohn Marino 	}
1429*86d7f5d3SJohn Marino 
1430*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1431*86d7f5d3SJohn Marino 	if (update_server && server_active)
1432*86d7f5d3SJohn Marino 	    server_updated (finfo, vers_ts,
1433*86d7f5d3SJohn Marino 			    merging ? SERVER_MERGED : SERVER_UPDATED,
1434*86d7f5d3SJohn Marino 			    mode, NULL, revbuf);
1435*86d7f5d3SJohn Marino #endif
1436*86d7f5d3SJohn Marino     }
1437*86d7f5d3SJohn Marino     else
1438*86d7f5d3SJohn Marino     {
1439*86d7f5d3SJohn Marino 	if (backup != NULL)
1440*86d7f5d3SJohn Marino 	{
1441*86d7f5d3SJohn Marino 	    rename_file (backup, finfo->file);
1442*86d7f5d3SJohn Marino 	    free (backup);
1443*86d7f5d3SJohn Marino 	    backup = NULL;
1444*86d7f5d3SJohn Marino 	}
1445*86d7f5d3SJohn Marino 
1446*86d7f5d3SJohn Marino 	error (0, 0, "could not check out %s", finfo->fullname);
1447*86d7f5d3SJohn Marino 
1448*86d7f5d3SJohn Marino 	retval = status;
1449*86d7f5d3SJohn Marino     }
1450*86d7f5d3SJohn Marino 
1451*86d7f5d3SJohn Marino     if (backup != NULL)
1452*86d7f5d3SJohn Marino     {
1453*86d7f5d3SJohn Marino 	/* If -f/-t wrappers are being used to wrap up a directory,
1454*86d7f5d3SJohn Marino 	   then backup might be a directory instead of just a file.  */
1455*86d7f5d3SJohn Marino 	if (unlink_file_dir (backup) < 0)
1456*86d7f5d3SJohn Marino 	{
1457*86d7f5d3SJohn Marino 	    /* Not sure if the existence_error check is needed here.  */
1458*86d7f5d3SJohn Marino 	    if (!existence_error (errno))
1459*86d7f5d3SJohn Marino 		/* FIXME: should include update_dir in message.  */
1460*86d7f5d3SJohn Marino 		error (0, errno, "error removing %s", backup);
1461*86d7f5d3SJohn Marino 	}
1462*86d7f5d3SJohn Marino 	free (backup);
1463*86d7f5d3SJohn Marino     }
1464*86d7f5d3SJohn Marino 
1465*86d7f5d3SJohn Marino #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
1466*86d7f5d3SJohn Marino     if (revbuf != NULL)
1467*86d7f5d3SJohn Marino 	buf_free (revbuf);
1468*86d7f5d3SJohn Marino #endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
1469*86d7f5d3SJohn Marino     return retval;
1470*86d7f5d3SJohn Marino }
1471*86d7f5d3SJohn Marino 
1472*86d7f5d3SJohn Marino 
1473*86d7f5d3SJohn Marino 
1474*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1475*86d7f5d3SJohn Marino 
1476*86d7f5d3SJohn Marino /* This function is used to write data from a file being checked out
1477*86d7f5d3SJohn Marino    into a buffer.  */
1478*86d7f5d3SJohn Marino 
1479*86d7f5d3SJohn Marino static void
checkout_to_buffer(void * callerdat,const char * data,size_t len)1480*86d7f5d3SJohn Marino checkout_to_buffer (void *callerdat, const char *data, size_t len)
1481*86d7f5d3SJohn Marino {
1482*86d7f5d3SJohn Marino     struct buffer *buf = (struct buffer *) callerdat;
1483*86d7f5d3SJohn Marino 
1484*86d7f5d3SJohn Marino     buf_output (buf, data, len);
1485*86d7f5d3SJohn Marino }
1486*86d7f5d3SJohn Marino 
1487*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
1488*86d7f5d3SJohn Marino 
1489*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1490*86d7f5d3SJohn Marino 
1491*86d7f5d3SJohn Marino /* This structure is used to pass information between patch_file and
1492*86d7f5d3SJohn Marino    patch_file_write.  */
1493*86d7f5d3SJohn Marino 
1494*86d7f5d3SJohn Marino struct patch_file_data
1495*86d7f5d3SJohn Marino {
1496*86d7f5d3SJohn Marino     /* File name, for error messages.  */
1497*86d7f5d3SJohn Marino     const char *filename;
1498*86d7f5d3SJohn Marino     /* File to which to write.  */
1499*86d7f5d3SJohn Marino     FILE *fp;
1500*86d7f5d3SJohn Marino     /* Whether to compute the MD5 checksum.  */
1501*86d7f5d3SJohn Marino     int compute_checksum;
1502*86d7f5d3SJohn Marino     /* Data structure for computing the MD5 checksum.  */
1503*86d7f5d3SJohn Marino     struct md5_ctx context;
1504*86d7f5d3SJohn Marino     /* Set if the file has a final newline.  */
1505*86d7f5d3SJohn Marino     int final_nl;
1506*86d7f5d3SJohn Marino };
1507*86d7f5d3SJohn Marino 
1508*86d7f5d3SJohn Marino /* Patch a file.  Runs diff.  This is only done when running as the
1509*86d7f5d3SJohn Marino  * server.  The hope is that the diff will be smaller than the file
1510*86d7f5d3SJohn Marino  * itself.
1511*86d7f5d3SJohn Marino  */
1512*86d7f5d3SJohn Marino static int
patch_file(struct file_info * finfo,Vers_TS * vers_ts,int * docheckout,struct stat * file_info,unsigned char * checksum)1513*86d7f5d3SJohn Marino patch_file (struct file_info *finfo, Vers_TS *vers_ts, int *docheckout,
1514*86d7f5d3SJohn Marino 	    struct stat *file_info, unsigned char *checksum)
1515*86d7f5d3SJohn Marino {
1516*86d7f5d3SJohn Marino     char *backup;
1517*86d7f5d3SJohn Marino     char *file1;
1518*86d7f5d3SJohn Marino     char *file2;
1519*86d7f5d3SJohn Marino     int retval = 0;
1520*86d7f5d3SJohn Marino     int retcode = 0;
1521*86d7f5d3SJohn Marino     int fail;
1522*86d7f5d3SJohn Marino     FILE *e;
1523*86d7f5d3SJohn Marino     struct patch_file_data data;
1524*86d7f5d3SJohn Marino 
1525*86d7f5d3SJohn Marino     *docheckout = 0;
1526*86d7f5d3SJohn Marino 
1527*86d7f5d3SJohn Marino     if (noexec || pipeout || joining ())
1528*86d7f5d3SJohn Marino     {
1529*86d7f5d3SJohn Marino 	*docheckout = 1;
1530*86d7f5d3SJohn Marino 	return 0;
1531*86d7f5d3SJohn Marino     }
1532*86d7f5d3SJohn Marino 
1533*86d7f5d3SJohn Marino     /* If this file has been marked as being binary, then never send a
1534*86d7f5d3SJohn Marino        patch.  */
1535*86d7f5d3SJohn Marino     if (strcmp (vers_ts->options, "-kb") == 0)
1536*86d7f5d3SJohn Marino     {
1537*86d7f5d3SJohn Marino 	*docheckout = 1;
1538*86d7f5d3SJohn Marino 	return 0;
1539*86d7f5d3SJohn Marino     }
1540*86d7f5d3SJohn Marino 
1541*86d7f5d3SJohn Marino     /* First check that the first revision exists.  If it has been nuked
1542*86d7f5d3SJohn Marino        by cvs admin -o, then just fall back to checking out entire
1543*86d7f5d3SJohn Marino        revisions.  In some sense maybe we don't have to do this; after
1544*86d7f5d3SJohn Marino        all cvs.texinfo says "Make sure that no-one has checked out a
1545*86d7f5d3SJohn Marino        copy of the revision you outdate" but then again, that advice
1546*86d7f5d3SJohn Marino        doesn't really make complete sense, because "cvs admin" operates
1547*86d7f5d3SJohn Marino        on a working directory and so _someone_ will almost always have
1548*86d7f5d3SJohn Marino        _some_ revision checked out.  */
1549*86d7f5d3SJohn Marino     {
1550*86d7f5d3SJohn Marino 	char *rev;
1551*86d7f5d3SJohn Marino 
1552*86d7f5d3SJohn Marino 	rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
1553*86d7f5d3SJohn Marino 	if (rev == NULL)
1554*86d7f5d3SJohn Marino 	{
1555*86d7f5d3SJohn Marino 	    *docheckout = 1;
1556*86d7f5d3SJohn Marino 	    return 0;
1557*86d7f5d3SJohn Marino 	}
1558*86d7f5d3SJohn Marino 	else
1559*86d7f5d3SJohn Marino 	    free (rev);
1560*86d7f5d3SJohn Marino     }
1561*86d7f5d3SJohn Marino 
1562*86d7f5d3SJohn Marino     /* If the revision is dead, let checkout_file handle it rather
1563*86d7f5d3SJohn Marino        than duplicating the processing here.  */
1564*86d7f5d3SJohn Marino     if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
1565*86d7f5d3SJohn Marino     {
1566*86d7f5d3SJohn Marino 	*docheckout = 1;
1567*86d7f5d3SJohn Marino 	return 0;
1568*86d7f5d3SJohn Marino     }
1569*86d7f5d3SJohn Marino 
1570*86d7f5d3SJohn Marino     backup = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1571*86d7f5d3SJohn Marino     if (isfile (finfo->file))
1572*86d7f5d3SJohn Marino         rename_file (finfo->file, backup);
1573*86d7f5d3SJohn Marino     else
1574*86d7f5d3SJohn Marino     {
1575*86d7f5d3SJohn Marino 	if (unlink_file (backup) < 0
1576*86d7f5d3SJohn Marino 	    && !existence_error (errno))
1577*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s", backup);
1578*86d7f5d3SJohn Marino     }
1579*86d7f5d3SJohn Marino 
1580*86d7f5d3SJohn Marino     file1 = Xasprintf ("%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
1581*86d7f5d3SJohn Marino     file2 = Xasprintf ("%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
1582*86d7f5d3SJohn Marino 
1583*86d7f5d3SJohn Marino     fail = 0;
1584*86d7f5d3SJohn Marino 
1585*86d7f5d3SJohn Marino     /* We need to check out both revisions first, to see if either one
1586*86d7f5d3SJohn Marino        has a trailing newline.  Because of this, we don't use rcsdiff,
1587*86d7f5d3SJohn Marino        but just use diff.  */
1588*86d7f5d3SJohn Marino 
1589*86d7f5d3SJohn Marino     e = CVS_FOPEN (file1, "w");
1590*86d7f5d3SJohn Marino     if (e == NULL)
1591*86d7f5d3SJohn Marino 	error (1, errno, "cannot open %s", file1);
1592*86d7f5d3SJohn Marino 
1593*86d7f5d3SJohn Marino     data.filename = file1;
1594*86d7f5d3SJohn Marino     data.fp = e;
1595*86d7f5d3SJohn Marino     data.final_nl = 0;
1596*86d7f5d3SJohn Marino     data.compute_checksum = 0;
1597*86d7f5d3SJohn Marino 
1598*86d7f5d3SJohn Marino     /* FIXME - Passing vers_ts->tag here is wrong in the least number
1599*86d7f5d3SJohn Marino      * of cases.  Since we don't know whether vn_user was checked out
1600*86d7f5d3SJohn Marino      * using a tag, we pass vers_ts->tag, which, assuming the user did
1601*86d7f5d3SJohn Marino      * not specify a new TAG to -r, will be the branch we are on.
1602*86d7f5d3SJohn Marino      *
1603*86d7f5d3SJohn Marino      * The only thing it is used for is to substitute in for the Name
1604*86d7f5d3SJohn Marino      * RCS keyword, so in the error case, the patch fails to apply on
1605*86d7f5d3SJohn Marino      * the client end and we end up resending the whole file.
1606*86d7f5d3SJohn Marino      *
1607*86d7f5d3SJohn Marino      * At least, if we are keeping track of the tag vn_user came from,
1608*86d7f5d3SJohn Marino      * I don't know where yet. -DRP
1609*86d7f5d3SJohn Marino      */
1610*86d7f5d3SJohn Marino     retcode = RCS_checkout (vers_ts->srcfile, NULL,
1611*86d7f5d3SJohn Marino 			    vers_ts->vn_user, vers_ts->tag,
1612*86d7f5d3SJohn Marino 			    vers_ts->options, RUN_TTY,
1613*86d7f5d3SJohn Marino 			    patch_file_write, (void *) &data);
1614*86d7f5d3SJohn Marino 
1615*86d7f5d3SJohn Marino     if (fclose (e) < 0)
1616*86d7f5d3SJohn Marino 	error (1, errno, "cannot close %s", file1);
1617*86d7f5d3SJohn Marino 
1618*86d7f5d3SJohn Marino     if (retcode != 0 || ! data.final_nl)
1619*86d7f5d3SJohn Marino 	fail = 1;
1620*86d7f5d3SJohn Marino 
1621*86d7f5d3SJohn Marino     if (! fail)
1622*86d7f5d3SJohn Marino     {
1623*86d7f5d3SJohn Marino 	e = CVS_FOPEN (file2, "w");
1624*86d7f5d3SJohn Marino 	if (e == NULL)
1625*86d7f5d3SJohn Marino 	    error (1, errno, "cannot open %s", file2);
1626*86d7f5d3SJohn Marino 
1627*86d7f5d3SJohn Marino 	data.filename = file2;
1628*86d7f5d3SJohn Marino 	data.fp = e;
1629*86d7f5d3SJohn Marino 	data.final_nl = 0;
1630*86d7f5d3SJohn Marino 	data.compute_checksum = 1;
1631*86d7f5d3SJohn Marino 	md5_init_ctx (&data.context);
1632*86d7f5d3SJohn Marino 
1633*86d7f5d3SJohn Marino 	retcode = RCS_checkout (vers_ts->srcfile, NULL,
1634*86d7f5d3SJohn Marino 				vers_ts->vn_rcs, vers_ts->tag,
1635*86d7f5d3SJohn Marino 				vers_ts->options, RUN_TTY,
1636*86d7f5d3SJohn Marino 				patch_file_write, (void *) &data);
1637*86d7f5d3SJohn Marino 
1638*86d7f5d3SJohn Marino 	if (fclose (e) < 0)
1639*86d7f5d3SJohn Marino 	    error (1, errno, "cannot close %s", file2);
1640*86d7f5d3SJohn Marino 
1641*86d7f5d3SJohn Marino 	if (retcode != 0 || ! data.final_nl)
1642*86d7f5d3SJohn Marino 	    fail = 1;
1643*86d7f5d3SJohn Marino 	else
1644*86d7f5d3SJohn Marino 	    md5_finish_ctx (&data.context, checksum);
1645*86d7f5d3SJohn Marino     }
1646*86d7f5d3SJohn Marino 
1647*86d7f5d3SJohn Marino     retcode = 0;
1648*86d7f5d3SJohn Marino     if (! fail)
1649*86d7f5d3SJohn Marino     {
1650*86d7f5d3SJohn Marino 	int dargc = 0;
1651*86d7f5d3SJohn Marino 	size_t darg_allocated = 0;
1652*86d7f5d3SJohn Marino 	char **dargv = NULL;
1653*86d7f5d3SJohn Marino 
1654*86d7f5d3SJohn Marino 	/* If the client does not support the Rcs-diff command, we
1655*86d7f5d3SJohn Marino            send a context diff, and the client must invoke patch.
1656*86d7f5d3SJohn Marino            That approach was problematical for various reasons.  The
1657*86d7f5d3SJohn Marino            new approach only requires running diff in the server; the
1658*86d7f5d3SJohn Marino            client can handle everything without invoking an external
1659*86d7f5d3SJohn Marino            program.  */
1660*86d7f5d3SJohn Marino 	if (!rcs_diff_patches)
1661*86d7f5d3SJohn Marino 	    /* We use -c, not -u, because that is what CVS has
1662*86d7f5d3SJohn Marino 	       traditionally used.  Kind of a moot point, now that
1663*86d7f5d3SJohn Marino 	       Rcs-diff is preferred, so there is no point in making
1664*86d7f5d3SJohn Marino 	       the compatibility issues worse.  */
1665*86d7f5d3SJohn Marino 	    run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
1666*86d7f5d3SJohn Marino 	else
1667*86d7f5d3SJohn Marino 	    /* Now that diff is librarified, we could be passing -a if
1668*86d7f5d3SJohn Marino 	       we wanted to.  However, it is unclear to me whether we
1669*86d7f5d3SJohn Marino 	       would want to.  Does diff -a, in any significant
1670*86d7f5d3SJohn Marino 	       percentage of cases, produce patches which are smaller
1671*86d7f5d3SJohn Marino 	       than the files it is patching?  I guess maybe text
1672*86d7f5d3SJohn Marino 	       files with character sets which diff regards as
1673*86d7f5d3SJohn Marino 	       'binary'.  Conversely, do they tend to be much larger
1674*86d7f5d3SJohn Marino 	       in the bad cases?  This needs some more
1675*86d7f5d3SJohn Marino 	       thought/investigation, I suspect.  */
1676*86d7f5d3SJohn Marino 	    run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
1677*86d7f5d3SJohn Marino 	retcode = diff_exec (file1, file2, NULL, NULL, dargc, dargv,
1678*86d7f5d3SJohn Marino 			     finfo->file);
1679*86d7f5d3SJohn Marino 	run_arg_free_p (dargc, dargv);
1680*86d7f5d3SJohn Marino 	free (dargv);
1681*86d7f5d3SJohn Marino 
1682*86d7f5d3SJohn Marino 	/* A retcode of 0 means no differences.  1 means some differences.  */
1683*86d7f5d3SJohn Marino 	if (retcode != 0 && retcode != 1)
1684*86d7f5d3SJohn Marino 	    fail = 1;
1685*86d7f5d3SJohn Marino     }
1686*86d7f5d3SJohn Marino 
1687*86d7f5d3SJohn Marino     if (!fail)
1688*86d7f5d3SJohn Marino     {
1689*86d7f5d3SJohn Marino 	struct stat file2_info;
1690*86d7f5d3SJohn Marino 
1691*86d7f5d3SJohn Marino 	/* Check to make sure the patch is really shorter */
1692*86d7f5d3SJohn Marino 	if (stat (file2, &file2_info) < 0)
1693*86d7f5d3SJohn Marino 	    error (1, errno, "could not stat %s", file2);
1694*86d7f5d3SJohn Marino 	if (stat (finfo->file, file_info) < 0)
1695*86d7f5d3SJohn Marino 	    error (1, errno, "could not stat %s", finfo->file);
1696*86d7f5d3SJohn Marino 	if (file2_info.st_size <= file_info->st_size)
1697*86d7f5d3SJohn Marino 	    fail = 1;
1698*86d7f5d3SJohn Marino     }
1699*86d7f5d3SJohn Marino 
1700*86d7f5d3SJohn Marino     if (! fail)
1701*86d7f5d3SJohn Marino     {
1702*86d7f5d3SJohn Marino # define BINARY "Binary"
1703*86d7f5d3SJohn Marino 	char buf[sizeof BINARY];
1704*86d7f5d3SJohn Marino 	unsigned int c;
1705*86d7f5d3SJohn Marino 
1706*86d7f5d3SJohn Marino 	/* Check the diff output to make sure patch will be handle it.  */
1707*86d7f5d3SJohn Marino 	e = CVS_FOPEN (finfo->file, "r");
1708*86d7f5d3SJohn Marino 	if (e == NULL)
1709*86d7f5d3SJohn Marino 	    error (1, errno, "could not open diff output file %s",
1710*86d7f5d3SJohn Marino 		   finfo->fullname);
1711*86d7f5d3SJohn Marino 	c = fread (buf, 1, sizeof BINARY - 1, e);
1712*86d7f5d3SJohn Marino 	buf[c] = '\0';
1713*86d7f5d3SJohn Marino 	if (strcmp (buf, BINARY) == 0)
1714*86d7f5d3SJohn Marino 	{
1715*86d7f5d3SJohn Marino 	    /* These are binary files.  We could use diff -a, but
1716*86d7f5d3SJohn Marino 	       patch can't handle that.  */
1717*86d7f5d3SJohn Marino 	    fail = 1;
1718*86d7f5d3SJohn Marino 	}
1719*86d7f5d3SJohn Marino 	fclose (e);
1720*86d7f5d3SJohn Marino     }
1721*86d7f5d3SJohn Marino 
1722*86d7f5d3SJohn Marino     if (! fail)
1723*86d7f5d3SJohn Marino     {
1724*86d7f5d3SJohn Marino         Vers_TS *xvers_ts;
1725*86d7f5d3SJohn Marino 
1726*86d7f5d3SJohn Marino 	/* Stat the original RCS file, and then adjust it the way
1727*86d7f5d3SJohn Marino 	   that RCS_checkout would.  FIXME: This is an abstraction
1728*86d7f5d3SJohn Marino 	   violation.  */
1729*86d7f5d3SJohn Marino 	if (stat (vers_ts->srcfile->path, file_info) < 0)
1730*86d7f5d3SJohn Marino 	    error (1, errno, "could not stat %s", vers_ts->srcfile->path);
1731*86d7f5d3SJohn Marino 	if (chmod (finfo->file,
1732*86d7f5d3SJohn Marino 		   file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
1733*86d7f5d3SJohn Marino 	    < 0)
1734*86d7f5d3SJohn Marino 	    error (0, errno, "cannot change mode of file %s", finfo->file);
1735*86d7f5d3SJohn Marino 	if (cvswrite
1736*86d7f5d3SJohn Marino 	    && !fileattr_get (finfo->file, "_watched"))
1737*86d7f5d3SJohn Marino 	    xchmod (finfo->file, 1);
1738*86d7f5d3SJohn Marino 
1739*86d7f5d3SJohn Marino         /* This stuff is just copied blindly from checkout_file.  I
1740*86d7f5d3SJohn Marino 	   don't really know what it does.  */
1741*86d7f5d3SJohn Marino         xvers_ts = Version_TS (finfo, options, tag, date,
1742*86d7f5d3SJohn Marino 			       force_tag_match, 0);
1743*86d7f5d3SJohn Marino 	if (strcmp (xvers_ts->options, "-V4") == 0)
1744*86d7f5d3SJohn Marino 	    xvers_ts->options[0] = '\0';
1745*86d7f5d3SJohn Marino 
1746*86d7f5d3SJohn Marino 	Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
1747*86d7f5d3SJohn Marino 		  xvers_ts->ts_user, xvers_ts->options,
1748*86d7f5d3SJohn Marino 		  xvers_ts->tag, xvers_ts->date, NULL);
1749*86d7f5d3SJohn Marino 
1750*86d7f5d3SJohn Marino 	if (stat (finfo->file, file_info) < 0)
1751*86d7f5d3SJohn Marino 	    error (1, errno, "could not stat %s", finfo->file);
1752*86d7f5d3SJohn Marino 
1753*86d7f5d3SJohn Marino 	/* If this is really Update and not Checkout, record history.  */
1754*86d7f5d3SJohn Marino 	if (strcmp (cvs_cmd_name, "update") == 0)
1755*86d7f5d3SJohn Marino 	    history_write ('P', finfo->update_dir, xvers_ts->vn_rcs,
1756*86d7f5d3SJohn Marino 	                   finfo->file, finfo->repository);
1757*86d7f5d3SJohn Marino 
1758*86d7f5d3SJohn Marino 	freevers_ts (&xvers_ts);
1759*86d7f5d3SJohn Marino 
1760*86d7f5d3SJohn Marino 	if (!really_quiet)
1761*86d7f5d3SJohn Marino 	{
1762*86d7f5d3SJohn Marino 	    write_letter (finfo, 'P');
1763*86d7f5d3SJohn Marino 	}
1764*86d7f5d3SJohn Marino     }
1765*86d7f5d3SJohn Marino     else
1766*86d7f5d3SJohn Marino     {
1767*86d7f5d3SJohn Marino 	int old_errno = errno;		/* save errno value over the rename */
1768*86d7f5d3SJohn Marino 
1769*86d7f5d3SJohn Marino 	if (isfile (backup))
1770*86d7f5d3SJohn Marino 	    rename_file (backup, finfo->file);
1771*86d7f5d3SJohn Marino 
1772*86d7f5d3SJohn Marino 	if (retcode != 0 && retcode != 1)
1773*86d7f5d3SJohn Marino 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1774*86d7f5d3SJohn Marino 		   "could not diff %s", finfo->fullname);
1775*86d7f5d3SJohn Marino 
1776*86d7f5d3SJohn Marino 	*docheckout = 1;
1777*86d7f5d3SJohn Marino 	retval = retcode;
1778*86d7f5d3SJohn Marino     }
1779*86d7f5d3SJohn Marino 
1780*86d7f5d3SJohn Marino     if (unlink_file (backup) < 0
1781*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1782*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", backup);
1783*86d7f5d3SJohn Marino     if (unlink_file (file1) < 0
1784*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1785*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", file1);
1786*86d7f5d3SJohn Marino     if (unlink_file (file2) < 0
1787*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1788*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", file2);
1789*86d7f5d3SJohn Marino 
1790*86d7f5d3SJohn Marino     free (backup);
1791*86d7f5d3SJohn Marino     free (file1);
1792*86d7f5d3SJohn Marino     free (file2);
1793*86d7f5d3SJohn Marino     return retval;
1794*86d7f5d3SJohn Marino }
1795*86d7f5d3SJohn Marino 
1796*86d7f5d3SJohn Marino 
1797*86d7f5d3SJohn Marino 
1798*86d7f5d3SJohn Marino /* Write data to a file.  Record whether the last byte written was a
1799*86d7f5d3SJohn Marino    newline.  Optionally compute a checksum.  This is called by
1800*86d7f5d3SJohn Marino    patch_file via RCS_checkout.  */
1801*86d7f5d3SJohn Marino 
1802*86d7f5d3SJohn Marino static void
patch_file_write(void * callerdat,const char * buffer,size_t len)1803*86d7f5d3SJohn Marino patch_file_write (void *callerdat, const char *buffer, size_t len)
1804*86d7f5d3SJohn Marino {
1805*86d7f5d3SJohn Marino     struct patch_file_data *data = (struct patch_file_data *) callerdat;
1806*86d7f5d3SJohn Marino 
1807*86d7f5d3SJohn Marino     if (fwrite (buffer, 1, len, data->fp) != len)
1808*86d7f5d3SJohn Marino 	error (1, errno, "cannot write %s", data->filename);
1809*86d7f5d3SJohn Marino 
1810*86d7f5d3SJohn Marino     data->final_nl = (buffer[len - 1] == '\n');
1811*86d7f5d3SJohn Marino 
1812*86d7f5d3SJohn Marino     if (data->compute_checksum)
1813*86d7f5d3SJohn Marino 	md5_process_bytes (buffer, len, &data->context);
1814*86d7f5d3SJohn Marino }
1815*86d7f5d3SJohn Marino 
1816*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
1817*86d7f5d3SJohn Marino 
1818*86d7f5d3SJohn Marino /*
1819*86d7f5d3SJohn Marino  * Several of the types we process only print a bit of information consisting
1820*86d7f5d3SJohn Marino  * of a single letter and the name.
1821*86d7f5d3SJohn Marino  */
1822*86d7f5d3SJohn Marino void
write_letter(struct file_info * finfo,int letter)1823*86d7f5d3SJohn Marino write_letter (struct file_info *finfo, int letter)
1824*86d7f5d3SJohn Marino {
1825*86d7f5d3SJohn Marino     if (!really_quiet)
1826*86d7f5d3SJohn Marino     {
1827*86d7f5d3SJohn Marino 	char *tag = NULL;
1828*86d7f5d3SJohn Marino 	/* Big enough for "+updated" or any of its ilk.  */
1829*86d7f5d3SJohn Marino 	char buf[80];
1830*86d7f5d3SJohn Marino 
1831*86d7f5d3SJohn Marino 	switch (letter)
1832*86d7f5d3SJohn Marino 	{
1833*86d7f5d3SJohn Marino 	    case 'U':
1834*86d7f5d3SJohn Marino 		tag = "updated";
1835*86d7f5d3SJohn Marino 		break;
1836*86d7f5d3SJohn Marino 	    default:
1837*86d7f5d3SJohn Marino 		/* We don't yet support tagged output except for "U".  */
1838*86d7f5d3SJohn Marino 		break;
1839*86d7f5d3SJohn Marino 	}
1840*86d7f5d3SJohn Marino 
1841*86d7f5d3SJohn Marino 	if (tag != NULL)
1842*86d7f5d3SJohn Marino 	{
1843*86d7f5d3SJohn Marino 	    sprintf (buf, "+%s", tag);
1844*86d7f5d3SJohn Marino 	    cvs_output_tagged (buf, NULL);
1845*86d7f5d3SJohn Marino 	}
1846*86d7f5d3SJohn Marino 	buf[0] = letter;
1847*86d7f5d3SJohn Marino 	buf[1] = ' ';
1848*86d7f5d3SJohn Marino 	buf[2] = '\0';
1849*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", buf);
1850*86d7f5d3SJohn Marino 	cvs_output_tagged ("fname", finfo->fullname);
1851*86d7f5d3SJohn Marino 	cvs_output_tagged ("newline", NULL);
1852*86d7f5d3SJohn Marino 	if (tag != NULL)
1853*86d7f5d3SJohn Marino 	{
1854*86d7f5d3SJohn Marino 	    sprintf (buf, "-%s", tag);
1855*86d7f5d3SJohn Marino 	    cvs_output_tagged (buf, NULL);
1856*86d7f5d3SJohn Marino 	}
1857*86d7f5d3SJohn Marino     }
1858*86d7f5d3SJohn Marino     return;
1859*86d7f5d3SJohn Marino }
1860*86d7f5d3SJohn Marino 
1861*86d7f5d3SJohn Marino 
1862*86d7f5d3SJohn Marino 
1863*86d7f5d3SJohn Marino /* Reregister a file after a merge.  */
1864*86d7f5d3SJohn Marino static void
RegisterMerge(struct file_info * finfo,Vers_TS * vers,const char * backup,int has_conflicts)1865*86d7f5d3SJohn Marino RegisterMerge (struct file_info *finfo, Vers_TS *vers,
1866*86d7f5d3SJohn Marino 	       const char *backup, int has_conflicts)
1867*86d7f5d3SJohn Marino {
1868*86d7f5d3SJohn Marino     /* This file is the result of a merge, which means that it has
1869*86d7f5d3SJohn Marino        been modified.  We use a special timestamp string which will
1870*86d7f5d3SJohn Marino        not compare equal to any actual timestamp.  */
1871*86d7f5d3SJohn Marino     char *cp = NULL;
1872*86d7f5d3SJohn Marino 
1873*86d7f5d3SJohn Marino     if (has_conflicts)
1874*86d7f5d3SJohn Marino     {
1875*86d7f5d3SJohn Marino 	time (&last_register_time);
1876*86d7f5d3SJohn Marino 	cp = time_stamp (finfo->file);
1877*86d7f5d3SJohn Marino     }
1878*86d7f5d3SJohn Marino     Register (finfo->entries, finfo->file, vers->vn_rcs ? vers->vn_rcs : "0",
1879*86d7f5d3SJohn Marino 	      "Result of merge", vers->options, vers->tag, vers->date, cp);
1880*86d7f5d3SJohn Marino     if (cp)
1881*86d7f5d3SJohn Marino 	free (cp);
1882*86d7f5d3SJohn Marino 
1883*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
1884*86d7f5d3SJohn Marino     /* Send the new contents of the file before the message.  If we
1885*86d7f5d3SJohn Marino        wanted to be totally correct, we would have the client write
1886*86d7f5d3SJohn Marino        the message only after the file has safely been written.  */
1887*86d7f5d3SJohn Marino     if (server_active)
1888*86d7f5d3SJohn Marino     {
1889*86d7f5d3SJohn Marino         server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
1890*86d7f5d3SJohn Marino 			  backup);
1891*86d7f5d3SJohn Marino 	server_updated (finfo, vers, SERVER_MERGED, (mode_t) -1, NULL, NULL);
1892*86d7f5d3SJohn Marino     }
1893*86d7f5d3SJohn Marino #endif
1894*86d7f5d3SJohn Marino }
1895*86d7f5d3SJohn Marino 
1896*86d7f5d3SJohn Marino 
1897*86d7f5d3SJohn Marino 
1898*86d7f5d3SJohn Marino /*
1899*86d7f5d3SJohn Marino  * Do all the magic associated with a file which needs to be merged
1900*86d7f5d3SJohn Marino  */
1901*86d7f5d3SJohn Marino static int
merge_file(struct file_info * finfo,Vers_TS * vers)1902*86d7f5d3SJohn Marino merge_file (struct file_info *finfo, Vers_TS *vers)
1903*86d7f5d3SJohn Marino {
1904*86d7f5d3SJohn Marino     char *backup;
1905*86d7f5d3SJohn Marino     int status;
1906*86d7f5d3SJohn Marino     int retval;
1907*86d7f5d3SJohn Marino 
1908*86d7f5d3SJohn Marino     assert (vers->vn_user);
1909*86d7f5d3SJohn Marino 
1910*86d7f5d3SJohn Marino     /*
1911*86d7f5d3SJohn Marino      * The users currently modified file is moved to a backup file name
1912*86d7f5d3SJohn Marino      * ".#filename.version", so that it will stay around for a few days
1913*86d7f5d3SJohn Marino      * before being automatically removed by some cron daemon.  The "version"
1914*86d7f5d3SJohn Marino      * is the version of the file that the user was most up-to-date with
1915*86d7f5d3SJohn Marino      * before the merge.
1916*86d7f5d3SJohn Marino      */
1917*86d7f5d3SJohn Marino     backup = Xasprintf ("%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
1918*86d7f5d3SJohn Marino 
1919*86d7f5d3SJohn Marino     if (unlink_file (backup) && !existence_error (errno))
1920*86d7f5d3SJohn Marino 	error (0, errno, "unable to remove %s", backup);
1921*86d7f5d3SJohn Marino     copy_file (finfo->file, backup);
1922*86d7f5d3SJohn Marino     xchmod (finfo->file, 1);
1923*86d7f5d3SJohn Marino 
1924*86d7f5d3SJohn Marino     if (strcmp (vers->options, "-kb") == 0
1925*86d7f5d3SJohn Marino 	|| wrap_merge_is_copy (finfo->file)
1926*86d7f5d3SJohn Marino 	|| special_file_mismatch (finfo, NULL, vers->vn_rcs))
1927*86d7f5d3SJohn Marino     {
1928*86d7f5d3SJohn Marino 	/* For binary files, a merge is always a conflict.  Same for
1929*86d7f5d3SJohn Marino 	   files whose permissions or linkage do not match.  We give the
1930*86d7f5d3SJohn Marino 	   user the two files, and let them resolve it.  It is possible
1931*86d7f5d3SJohn Marino 	   that we should require a "touch foo" or similar step before
1932*86d7f5d3SJohn Marino 	   we allow a checkin.  */
1933*86d7f5d3SJohn Marino 
1934*86d7f5d3SJohn Marino 	/* TODO: it may not always be necessary to regard a permission
1935*86d7f5d3SJohn Marino 	   mismatch as a conflict.  The working file and the RCS file
1936*86d7f5d3SJohn Marino 	   have a common ancestor `A'; if the working file's permissions
1937*86d7f5d3SJohn Marino 	   match A's, then it's probably safe to overwrite them with the
1938*86d7f5d3SJohn Marino 	   RCS permissions.  Only if the working file, the RCS file, and
1939*86d7f5d3SJohn Marino 	   A all disagree should this be considered a conflict.  But more
1940*86d7f5d3SJohn Marino 	   thought needs to go into this, and in the meantime it is safe
1941*86d7f5d3SJohn Marino 	   to treat any such mismatch as an automatic conflict. -twp */
1942*86d7f5d3SJohn Marino 
1943*86d7f5d3SJohn Marino 	status = RCS_checkout (finfo->rcs, finfo->file, vers->vn_rcs,
1944*86d7f5d3SJohn Marino 			       vers->tag, vers->options, NULL, NULL, NULL);
1945*86d7f5d3SJohn Marino 	if (status)
1946*86d7f5d3SJohn Marino 	{
1947*86d7f5d3SJohn Marino 	    error (0, 0, "failed to check out `%s' file", finfo->fullname);
1948*86d7f5d3SJohn Marino 	    error (0, 0, "restoring `%s' from backup file `%s'",
1949*86d7f5d3SJohn Marino 		   finfo->fullname, backup);
1950*86d7f5d3SJohn Marino 	    rename_file (backup, finfo->file);
1951*86d7f5d3SJohn Marino 	    retval = 1;
1952*86d7f5d3SJohn Marino 	    goto out;
1953*86d7f5d3SJohn Marino 	}
1954*86d7f5d3SJohn Marino 
1955*86d7f5d3SJohn Marino 	xchmod (finfo->file, 1);
1956*86d7f5d3SJohn Marino 
1957*86d7f5d3SJohn Marino 	RegisterMerge (finfo, vers, backup, 1);
1958*86d7f5d3SJohn Marino 
1959*86d7f5d3SJohn Marino 	/* Is there a better term than "nonmergeable file"?  What we
1960*86d7f5d3SJohn Marino 	   really mean is, not something that CVS cannot or does not
1961*86d7f5d3SJohn Marino 	   want to merge (there might be an external manual or
1962*86d7f5d3SJohn Marino 	   automatic merge process).  */
1963*86d7f5d3SJohn Marino 	error (0, 0, "nonmergeable file needs merge");
1964*86d7f5d3SJohn Marino 	error (0, 0, "revision %s from repository is now in %s",
1965*86d7f5d3SJohn Marino 	       vers->vn_rcs, finfo->fullname);
1966*86d7f5d3SJohn Marino 	error (0, 0, "file from working directory is now in %s", backup);
1967*86d7f5d3SJohn Marino 	write_letter (finfo, 'C');
1968*86d7f5d3SJohn Marino 
1969*86d7f5d3SJohn Marino 	history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
1970*86d7f5d3SJohn Marino 		       finfo->repository);
1971*86d7f5d3SJohn Marino 	retval = 0;
1972*86d7f5d3SJohn Marino 	goto out;
1973*86d7f5d3SJohn Marino     }
1974*86d7f5d3SJohn Marino 
1975*86d7f5d3SJohn Marino     status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
1976*86d7f5d3SJohn Marino 		        vers->options, vers->vn_user, vers->vn_rcs);
1977*86d7f5d3SJohn Marino     if (status != 0 && status != 1)
1978*86d7f5d3SJohn Marino     {
1979*86d7f5d3SJohn Marino 	error (0, status == -1 ? errno : 0,
1980*86d7f5d3SJohn Marino 	       "could not merge revision %s of %s", vers->vn_user, finfo->fullname);
1981*86d7f5d3SJohn Marino 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1982*86d7f5d3SJohn Marino 	       finfo->fullname, backup);
1983*86d7f5d3SJohn Marino 	rename_file (backup, finfo->file);
1984*86d7f5d3SJohn Marino 	retval = 1;
1985*86d7f5d3SJohn Marino 	goto out;
1986*86d7f5d3SJohn Marino     }
1987*86d7f5d3SJohn Marino 
1988*86d7f5d3SJohn Marino     if (strcmp (vers->options, "-V4") == 0)
1989*86d7f5d3SJohn Marino 	vers->options[0] = '\0';
1990*86d7f5d3SJohn Marino 
1991*86d7f5d3SJohn Marino     /* fix up the vers structure, in case it is used by join */
1992*86d7f5d3SJohn Marino     if (join_rev1)
1993*86d7f5d3SJohn Marino     {
1994*86d7f5d3SJohn Marino 	/* FIXME: Throwing away the original revision info is almost
1995*86d7f5d3SJohn Marino 	   certainly wrong -- what if join_rev1 is "BASE"?  */
1996*86d7f5d3SJohn Marino 	if (vers->vn_user != NULL)
1997*86d7f5d3SJohn Marino 	    free (vers->vn_user);
1998*86d7f5d3SJohn Marino 	vers->vn_user = xstrdup (vers->vn_rcs);
1999*86d7f5d3SJohn Marino     }
2000*86d7f5d3SJohn Marino 
2001*86d7f5d3SJohn Marino     RegisterMerge (finfo, vers, backup, status);
2002*86d7f5d3SJohn Marino 
2003*86d7f5d3SJohn Marino     if (status == 1)
2004*86d7f5d3SJohn Marino     {
2005*86d7f5d3SJohn Marino 	error (0, 0, "conflicts found in %s", finfo->fullname);
2006*86d7f5d3SJohn Marino 
2007*86d7f5d3SJohn Marino 	write_letter (finfo, 'C');
2008*86d7f5d3SJohn Marino 
2009*86d7f5d3SJohn Marino 	history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
2010*86d7f5d3SJohn Marino 	               finfo->repository);
2011*86d7f5d3SJohn Marino 
2012*86d7f5d3SJohn Marino     }
2013*86d7f5d3SJohn Marino     else /* status == 0 */
2014*86d7f5d3SJohn Marino     {
2015*86d7f5d3SJohn Marino 	history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
2016*86d7f5d3SJohn Marino 		       finfo->repository);
2017*86d7f5d3SJohn Marino 
2018*86d7f5d3SJohn Marino 	/* FIXME: the noexec case is broken.  RCS_merge could be doing the
2019*86d7f5d3SJohn Marino 	   xcmp on the temporary files without much hassle, I think.  */
2020*86d7f5d3SJohn Marino 	if (!noexec && !xcmp (backup, finfo->file))
2021*86d7f5d3SJohn Marino 	{
2022*86d7f5d3SJohn Marino 	    cvs_output (finfo->fullname, 0);
2023*86d7f5d3SJohn Marino 	    cvs_output (" already contains the differences between ", 0);
2024*86d7f5d3SJohn Marino 	    cvs_output (vers->vn_user, 0);
2025*86d7f5d3SJohn Marino 	    cvs_output (" and ", 0);
2026*86d7f5d3SJohn Marino 	    cvs_output (vers->vn_rcs, 0);
2027*86d7f5d3SJohn Marino 	    cvs_output ("\n", 1);
2028*86d7f5d3SJohn Marino 
2029*86d7f5d3SJohn Marino 	    retval = 0;
2030*86d7f5d3SJohn Marino 	    goto out;
2031*86d7f5d3SJohn Marino 	}
2032*86d7f5d3SJohn Marino 
2033*86d7f5d3SJohn Marino 	write_letter (finfo, 'M');
2034*86d7f5d3SJohn Marino     }
2035*86d7f5d3SJohn Marino     retval = 0;
2036*86d7f5d3SJohn Marino  out:
2037*86d7f5d3SJohn Marino     free (backup);
2038*86d7f5d3SJohn Marino     return retval;
2039*86d7f5d3SJohn Marino }
2040*86d7f5d3SJohn Marino 
2041*86d7f5d3SJohn Marino 
2042*86d7f5d3SJohn Marino 
2043*86d7f5d3SJohn Marino /*
2044*86d7f5d3SJohn Marino  * Do all the magic associated with a file which needs to be joined
2045*86d7f5d3SJohn Marino  * (reached via the -j option to checkout or update).
2046*86d7f5d3SJohn Marino  *
2047*86d7f5d3SJohn Marino  * INPUTS
2048*86d7f5d3SJohn Marino  *   finfo		File information about the destination file.
2049*86d7f5d3SJohn Marino  *   vers		The Vers_TS structure for finfo.
2050*86d7f5d3SJohn Marino  *
2051*86d7f5d3SJohn Marino  * GLOBALS
2052*86d7f5d3SJohn Marino  *   join_rev1		From the command line.
2053*86d7f5d3SJohn Marino  *   join_rev2		From the command line.
2054*86d7f5d3SJohn Marino  *   server_active	Natch.
2055*86d7f5d3SJohn Marino  *
2056*86d7f5d3SJohn Marino  * ASSUMPTIONS
2057*86d7f5d3SJohn Marino  *   1.  Is not called in client mode.
2058*86d7f5d3SJohn Marino  */
2059*86d7f5d3SJohn Marino static void
join_file(struct file_info * finfo,Vers_TS * vers)2060*86d7f5d3SJohn Marino join_file (struct file_info *finfo, Vers_TS *vers)
2061*86d7f5d3SJohn Marino {
2062*86d7f5d3SJohn Marino     char *backup;
2063*86d7f5d3SJohn Marino     char *t_options;
2064*86d7f5d3SJohn Marino     int status;
2065*86d7f5d3SJohn Marino 
2066*86d7f5d3SJohn Marino     char *rev1;
2067*86d7f5d3SJohn Marino     char *rev2;
2068*86d7f5d3SJohn Marino     char *jrev1;
2069*86d7f5d3SJohn Marino     char *jrev2;
2070*86d7f5d3SJohn Marino     char *jdate1;
2071*86d7f5d3SJohn Marino     char *jdate2;
2072*86d7f5d3SJohn Marino 
2073*86d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "join_file(%s, %s%s%s%s, %s, %s)",
2074*86d7f5d3SJohn Marino 	   finfo->file,
2075*86d7f5d3SJohn Marino 	   vers->tag ? vers->tag : "",
2076*86d7f5d3SJohn Marino 	   vers->tag ? " (" : "",
2077*86d7f5d3SJohn Marino 	   vers->vn_rcs ? vers->vn_rcs : "",
2078*86d7f5d3SJohn Marino 	   vers->tag ? ")" : "",
2079*86d7f5d3SJohn Marino 	   join_rev1 ? join_rev1 : "",
2080*86d7f5d3SJohn Marino 	   join_rev2 ? join_rev2 : "");
2081*86d7f5d3SJohn Marino 
2082*86d7f5d3SJohn Marino     jrev1 = join_rev1;
2083*86d7f5d3SJohn Marino     jrev2 = join_rev2;
2084*86d7f5d3SJohn Marino     jdate1 = join_date1;
2085*86d7f5d3SJohn Marino     jdate2 = join_date2;
2086*86d7f5d3SJohn Marino 
2087*86d7f5d3SJohn Marino     /* Determine if we need to do anything at all.  */
2088*86d7f5d3SJohn Marino     if (vers->srcfile == NULL ||
2089*86d7f5d3SJohn Marino 	vers->srcfile->path == NULL)
2090*86d7f5d3SJohn Marino     {
2091*86d7f5d3SJohn Marino 	return;
2092*86d7f5d3SJohn Marino     }
2093*86d7f5d3SJohn Marino 
2094*86d7f5d3SJohn Marino     /* If only one join revision is specified, it becomes the second
2095*86d7f5d3SJohn Marino        revision.  */
2096*86d7f5d3SJohn Marino     if (jrev2 == NULL)
2097*86d7f5d3SJohn Marino     {
2098*86d7f5d3SJohn Marino 	jrev2 = jrev1;
2099*86d7f5d3SJohn Marino 	jrev1 = NULL;
2100*86d7f5d3SJohn Marino 	jdate2 = jdate1;
2101*86d7f5d3SJohn Marino 	jdate1 = NULL;
2102*86d7f5d3SJohn Marino     }
2103*86d7f5d3SJohn Marino 
2104*86d7f5d3SJohn Marino     /* FIXME: Need to handle "BASE" for jrev1 and/or jrev2.  Note caveat
2105*86d7f5d3SJohn Marino        below about vn_user.  */
2106*86d7f5d3SJohn Marino 
2107*86d7f5d3SJohn Marino     /* Convert the second revision, walking branches and dates.  */
2108*86d7f5d3SJohn Marino     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, NULL);
2109*86d7f5d3SJohn Marino 
2110*86d7f5d3SJohn Marino     /* If this is a merge of two revisions, get the first revision.
2111*86d7f5d3SJohn Marino        If only one join tag was specified, then the first revision is
2112*86d7f5d3SJohn Marino        the greatest common ancestor of the second revision and the
2113*86d7f5d3SJohn Marino        working file.  */
2114*86d7f5d3SJohn Marino     if (jrev1 != NULL)
2115*86d7f5d3SJohn Marino 	rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, NULL);
2116*86d7f5d3SJohn Marino     else
2117*86d7f5d3SJohn Marino     {
2118*86d7f5d3SJohn Marino 	/* Note that we use vn_rcs here, since vn_user may contain a
2119*86d7f5d3SJohn Marino            special string such as "-nn".  */
2120*86d7f5d3SJohn Marino 	if (vers->vn_rcs == NULL)
2121*86d7f5d3SJohn Marino 	    rev1 = NULL;
2122*86d7f5d3SJohn Marino 	else if (rev2 == NULL)
2123*86d7f5d3SJohn Marino 	{
2124*86d7f5d3SJohn Marino 	    /* This means that the file never existed on the branch.
2125*86d7f5d3SJohn Marino                It does not mean that the file was removed on the
2126*86d7f5d3SJohn Marino                branch: that case is represented by a dead rev2.  If
2127*86d7f5d3SJohn Marino                the file never existed on the branch, then we have
2128*86d7f5d3SJohn Marino                nothing to merge, so we just return.  */
2129*86d7f5d3SJohn Marino 	    return;
2130*86d7f5d3SJohn Marino 	}
2131*86d7f5d3SJohn Marino 	else
2132*86d7f5d3SJohn Marino 	    rev1 = gca (vers->vn_rcs, rev2);
2133*86d7f5d3SJohn Marino     }
2134*86d7f5d3SJohn Marino 
2135*86d7f5d3SJohn Marino     /* Handle a nonexistent or dead merge target.  */
2136*86d7f5d3SJohn Marino     if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
2137*86d7f5d3SJohn Marino     {
2138*86d7f5d3SJohn Marino 	char *mrev;
2139*86d7f5d3SJohn Marino 
2140*86d7f5d3SJohn Marino 	if (rev2 != NULL)
2141*86d7f5d3SJohn Marino 	    free (rev2);
2142*86d7f5d3SJohn Marino 
2143*86d7f5d3SJohn Marino 	/* If the first revision doesn't exist either, then there is
2144*86d7f5d3SJohn Marino            no change between the two revisions, so we don't do
2145*86d7f5d3SJohn Marino            anything.  */
2146*86d7f5d3SJohn Marino 	if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2147*86d7f5d3SJohn Marino 	{
2148*86d7f5d3SJohn Marino 	    if (rev1 != NULL)
2149*86d7f5d3SJohn Marino 		free (rev1);
2150*86d7f5d3SJohn Marino 	    return;
2151*86d7f5d3SJohn Marino 	}
2152*86d7f5d3SJohn Marino 
2153*86d7f5d3SJohn Marino 	/* If we are merging two revisions, then the file was removed
2154*86d7f5d3SJohn Marino 	   between the first revision and the second one.  In this
2155*86d7f5d3SJohn Marino 	   case we want to mark the file for removal.
2156*86d7f5d3SJohn Marino 
2157*86d7f5d3SJohn Marino 	   If we are merging one revision, then the file has been
2158*86d7f5d3SJohn Marino 	   removed between the greatest common ancestor and the merge
2159*86d7f5d3SJohn Marino 	   revision.  From the perspective of the branch on to which
2160*86d7f5d3SJohn Marino 	   we ar emerging, which may be the trunk, either 1) the file
2161*86d7f5d3SJohn Marino 	   does not currently exist on the target, or 2) the file has
2162*86d7f5d3SJohn Marino 	   not been modified on the target branch since the greatest
2163*86d7f5d3SJohn Marino 	   common ancestor, or 3) the file has been modified on the
2164*86d7f5d3SJohn Marino 	   target branch since the greatest common ancestor.  In case
2165*86d7f5d3SJohn Marino 	   1 there is nothing to do.  In case 2 we mark the file for
2166*86d7f5d3SJohn Marino 	   removal.  In case 3 we have a conflict.
2167*86d7f5d3SJohn Marino 
2168*86d7f5d3SJohn Marino 	   Note that the handling is slightly different depending upon
2169*86d7f5d3SJohn Marino 	   whether one or two join targets were specified.  If two
2170*86d7f5d3SJohn Marino 	   join targets were specified, we don't check whether the
2171*86d7f5d3SJohn Marino 	   file was modified since a given point.  My reasoning is
2172*86d7f5d3SJohn Marino 	   that if you ask for an explicit merge between two tags,
2173*86d7f5d3SJohn Marino 	   then you want to merge in whatever was changed between
2174*86d7f5d3SJohn Marino 	   those two tags.  If a file was removed between the two
2175*86d7f5d3SJohn Marino 	   tags, then you want it to be removed.  However, if you ask
2176*86d7f5d3SJohn Marino 	   for a merge of a branch, then you want to merge in all
2177*86d7f5d3SJohn Marino 	   changes which were made on the branch.  If a file was
2178*86d7f5d3SJohn Marino 	   removed on the branch, that is a change to the file.  If
2179*86d7f5d3SJohn Marino 	   the file was also changed on the main line, then that is
2180*86d7f5d3SJohn Marino 	   also a change.  These two changes--the file removal and the
2181*86d7f5d3SJohn Marino 	   modification--must be merged.  This is a conflict.  */
2182*86d7f5d3SJohn Marino 
2183*86d7f5d3SJohn Marino 	/* If the user file is dead, or does not exist, or has been
2184*86d7f5d3SJohn Marino            marked for removal, then there is nothing to do.  */
2185*86d7f5d3SJohn Marino 	if (vers->vn_user == NULL
2186*86d7f5d3SJohn Marino 	    || vers->vn_user[0] == '-'
2187*86d7f5d3SJohn Marino 	    || RCS_isdead (vers->srcfile, vers->vn_user))
2188*86d7f5d3SJohn Marino 	{
2189*86d7f5d3SJohn Marino 	    if (rev1 != NULL)
2190*86d7f5d3SJohn Marino 		free (rev1);
2191*86d7f5d3SJohn Marino 	    return;
2192*86d7f5d3SJohn Marino 	}
2193*86d7f5d3SJohn Marino 
2194*86d7f5d3SJohn Marino 	/* If the user file has been marked for addition, or has been
2195*86d7f5d3SJohn Marino 	   locally modified, then we have a conflict which we can not
2196*86d7f5d3SJohn Marino 	   resolve.  No_Difference will already have been called in
2197*86d7f5d3SJohn Marino 	   this case, so comparing the timestamps is sufficient to
2198*86d7f5d3SJohn Marino 	   determine whether the file is locally modified.  */
2199*86d7f5d3SJohn Marino 	if (strcmp (vers->vn_user, "0") == 0
2200*86d7f5d3SJohn Marino 	    || (vers->ts_user != NULL
2201*86d7f5d3SJohn Marino 		&& strcmp (vers->ts_user, vers->ts_rcs) != 0))
2202*86d7f5d3SJohn Marino 	{
2203*86d7f5d3SJohn Marino 	    if (jdate2 != NULL)
2204*86d7f5d3SJohn Marino 		error (0, 0,
2205*86d7f5d3SJohn Marino 		       "file %s is locally modified, but has been removed in revision %s as of %s",
2206*86d7f5d3SJohn Marino 		       finfo->fullname, jrev2, jdate2);
2207*86d7f5d3SJohn Marino 	    else
2208*86d7f5d3SJohn Marino 		error (0, 0,
2209*86d7f5d3SJohn Marino 		       "file %s is locally modified, but has been removed in revision %s",
2210*86d7f5d3SJohn Marino 		       finfo->fullname, jrev2);
2211*86d7f5d3SJohn Marino 
2212*86d7f5d3SJohn Marino 	    /* FIXME: Should we arrange to return a non-zero exit
2213*86d7f5d3SJohn Marino                status?  */
2214*86d7f5d3SJohn Marino 
2215*86d7f5d3SJohn Marino 	    if (rev1 != NULL)
2216*86d7f5d3SJohn Marino 		free (rev1);
2217*86d7f5d3SJohn Marino 
2218*86d7f5d3SJohn Marino 	    return;
2219*86d7f5d3SJohn Marino 	}
2220*86d7f5d3SJohn Marino 
2221*86d7f5d3SJohn Marino 	/* If only one join tag was specified, and the user file has
2222*86d7f5d3SJohn Marino            been changed since the greatest common ancestor (rev1),
2223*86d7f5d3SJohn Marino            then there is a conflict we can not resolve.  See above for
2224*86d7f5d3SJohn Marino            the rationale.  */
2225*86d7f5d3SJohn Marino 	if (join_rev2 == NULL
2226*86d7f5d3SJohn Marino 	    && strcmp (rev1, vers->vn_user) != 0)
2227*86d7f5d3SJohn Marino 	{
2228*86d7f5d3SJohn Marino 	    if (jdate2 != NULL)
2229*86d7f5d3SJohn Marino 		error (0, 0,
2230*86d7f5d3SJohn Marino 		       "file %s has been modified, but has been removed in revision %s as of %s",
2231*86d7f5d3SJohn Marino 		       finfo->fullname, jrev2, jdate2);
2232*86d7f5d3SJohn Marino 	    else
2233*86d7f5d3SJohn Marino 		error (0, 0,
2234*86d7f5d3SJohn Marino 		       "file %s has been modified, but has been removed in revision %s",
2235*86d7f5d3SJohn Marino 		       finfo->fullname, jrev2);
2236*86d7f5d3SJohn Marino 
2237*86d7f5d3SJohn Marino 	    /* FIXME: Should we arrange to return a non-zero exit
2238*86d7f5d3SJohn Marino                status?  */
2239*86d7f5d3SJohn Marino 
2240*86d7f5d3SJohn Marino 	    if (rev1 != NULL)
2241*86d7f5d3SJohn Marino 		free (rev1);
2242*86d7f5d3SJohn Marino 
2243*86d7f5d3SJohn Marino 	    return;
2244*86d7f5d3SJohn Marino 	}
2245*86d7f5d3SJohn Marino 
2246*86d7f5d3SJohn Marino 	if (rev1 != NULL)
2247*86d7f5d3SJohn Marino 	    free (rev1);
2248*86d7f5d3SJohn Marino 
2249*86d7f5d3SJohn Marino 	/* The user file exists and has not been modified.  Mark it
2250*86d7f5d3SJohn Marino            for removal.  FIXME: If we are doing a checkout, this has
2251*86d7f5d3SJohn Marino            the effect of first checking out the file, and then
2252*86d7f5d3SJohn Marino            removing it.  It would be better to just register the
2253*86d7f5d3SJohn Marino            removal.
2254*86d7f5d3SJohn Marino 
2255*86d7f5d3SJohn Marino 	   The same goes for a removal then an add.  e.g.
2256*86d7f5d3SJohn Marino 	   cvs up -rbr -jbr2 could remove and readd the same file
2257*86d7f5d3SJohn Marino 	 */
2258*86d7f5d3SJohn Marino 	/* save the rev since server_updated might invalidate it */
2259*86d7f5d3SJohn Marino 	mrev = Xasprintf ("-%s", vers->vn_user);
2260*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
2261*86d7f5d3SJohn Marino 	if (server_active)
2262*86d7f5d3SJohn Marino 	{
2263*86d7f5d3SJohn Marino 	    server_scratch (finfo->file);
2264*86d7f5d3SJohn Marino 	    server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
2265*86d7f5d3SJohn Marino 			    NULL, NULL);
2266*86d7f5d3SJohn Marino 	}
2267*86d7f5d3SJohn Marino #endif
2268*86d7f5d3SJohn Marino 	Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
2269*86d7f5d3SJohn Marino 		  vers->options, vers->tag, vers->date, vers->ts_conflict);
2270*86d7f5d3SJohn Marino 	free (mrev);
2271*86d7f5d3SJohn Marino 	/* We need to check existence_error here because if we are
2272*86d7f5d3SJohn Marino            running as the server, and the file is up to date in the
2273*86d7f5d3SJohn Marino            working directory, the client will not have sent us a copy.  */
2274*86d7f5d3SJohn Marino 	if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
2275*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove file %s", finfo->fullname);
2276*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
2277*86d7f5d3SJohn Marino 	if (server_active)
2278*86d7f5d3SJohn Marino 	    server_checked_in (finfo->file, finfo->update_dir,
2279*86d7f5d3SJohn Marino 			       finfo->repository);
2280*86d7f5d3SJohn Marino #endif
2281*86d7f5d3SJohn Marino 	if (! really_quiet)
2282*86d7f5d3SJohn Marino 	    error (0, 0, "scheduling `%s' for removal", finfo->fullname);
2283*86d7f5d3SJohn Marino 
2284*86d7f5d3SJohn Marino 	return;
2285*86d7f5d3SJohn Marino     }
2286*86d7f5d3SJohn Marino 
2287*86d7f5d3SJohn Marino     /* If the two merge revisions are the same, then there is nothing
2288*86d7f5d3SJohn Marino      * to do.  This needs to be checked before the rev2 == up-to-date base
2289*86d7f5d3SJohn Marino      * revision check tha comes next.  Otherwise, rev1 can == rev2 and get an
2290*86d7f5d3SJohn Marino      * "already contains the changes between <rev1> and <rev1>" message.
2291*86d7f5d3SJohn Marino      */
2292*86d7f5d3SJohn Marino     if (rev1 && strcmp (rev1, rev2) == 0)
2293*86d7f5d3SJohn Marino     {
2294*86d7f5d3SJohn Marino 	free (rev1);
2295*86d7f5d3SJohn Marino 	free (rev2);
2296*86d7f5d3SJohn Marino 	return;
2297*86d7f5d3SJohn Marino     }
2298*86d7f5d3SJohn Marino 
2299*86d7f5d3SJohn Marino     /* If we know that the user file is up-to-date, then it becomes an
2300*86d7f5d3SJohn Marino      * optimization to skip the merge when rev2 is the same as the base
2301*86d7f5d3SJohn Marino      * revision.  i.e. we know that diff3(file2,file1,file2) will produce
2302*86d7f5d3SJohn Marino      * file2.
2303*86d7f5d3SJohn Marino      */
2304*86d7f5d3SJohn Marino     if (vers->vn_user != NULL && vers->ts_user != NULL
2305*86d7f5d3SJohn Marino         && strcmp (vers->ts_user, vers->ts_rcs) == 0
2306*86d7f5d3SJohn Marino         && strcmp (rev2, vers->vn_user) == 0)
2307*86d7f5d3SJohn Marino     {
2308*86d7f5d3SJohn Marino 	if (!really_quiet)
2309*86d7f5d3SJohn Marino 	{
2310*86d7f5d3SJohn Marino 	    cvs_output (finfo->fullname, 0);
2311*86d7f5d3SJohn Marino 	    cvs_output (" already contains the differences between ", 0);
2312*86d7f5d3SJohn Marino 	    cvs_output (rev1 ? rev1 : "creation", 0);
2313*86d7f5d3SJohn Marino 	    cvs_output (" and ", 0);
2314*86d7f5d3SJohn Marino 	    cvs_output (rev2, 0);
2315*86d7f5d3SJohn Marino 	    cvs_output ("\n", 1);
2316*86d7f5d3SJohn Marino 	}
2317*86d7f5d3SJohn Marino 
2318*86d7f5d3SJohn Marino 	if (rev1 != NULL)
2319*86d7f5d3SJohn Marino 	    free (rev1);
2320*86d7f5d3SJohn Marino 	free (rev2);
2321*86d7f5d3SJohn Marino 
2322*86d7f5d3SJohn Marino 	return;
2323*86d7f5d3SJohn Marino     }
2324*86d7f5d3SJohn Marino 
2325*86d7f5d3SJohn Marino     /* If rev1 is dead or does not exist, then the file was added
2326*86d7f5d3SJohn Marino        between rev1 and rev2.  */
2327*86d7f5d3SJohn Marino     if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2328*86d7f5d3SJohn Marino     {
2329*86d7f5d3SJohn Marino 	if (rev1 != NULL)
2330*86d7f5d3SJohn Marino 	    free (rev1);
2331*86d7f5d3SJohn Marino 	free (rev2);
2332*86d7f5d3SJohn Marino 
2333*86d7f5d3SJohn Marino 	/* If the file does not exist in the working directory, then
2334*86d7f5d3SJohn Marino            we can just check out the new revision and mark it for
2335*86d7f5d3SJohn Marino            addition.  */
2336*86d7f5d3SJohn Marino 	if (vers->vn_user == NULL)
2337*86d7f5d3SJohn Marino 	{
2338*86d7f5d3SJohn Marino 	    char *saved_options = options;
2339*86d7f5d3SJohn Marino 	    Vers_TS *xvers;
2340*86d7f5d3SJohn Marino 
2341*86d7f5d3SJohn Marino 	    xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
2342*86d7f5d3SJohn Marino 
2343*86d7f5d3SJohn Marino 	    /* Reset any keyword expansion option.  Otherwise, when a
2344*86d7f5d3SJohn Marino 	       command like `cvs update -kk -jT1 -jT2' creates a new file
2345*86d7f5d3SJohn Marino 	       (because a file had the T2 tag, but not T1), the subsequent
2346*86d7f5d3SJohn Marino 	       commit of that just-added file effectively would set the
2347*86d7f5d3SJohn Marino 	       admin `-kk' option for that file in the repository.  */
2348*86d7f5d3SJohn Marino 	    options = NULL;
2349*86d7f5d3SJohn Marino 
2350*86d7f5d3SJohn Marino 	    /* FIXME: If checkout_file fails, we should arrange to
2351*86d7f5d3SJohn Marino                return a non-zero exit status.  */
2352*86d7f5d3SJohn Marino 	    status = checkout_file (finfo, xvers, 1, 0, 1);
2353*86d7f5d3SJohn Marino 	    options = saved_options;
2354*86d7f5d3SJohn Marino 
2355*86d7f5d3SJohn Marino 	    freevers_ts (&xvers);
2356*86d7f5d3SJohn Marino 
2357*86d7f5d3SJohn Marino 	    return;
2358*86d7f5d3SJohn Marino 	}
2359*86d7f5d3SJohn Marino 
2360*86d7f5d3SJohn Marino 	/* The file currently exists in the working directory, so we
2361*86d7f5d3SJohn Marino            have a conflict which we can not resolve.  Note that this
2362*86d7f5d3SJohn Marino            is true even if the file is marked for addition or removal.  */
2363*86d7f5d3SJohn Marino 
2364*86d7f5d3SJohn Marino 	if (jdate2 != NULL)
2365*86d7f5d3SJohn Marino 	    error (0, 0,
2366*86d7f5d3SJohn Marino 		   "file %s exists, but has been added in revision %s as of %s",
2367*86d7f5d3SJohn Marino 		   finfo->fullname, jrev2, jdate2);
2368*86d7f5d3SJohn Marino 	else
2369*86d7f5d3SJohn Marino 	    error (0, 0,
2370*86d7f5d3SJohn Marino 		   "file %s exists, but has been added in revision %s",
2371*86d7f5d3SJohn Marino 		   finfo->fullname, jrev2);
2372*86d7f5d3SJohn Marino 
2373*86d7f5d3SJohn Marino 	return;
2374*86d7f5d3SJohn Marino     }
2375*86d7f5d3SJohn Marino 
2376*86d7f5d3SJohn Marino     /* If there is no working file, then we can't do the merge.  */
2377*86d7f5d3SJohn Marino     if (vers->vn_user == NULL || vers->vn_user[0] == '-')
2378*86d7f5d3SJohn Marino     {
2379*86d7f5d3SJohn Marino 	free (rev1);
2380*86d7f5d3SJohn Marino 	free (rev2);
2381*86d7f5d3SJohn Marino 
2382*86d7f5d3SJohn Marino 	if (jdate2 != NULL)
2383*86d7f5d3SJohn Marino 	    error (0, 0,
2384*86d7f5d3SJohn Marino 		   "file %s does not exist, but is present in revision %s as of %s",
2385*86d7f5d3SJohn Marino 		   finfo->fullname, jrev2, jdate2);
2386*86d7f5d3SJohn Marino 	else
2387*86d7f5d3SJohn Marino 	    error (0, 0,
2388*86d7f5d3SJohn Marino 		   "file %s does not exist, but is present in revision %s",
2389*86d7f5d3SJohn Marino 		   finfo->fullname, jrev2);
2390*86d7f5d3SJohn Marino 
2391*86d7f5d3SJohn Marino 	/* FIXME: Should we arrange to return a non-zero exit status?  */
2392*86d7f5d3SJohn Marino 
2393*86d7f5d3SJohn Marino 	return;
2394*86d7f5d3SJohn Marino     }
2395*86d7f5d3SJohn Marino 
2396*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
2397*86d7f5d3SJohn Marino     if (server_active && !isreadable (finfo->file))
2398*86d7f5d3SJohn Marino     {
2399*86d7f5d3SJohn Marino 	int retcode;
2400*86d7f5d3SJohn Marino 	/* The file is up to date.  Need to check out the current contents.  */
2401*86d7f5d3SJohn Marino 	/* FIXME - see the FIXME comment above the call to RCS_checkout in the
2402*86d7f5d3SJohn Marino 	 * patch_file function.
2403*86d7f5d3SJohn Marino 	 */
2404*86d7f5d3SJohn Marino 	retcode = RCS_checkout (vers->srcfile, finfo->file,
2405*86d7f5d3SJohn Marino 				vers->vn_user, vers->tag,
2406*86d7f5d3SJohn Marino 				NULL, RUN_TTY, NULL, NULL);
2407*86d7f5d3SJohn Marino 	if (retcode != 0)
2408*86d7f5d3SJohn Marino 	    error (1, 0,
2409*86d7f5d3SJohn Marino 		   "failed to check out %s file", finfo->fullname);
2410*86d7f5d3SJohn Marino     }
2411*86d7f5d3SJohn Marino #endif
2412*86d7f5d3SJohn Marino 
2413*86d7f5d3SJohn Marino     /*
2414*86d7f5d3SJohn Marino      * The users currently modified file is moved to a backup file name
2415*86d7f5d3SJohn Marino      * ".#filename.version", so that it will stay around for a few days
2416*86d7f5d3SJohn Marino      * before being automatically removed by some cron daemon.  The "version"
2417*86d7f5d3SJohn Marino      * is the version of the file that the user was most up-to-date with
2418*86d7f5d3SJohn Marino      * before the merge.
2419*86d7f5d3SJohn Marino      */
2420*86d7f5d3SJohn Marino     backup = Xasprintf ("%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
2421*86d7f5d3SJohn Marino 
2422*86d7f5d3SJohn Marino     if (unlink_file (backup) < 0
2423*86d7f5d3SJohn Marino 	&& !existence_error (errno))
2424*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", backup);
2425*86d7f5d3SJohn Marino     copy_file (finfo->file, backup);
2426*86d7f5d3SJohn Marino     xchmod (finfo->file, 1);
2427*86d7f5d3SJohn Marino 
2428*86d7f5d3SJohn Marino     t_options = vers->options;
2429*86d7f5d3SJohn Marino #if 0
2430*86d7f5d3SJohn Marino     if (*t_options == '\0')
2431*86d7f5d3SJohn Marino 	t_options = "-kk";		/* to ignore keyword expansions */
2432*86d7f5d3SJohn Marino #endif
2433*86d7f5d3SJohn Marino 
2434*86d7f5d3SJohn Marino     /* If the source of the merge is the same as the working file
2435*86d7f5d3SJohn Marino        revision, then we can just RCS_checkout the target (no merging
2436*86d7f5d3SJohn Marino        as such).  In the text file case, this is probably quite
2437*86d7f5d3SJohn Marino        similar to the RCS_merge, but in the binary file case,
2438*86d7f5d3SJohn Marino        RCS_merge gives all kinds of trouble.  */
2439*86d7f5d3SJohn Marino     if (vers->vn_user != NULL
2440*86d7f5d3SJohn Marino 	&& strcmp (rev1, vers->vn_user) == 0
2441*86d7f5d3SJohn Marino 	/* See comments above about how No_Difference has already been
2442*86d7f5d3SJohn Marino 	   called.  */
2443*86d7f5d3SJohn Marino 	&& vers->ts_user != NULL
2444*86d7f5d3SJohn Marino 	&& strcmp (vers->ts_user, vers->ts_rcs) == 0
2445*86d7f5d3SJohn Marino 
2446*86d7f5d3SJohn Marino 	/* Avoid this in the text file case.  See below for why.
2447*86d7f5d3SJohn Marino 	 */
2448*86d7f5d3SJohn Marino 	&& (strcmp (t_options, "-kb") == 0
2449*86d7f5d3SJohn Marino 	    || wrap_merge_is_copy (finfo->file)))
2450*86d7f5d3SJohn Marino     {
2451*86d7f5d3SJohn Marino 	/* FIXME: Verify my comment below:
2452*86d7f5d3SJohn Marino 	 *
2453*86d7f5d3SJohn Marino 	 * RCS_merge does nothing with keywords.  It merges the changes between
2454*86d7f5d3SJohn Marino 	 * two revisions without expanding the keywords (it might expand in
2455*86d7f5d3SJohn Marino 	 * -kk mode before computing the diff between rev1 and rev2 - I'm not
2456*86d7f5d3SJohn Marino 	 * sure).  In other words, the keyword lines in the current work file
2457*86d7f5d3SJohn Marino 	 * get left alone.
2458*86d7f5d3SJohn Marino 	 *
2459*86d7f5d3SJohn Marino 	 * Therfore, checking out the destination revision (rev2) is probably
2460*86d7f5d3SJohn Marino 	 * incorrect in the text case since we should see the keywords that were
2461*86d7f5d3SJohn Marino 	 * substituted into the original file at the time it was checked out
2462*86d7f5d3SJohn Marino 	 * and not the keywords from rev2.
2463*86d7f5d3SJohn Marino 	 *
2464*86d7f5d3SJohn Marino 	 * Also, it is safe to pass in NULL for nametag since we know no
2465*86d7f5d3SJohn Marino 	 * substitution is happening during the binary mode checkout.
2466*86d7f5d3SJohn Marino 	 */
2467*86d7f5d3SJohn Marino 	if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, t_options,
2468*86d7f5d3SJohn Marino 			  RUN_TTY, NULL, NULL) != 0)
2469*86d7f5d3SJohn Marino 	    status = 2;
2470*86d7f5d3SJohn Marino 	else
2471*86d7f5d3SJohn Marino 	    status = 0;
2472*86d7f5d3SJohn Marino 
2473*86d7f5d3SJohn Marino 	/* OK, this is really stupid.  RCS_checkout carefully removes
2474*86d7f5d3SJohn Marino 	   write permissions, and we carefully put them back.  But
2475*86d7f5d3SJohn Marino 	   until someone gets around to fixing it, that seems like the
2476*86d7f5d3SJohn Marino 	   easiest way to get what would seem to be the right mode.
2477*86d7f5d3SJohn Marino 	   I don't check CVSWRITE or _watched; I haven't thought about
2478*86d7f5d3SJohn Marino 	   that in great detail, but it seems like a watched file should
2479*86d7f5d3SJohn Marino 	   be checked out (writable) after a merge.  */
2480*86d7f5d3SJohn Marino 	xchmod (finfo->file, 1);
2481*86d7f5d3SJohn Marino 
2482*86d7f5d3SJohn Marino 	/* Traditionally, the text file case prints a whole bunch of
2483*86d7f5d3SJohn Marino 	   scary looking and verbose output which fails to tell the user
2484*86d7f5d3SJohn Marino 	   what is really going on (it gives them rev1 and rev2 but doesn't
2485*86d7f5d3SJohn Marino 	   indicate in any way that rev1 == vn_user).  I think just a
2486*86d7f5d3SJohn Marino 	   simple "U foo" is good here; it seems analogous to the case in
2487*86d7f5d3SJohn Marino 	   which the file was added on the branch in terms of what to
2488*86d7f5d3SJohn Marino 	   print.  */
2489*86d7f5d3SJohn Marino 	write_letter (finfo, 'U');
2490*86d7f5d3SJohn Marino     }
2491*86d7f5d3SJohn Marino     else if (strcmp (t_options, "-kb") == 0
2492*86d7f5d3SJohn Marino 	     || wrap_merge_is_copy (finfo->file)
2493*86d7f5d3SJohn Marino 	     || special_file_mismatch (finfo, rev1, rev2))
2494*86d7f5d3SJohn Marino     {
2495*86d7f5d3SJohn Marino 	/* We are dealing with binary files, or files with a
2496*86d7f5d3SJohn Marino 	   permission/linkage mismatch (this second case only occurs when
2497*86d7f5d3SJohn Marino 	   PRESERVE_PERMISSIONS_SUPPORT is enabled), and real merging would
2498*86d7f5d3SJohn Marino 	   need to take place.  This is a conflict.  We give the user
2499*86d7f5d3SJohn Marino 	   the two files, and let them resolve it.  It is possible
2500*86d7f5d3SJohn Marino 	   that we should require a "touch foo" or similar step before
2501*86d7f5d3SJohn Marino 	   we allow a checkin.  */
2502*86d7f5d3SJohn Marino 	if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL,
2503*86d7f5d3SJohn Marino 			  t_options, RUN_TTY, NULL, NULL) != 0)
2504*86d7f5d3SJohn Marino 	    status = 2;
2505*86d7f5d3SJohn Marino 	else
2506*86d7f5d3SJohn Marino 	    status = 0;
2507*86d7f5d3SJohn Marino 
2508*86d7f5d3SJohn Marino 	/* OK, this is really stupid.  RCS_checkout carefully removes
2509*86d7f5d3SJohn Marino 	   write permissions, and we carefully put them back.  But
2510*86d7f5d3SJohn Marino 	   until someone gets around to fixing it, that seems like the
2511*86d7f5d3SJohn Marino 	   easiest way to get what would seem to be the right mode.
2512*86d7f5d3SJohn Marino 	   I don't check CVSWRITE or _watched; I haven't thought about
2513*86d7f5d3SJohn Marino 	   that in great detail, but it seems like a watched file should
2514*86d7f5d3SJohn Marino 	   be checked out (writable) after a merge.  */
2515*86d7f5d3SJohn Marino 	xchmod (finfo->file, 1);
2516*86d7f5d3SJohn Marino 
2517*86d7f5d3SJohn Marino 	/* Hmm.  We don't give them REV1 anywhere.  I guess most people
2518*86d7f5d3SJohn Marino 	   probably don't have a 3-way merge tool for the file type in
2519*86d7f5d3SJohn Marino 	   question, and might just get confused if we tried to either
2520*86d7f5d3SJohn Marino 	   provide them with a copy of the file from REV1, or even just
2521*86d7f5d3SJohn Marino 	   told them what REV1 is so they can get it themself, but it
2522*86d7f5d3SJohn Marino 	   might be worth thinking about.  */
2523*86d7f5d3SJohn Marino 	/* See comment in merge_file about the "nonmergeable file"
2524*86d7f5d3SJohn Marino 	   terminology.  */
2525*86d7f5d3SJohn Marino 	error (0, 0, "nonmergeable file needs merge");
2526*86d7f5d3SJohn Marino 	error (0, 0, "revision %s from repository is now in %s",
2527*86d7f5d3SJohn Marino 	       rev2, finfo->fullname);
2528*86d7f5d3SJohn Marino 	error (0, 0, "file from working directory is now in %s", backup);
2529*86d7f5d3SJohn Marino 	write_letter (finfo, 'C');
2530*86d7f5d3SJohn Marino     }
2531*86d7f5d3SJohn Marino     else
2532*86d7f5d3SJohn Marino 	status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
2533*86d7f5d3SJohn Marino 			    t_options, rev1, rev2);
2534*86d7f5d3SJohn Marino 
2535*86d7f5d3SJohn Marino     if (status != 0)
2536*86d7f5d3SJohn Marino     {
2537*86d7f5d3SJohn Marino 	if (status != 1)
2538*86d7f5d3SJohn Marino 	{
2539*86d7f5d3SJohn Marino 	    error (0, status == -1 ? errno : 0,
2540*86d7f5d3SJohn Marino 		   "could not merge revision %s of %s", rev2, finfo->fullname);
2541*86d7f5d3SJohn Marino 	    error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
2542*86d7f5d3SJohn Marino 		   finfo->fullname, backup);
2543*86d7f5d3SJohn Marino 	    rename_file (backup, finfo->file);
2544*86d7f5d3SJohn Marino 	}
2545*86d7f5d3SJohn Marino     }
2546*86d7f5d3SJohn Marino     else /* status == 0 */
2547*86d7f5d3SJohn Marino     {
2548*86d7f5d3SJohn Marino 	/* FIXME: the noexec case is broken.  RCS_merge could be doing the
2549*86d7f5d3SJohn Marino 	   xcmp on the temporary files without much hassle, I think.  */
2550*86d7f5d3SJohn Marino 	if (!noexec && !xcmp (backup, finfo->file))
2551*86d7f5d3SJohn Marino 	{
2552*86d7f5d3SJohn Marino 	    if (!really_quiet)
2553*86d7f5d3SJohn Marino 	    {
2554*86d7f5d3SJohn Marino 		cvs_output (finfo->fullname, 0);
2555*86d7f5d3SJohn Marino 		cvs_output (" already contains the differences between ", 0);
2556*86d7f5d3SJohn Marino 		cvs_output (rev1, 0);
2557*86d7f5d3SJohn Marino 		cvs_output (" and ", 0);
2558*86d7f5d3SJohn Marino 		cvs_output (rev2, 0);
2559*86d7f5d3SJohn Marino 		cvs_output ("\n", 1);
2560*86d7f5d3SJohn Marino 	    }
2561*86d7f5d3SJohn Marino 
2562*86d7f5d3SJohn Marino 	    /* and skip the registering and sending the new file since it
2563*86d7f5d3SJohn Marino 	     * hasn't been updated.
2564*86d7f5d3SJohn Marino 	     */
2565*86d7f5d3SJohn Marino 	    goto out;
2566*86d7f5d3SJohn Marino 	}
2567*86d7f5d3SJohn Marino     }
2568*86d7f5d3SJohn Marino 
2569*86d7f5d3SJohn Marino     /* The file has changed, but if we just checked it out it may
2570*86d7f5d3SJohn Marino        still have the same timestamp it did when it was first
2571*86d7f5d3SJohn Marino        registered above in checkout_file.  We register it again with a
2572*86d7f5d3SJohn Marino        dummy timestamp to make sure that later runs of CVS will
2573*86d7f5d3SJohn Marino        recognize that it has changed.
2574*86d7f5d3SJohn Marino 
2575*86d7f5d3SJohn Marino        We don't actually need to register again if we called
2576*86d7f5d3SJohn Marino        RCS_checkout above, and we aren't running as the server.
2577*86d7f5d3SJohn Marino        However, that is not the normal case, and calling Register
2578*86d7f5d3SJohn Marino        again won't cost much in that case.  */
2579*86d7f5d3SJohn Marino     RegisterMerge (finfo, vers, backup, status);
2580*86d7f5d3SJohn Marino 
2581*86d7f5d3SJohn Marino out:
2582*86d7f5d3SJohn Marino     free (rev1);
2583*86d7f5d3SJohn Marino     free (rev2);
2584*86d7f5d3SJohn Marino     free (backup);
2585*86d7f5d3SJohn Marino }
2586*86d7f5d3SJohn Marino 
2587*86d7f5d3SJohn Marino 
2588*86d7f5d3SJohn Marino 
2589*86d7f5d3SJohn Marino /*
2590*86d7f5d3SJohn Marino  * Report whether revisions REV1 and REV2 of FINFO agree on:
2591*86d7f5d3SJohn Marino  *   . file ownership
2592*86d7f5d3SJohn Marino  *   . permissions
2593*86d7f5d3SJohn Marino  *   . major and minor device numbers
2594*86d7f5d3SJohn Marino  *   . symbolic links
2595*86d7f5d3SJohn Marino  *   . hard links
2596*86d7f5d3SJohn Marino  *
2597*86d7f5d3SJohn Marino  * If either REV1 or REV2 is NULL, the working copy is used instead.
2598*86d7f5d3SJohn Marino  *
2599*86d7f5d3SJohn Marino  * Return 1 if the files differ on these data.
2600*86d7f5d3SJohn Marino  */
2601*86d7f5d3SJohn Marino 
2602*86d7f5d3SJohn Marino int
special_file_mismatch(struct file_info * finfo,char * rev1,char * rev2)2603*86d7f5d3SJohn Marino special_file_mismatch (struct file_info *finfo, char *rev1, char *rev2)
2604*86d7f5d3SJohn Marino {
2605*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
2606*86d7f5d3SJohn Marino     struct stat sb;
2607*86d7f5d3SJohn Marino     RCSVers *vp;
2608*86d7f5d3SJohn Marino     Node *n;
2609*86d7f5d3SJohn Marino     uid_t rev1_uid, rev2_uid;
2610*86d7f5d3SJohn Marino     gid_t rev1_gid, rev2_gid;
2611*86d7f5d3SJohn Marino     mode_t rev1_mode, rev2_mode;
2612*86d7f5d3SJohn Marino     unsigned long dev_long;
2613*86d7f5d3SJohn Marino     dev_t rev1_dev, rev2_dev;
2614*86d7f5d3SJohn Marino     char *rev1_symlink = NULL;
2615*86d7f5d3SJohn Marino     char *rev2_symlink = NULL;
2616*86d7f5d3SJohn Marino     List *rev1_hardlinks = NULL;
2617*86d7f5d3SJohn Marino     List *rev2_hardlinks = NULL;
2618*86d7f5d3SJohn Marino     int check_uids, check_gids, check_modes;
2619*86d7f5d3SJohn Marino     int result;
2620*86d7f5d3SJohn Marino 
2621*86d7f5d3SJohn Marino     /* If we don't care about special file info, then
2622*86d7f5d3SJohn Marino        don't report a mismatch in any case. */
2623*86d7f5d3SJohn Marino     if (!preserve_perms)
2624*86d7f5d3SJohn Marino 	return 0;
2625*86d7f5d3SJohn Marino 
2626*86d7f5d3SJohn Marino     /* When special_file_mismatch is called from No_Difference, the
2627*86d7f5d3SJohn Marino        RCS file has been only partially parsed.  We must read the
2628*86d7f5d3SJohn Marino        delta tree in order to compare special file info recorded in
2629*86d7f5d3SJohn Marino        the delta nodes.  (I think this is safe. -twp) */
2630*86d7f5d3SJohn Marino     if (finfo->rcs->flags & PARTIAL)
2631*86d7f5d3SJohn Marino 	RCS_reparsercsfile (finfo->rcs, NULL, NULL);
2632*86d7f5d3SJohn Marino 
2633*86d7f5d3SJohn Marino     check_uids = check_gids = check_modes = 1;
2634*86d7f5d3SJohn Marino 
2635*86d7f5d3SJohn Marino     /* Obtain file information for REV1.  If this is null, then stat
2636*86d7f5d3SJohn Marino        finfo->file and use that info. */
2637*86d7f5d3SJohn Marino     /* If a revision does not know anything about its status,
2638*86d7f5d3SJohn Marino        then presumably it doesn't matter, and indicates no conflict. */
2639*86d7f5d3SJohn Marino 
2640*86d7f5d3SJohn Marino     if (rev1 == NULL)
2641*86d7f5d3SJohn Marino     {
2642*86d7f5d3SJohn Marino 	ssize_t rsize;
2643*86d7f5d3SJohn Marino 
2644*86d7f5d3SJohn Marino 	if ((rsize = islink (finfo->file)) > 0)
2645*86d7f5d3SJohn Marino 	    rev1_symlink = Xreadlink (finfo->file, rsize);
2646*86d7f5d3SJohn Marino 	else
2647*86d7f5d3SJohn Marino 	{
2648*86d7f5d3SJohn Marino # ifdef HAVE_STRUCT_STAT_ST_RDEV
2649*86d7f5d3SJohn Marino 	    if (lstat (finfo->file, &sb) < 0)
2650*86d7f5d3SJohn Marino 		error (1, errno, "could not get file information for %s",
2651*86d7f5d3SJohn Marino 		       finfo->file);
2652*86d7f5d3SJohn Marino 	    rev1_uid = sb.st_uid;
2653*86d7f5d3SJohn Marino 	    rev1_gid = sb.st_gid;
2654*86d7f5d3SJohn Marino 	    rev1_mode = sb.st_mode;
2655*86d7f5d3SJohn Marino 	    if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
2656*86d7f5d3SJohn Marino 		rev1_dev = sb.st_rdev;
2657*86d7f5d3SJohn Marino # else
2658*86d7f5d3SJohn Marino 	    error (1, 0, "cannot handle device files on this system (%s)",
2659*86d7f5d3SJohn Marino 		   finfo->file);
2660*86d7f5d3SJohn Marino # endif
2661*86d7f5d3SJohn Marino 	}
2662*86d7f5d3SJohn Marino 	rev1_hardlinks = list_linked_files_on_disk (finfo->file);
2663*86d7f5d3SJohn Marino     }
2664*86d7f5d3SJohn Marino     else
2665*86d7f5d3SJohn Marino     {
2666*86d7f5d3SJohn Marino 	n = findnode (finfo->rcs->versions, rev1);
2667*86d7f5d3SJohn Marino 	vp = n->data;
2668*86d7f5d3SJohn Marino 
2669*86d7f5d3SJohn Marino 	n = findnode (vp->other_delta, "symlink");
2670*86d7f5d3SJohn Marino 	if (n != NULL)
2671*86d7f5d3SJohn Marino 	    rev1_symlink = xstrdup (n->data);
2672*86d7f5d3SJohn Marino 	else
2673*86d7f5d3SJohn Marino 	{
2674*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "owner");
2675*86d7f5d3SJohn Marino 	    if (n == NULL)
2676*86d7f5d3SJohn Marino 		check_uids = 0;	/* don't care */
2677*86d7f5d3SJohn Marino 	    else
2678*86d7f5d3SJohn Marino 		rev1_uid = strtoul (n->data, NULL, 10);
2679*86d7f5d3SJohn Marino 
2680*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "group");
2681*86d7f5d3SJohn Marino 	    if (n == NULL)
2682*86d7f5d3SJohn Marino 		check_gids = 0;	/* don't care */
2683*86d7f5d3SJohn Marino 	    else
2684*86d7f5d3SJohn Marino 		rev1_gid = strtoul (n->data, NULL, 10);
2685*86d7f5d3SJohn Marino 
2686*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "permissions");
2687*86d7f5d3SJohn Marino 	    if (n == NULL)
2688*86d7f5d3SJohn Marino 		check_modes = 0;	/* don't care */
2689*86d7f5d3SJohn Marino 	    else
2690*86d7f5d3SJohn Marino 		rev1_mode = strtoul (n->data, NULL, 8);
2691*86d7f5d3SJohn Marino 
2692*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "special");
2693*86d7f5d3SJohn Marino 	    if (n == NULL)
2694*86d7f5d3SJohn Marino 		rev1_mode |= S_IFREG;
2695*86d7f5d3SJohn Marino 	    else
2696*86d7f5d3SJohn Marino 	    {
2697*86d7f5d3SJohn Marino 		/* If the size of `ftype' changes, fix the sscanf call also */
2698*86d7f5d3SJohn Marino 		char ftype[16];
2699*86d7f5d3SJohn Marino 		if (sscanf (n->data, "%15s %lu", ftype,
2700*86d7f5d3SJohn Marino 			    &dev_long) < 2)
2701*86d7f5d3SJohn Marino 		    error (1, 0, "%s:%s has bad `special' newphrase %s",
2702*86d7f5d3SJohn Marino 			   finfo->file, rev1, (char *)n->data);
2703*86d7f5d3SJohn Marino 		rev1_dev = dev_long;
2704*86d7f5d3SJohn Marino 		if (strcmp (ftype, "character") == 0)
2705*86d7f5d3SJohn Marino 		    rev1_mode |= S_IFCHR;
2706*86d7f5d3SJohn Marino 		else if (strcmp (ftype, "block") == 0)
2707*86d7f5d3SJohn Marino 		    rev1_mode |= S_IFBLK;
2708*86d7f5d3SJohn Marino 		else
2709*86d7f5d3SJohn Marino 		    error (0, 0, "%s:%s unknown file type `%s'",
2710*86d7f5d3SJohn Marino 			   finfo->file, rev1, ftype);
2711*86d7f5d3SJohn Marino 	    }
2712*86d7f5d3SJohn Marino 
2713*86d7f5d3SJohn Marino 	    rev1_hardlinks = vp->hardlinks;
2714*86d7f5d3SJohn Marino 	    if (rev1_hardlinks == NULL)
2715*86d7f5d3SJohn Marino 		rev1_hardlinks = getlist();
2716*86d7f5d3SJohn Marino 	}
2717*86d7f5d3SJohn Marino     }
2718*86d7f5d3SJohn Marino 
2719*86d7f5d3SJohn Marino     /* Obtain file information for REV2. */
2720*86d7f5d3SJohn Marino     if (rev2 == NULL)
2721*86d7f5d3SJohn Marino     {
2722*86d7f5d3SJohn Marino 	ssize_t rsize;
2723*86d7f5d3SJohn Marino 
2724*86d7f5d3SJohn Marino 	if ((rsize = islink (finfo->file)) > 0)
2725*86d7f5d3SJohn Marino 	    rev2_symlink = Xreadlink (finfo->file, rsize);
2726*86d7f5d3SJohn Marino 	else
2727*86d7f5d3SJohn Marino 	{
2728*86d7f5d3SJohn Marino # ifdef HAVE_STRUCT_STAT_ST_RDEV
2729*86d7f5d3SJohn Marino 	    if (lstat (finfo->file, &sb) < 0)
2730*86d7f5d3SJohn Marino 		error (1, errno, "could not get file information for %s",
2731*86d7f5d3SJohn Marino 		       finfo->file);
2732*86d7f5d3SJohn Marino 	    rev2_uid = sb.st_uid;
2733*86d7f5d3SJohn Marino 	    rev2_gid = sb.st_gid;
2734*86d7f5d3SJohn Marino 	    rev2_mode = sb.st_mode;
2735*86d7f5d3SJohn Marino 	    if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
2736*86d7f5d3SJohn Marino 		rev2_dev = sb.st_rdev;
2737*86d7f5d3SJohn Marino # else
2738*86d7f5d3SJohn Marino 	    error (1, 0, "cannot handle device files on this system (%s)",
2739*86d7f5d3SJohn Marino 		   finfo->file);
2740*86d7f5d3SJohn Marino # endif
2741*86d7f5d3SJohn Marino 	}
2742*86d7f5d3SJohn Marino 	rev2_hardlinks = list_linked_files_on_disk (finfo->file);
2743*86d7f5d3SJohn Marino     }
2744*86d7f5d3SJohn Marino     else
2745*86d7f5d3SJohn Marino     {
2746*86d7f5d3SJohn Marino 	n = findnode (finfo->rcs->versions, rev2);
2747*86d7f5d3SJohn Marino 	vp = n->data;
2748*86d7f5d3SJohn Marino 
2749*86d7f5d3SJohn Marino 	n = findnode (vp->other_delta, "symlink");
2750*86d7f5d3SJohn Marino 	if (n != NULL)
2751*86d7f5d3SJohn Marino 	    rev2_symlink = xstrdup (n->data);
2752*86d7f5d3SJohn Marino 	else
2753*86d7f5d3SJohn Marino 	{
2754*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "owner");
2755*86d7f5d3SJohn Marino 	    if (n == NULL)
2756*86d7f5d3SJohn Marino 		check_uids = 0;	/* don't care */
2757*86d7f5d3SJohn Marino 	    else
2758*86d7f5d3SJohn Marino 		rev2_uid = strtoul (n->data, NULL, 10);
2759*86d7f5d3SJohn Marino 
2760*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "group");
2761*86d7f5d3SJohn Marino 	    if (n == NULL)
2762*86d7f5d3SJohn Marino 		check_gids = 0;	/* don't care */
2763*86d7f5d3SJohn Marino 	    else
2764*86d7f5d3SJohn Marino 		rev2_gid = strtoul (n->data, NULL, 10);
2765*86d7f5d3SJohn Marino 
2766*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "permissions");
2767*86d7f5d3SJohn Marino 	    if (n == NULL)
2768*86d7f5d3SJohn Marino 		check_modes = 0;	/* don't care */
2769*86d7f5d3SJohn Marino 	    else
2770*86d7f5d3SJohn Marino 		rev2_mode = strtoul (n->data, NULL, 8);
2771*86d7f5d3SJohn Marino 
2772*86d7f5d3SJohn Marino 	    n = findnode (vp->other_delta, "special");
2773*86d7f5d3SJohn Marino 	    if (n == NULL)
2774*86d7f5d3SJohn Marino 		rev2_mode |= S_IFREG;
2775*86d7f5d3SJohn Marino 	    else
2776*86d7f5d3SJohn Marino 	    {
2777*86d7f5d3SJohn Marino 		/* If the size of `ftype' changes, fix the sscanf call also */
2778*86d7f5d3SJohn Marino 		char ftype[16];
2779*86d7f5d3SJohn Marino 		if (sscanf (n->data, "%15s %lu", ftype,
2780*86d7f5d3SJohn Marino 			    &dev_long) < 2)
2781*86d7f5d3SJohn Marino 		    error (1, 0, "%s:%s has bad `special' newphrase %s",
2782*86d7f5d3SJohn Marino 			   finfo->file, rev2, (char *)n->data);
2783*86d7f5d3SJohn Marino 		rev2_dev = dev_long;
2784*86d7f5d3SJohn Marino 		if (strcmp (ftype, "character") == 0)
2785*86d7f5d3SJohn Marino 		    rev2_mode |= S_IFCHR;
2786*86d7f5d3SJohn Marino 		else if (strcmp (ftype, "block") == 0)
2787*86d7f5d3SJohn Marino 		    rev2_mode |= S_IFBLK;
2788*86d7f5d3SJohn Marino 		else
2789*86d7f5d3SJohn Marino 		    error (0, 0, "%s:%s unknown file type `%s'",
2790*86d7f5d3SJohn Marino 			   finfo->file, rev2, ftype);
2791*86d7f5d3SJohn Marino 	    }
2792*86d7f5d3SJohn Marino 
2793*86d7f5d3SJohn Marino 	    rev2_hardlinks = vp->hardlinks;
2794*86d7f5d3SJohn Marino 	    if (rev2_hardlinks == NULL)
2795*86d7f5d3SJohn Marino 		rev2_hardlinks = getlist();
2796*86d7f5d3SJohn Marino 	}
2797*86d7f5d3SJohn Marino     }
2798*86d7f5d3SJohn Marino 
2799*86d7f5d3SJohn Marino     /* Check the user/group ownerships and file permissions, printing
2800*86d7f5d3SJohn Marino        an error for each mismatch found.  Return 0 if all characteristics
2801*86d7f5d3SJohn Marino        matched, and 1 otherwise. */
2802*86d7f5d3SJohn Marino 
2803*86d7f5d3SJohn Marino     result = 0;
2804*86d7f5d3SJohn Marino 
2805*86d7f5d3SJohn Marino     /* Compare symlinks first, since symlinks are simpler (don't have
2806*86d7f5d3SJohn Marino        any other characteristics). */
2807*86d7f5d3SJohn Marino     if (rev1_symlink != NULL && rev2_symlink == NULL)
2808*86d7f5d3SJohn Marino     {
2809*86d7f5d3SJohn Marino 	error (0, 0, "%s is a symbolic link",
2810*86d7f5d3SJohn Marino 	       (rev1 == NULL ? "working file" : rev1));
2811*86d7f5d3SJohn Marino 	result = 1;
2812*86d7f5d3SJohn Marino     }
2813*86d7f5d3SJohn Marino     else if (rev1_symlink == NULL && rev2_symlink != NULL)
2814*86d7f5d3SJohn Marino     {
2815*86d7f5d3SJohn Marino 	error (0, 0, "%s is a symbolic link",
2816*86d7f5d3SJohn Marino 	       (rev2 == NULL ? "working file" : rev2));
2817*86d7f5d3SJohn Marino 	result = 1;
2818*86d7f5d3SJohn Marino     }
2819*86d7f5d3SJohn Marino     else if (rev1_symlink != NULL)
2820*86d7f5d3SJohn Marino 	result = (strcmp (rev1_symlink, rev2_symlink) == 0);
2821*86d7f5d3SJohn Marino     else
2822*86d7f5d3SJohn Marino     {
2823*86d7f5d3SJohn Marino 	/* Compare user ownership. */
2824*86d7f5d3SJohn Marino 	if (check_uids && rev1_uid != rev2_uid)
2825*86d7f5d3SJohn Marino 	{
2826*86d7f5d3SJohn Marino 	    error (0, 0, "%s: owner mismatch between %s and %s",
2827*86d7f5d3SJohn Marino 		   finfo->file,
2828*86d7f5d3SJohn Marino 		   (rev1 == NULL ? "working file" : rev1),
2829*86d7f5d3SJohn Marino 		   (rev2 == NULL ? "working file" : rev2));
2830*86d7f5d3SJohn Marino 	    result = 1;
2831*86d7f5d3SJohn Marino 	}
2832*86d7f5d3SJohn Marino 
2833*86d7f5d3SJohn Marino 	/* Compare group ownership. */
2834*86d7f5d3SJohn Marino 	if (check_gids && rev1_gid != rev2_gid)
2835*86d7f5d3SJohn Marino 	{
2836*86d7f5d3SJohn Marino 	    error (0, 0, "%s: group mismatch between %s and %s",
2837*86d7f5d3SJohn Marino 		   finfo->file,
2838*86d7f5d3SJohn Marino 		   (rev1 == NULL ? "working file" : rev1),
2839*86d7f5d3SJohn Marino 		   (rev2 == NULL ? "working file" : rev2));
2840*86d7f5d3SJohn Marino 	    result = 1;
2841*86d7f5d3SJohn Marino 	}
2842*86d7f5d3SJohn Marino 
2843*86d7f5d3SJohn Marino 	/* Compare permissions. */
2844*86d7f5d3SJohn Marino 	if (check_modes &&
2845*86d7f5d3SJohn Marino 	    (rev1_mode & 07777) != (rev2_mode & 07777))
2846*86d7f5d3SJohn Marino 	{
2847*86d7f5d3SJohn Marino 	    error (0, 0, "%s: permission mismatch between %s and %s",
2848*86d7f5d3SJohn Marino 		   finfo->file,
2849*86d7f5d3SJohn Marino 		   (rev1 == NULL ? "working file" : rev1),
2850*86d7f5d3SJohn Marino 		   (rev2 == NULL ? "working file" : rev2));
2851*86d7f5d3SJohn Marino 	    result = 1;
2852*86d7f5d3SJohn Marino 	}
2853*86d7f5d3SJohn Marino 
2854*86d7f5d3SJohn Marino 	/* Compare device file characteristics. */
2855*86d7f5d3SJohn Marino 	if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
2856*86d7f5d3SJohn Marino 	{
2857*86d7f5d3SJohn Marino 	    error (0, 0, "%s: %s and %s are different file types",
2858*86d7f5d3SJohn Marino 		   finfo->file,
2859*86d7f5d3SJohn Marino 		   (rev1 == NULL ? "working file" : rev1),
2860*86d7f5d3SJohn Marino 		   (rev2 == NULL ? "working file" : rev2));
2861*86d7f5d3SJohn Marino 	    result = 1;
2862*86d7f5d3SJohn Marino 	}
2863*86d7f5d3SJohn Marino 	else if (S_ISBLK (rev1_mode))
2864*86d7f5d3SJohn Marino 	{
2865*86d7f5d3SJohn Marino 	    if (rev1_dev != rev2_dev)
2866*86d7f5d3SJohn Marino 	    {
2867*86d7f5d3SJohn Marino 		error (0, 0, "%s: device numbers of %s and %s do not match",
2868*86d7f5d3SJohn Marino 		       finfo->file,
2869*86d7f5d3SJohn Marino 		       (rev1 == NULL ? "working file" : rev1),
2870*86d7f5d3SJohn Marino 		       (rev2 == NULL ? "working file" : rev2));
2871*86d7f5d3SJohn Marino 		result = 1;
2872*86d7f5d3SJohn Marino 	    }
2873*86d7f5d3SJohn Marino 	}
2874*86d7f5d3SJohn Marino 
2875*86d7f5d3SJohn Marino 	/* Compare hard links. */
2876*86d7f5d3SJohn Marino 	if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0)
2877*86d7f5d3SJohn Marino 	{
2878*86d7f5d3SJohn Marino 	    error (0, 0, "%s: hard linkage of %s and %s do not match",
2879*86d7f5d3SJohn Marino 		   finfo->file,
2880*86d7f5d3SJohn Marino 		   (rev1 == NULL ? "working file" : rev1),
2881*86d7f5d3SJohn Marino 		   (rev2 == NULL ? "working file" : rev2));
2882*86d7f5d3SJohn Marino 	    result = 1;
2883*86d7f5d3SJohn Marino 	}
2884*86d7f5d3SJohn Marino     }
2885*86d7f5d3SJohn Marino 
2886*86d7f5d3SJohn Marino     if (rev1_symlink != NULL)
2887*86d7f5d3SJohn Marino 	free (rev1_symlink);
2888*86d7f5d3SJohn Marino     if (rev2_symlink != NULL)
2889*86d7f5d3SJohn Marino 	free (rev2_symlink);
2890*86d7f5d3SJohn Marino     if (rev1_hardlinks != NULL)
2891*86d7f5d3SJohn Marino 	dellist (&rev1_hardlinks);
2892*86d7f5d3SJohn Marino     if (rev2_hardlinks != NULL)
2893*86d7f5d3SJohn Marino 	dellist (&rev2_hardlinks);
2894*86d7f5d3SJohn Marino 
2895*86d7f5d3SJohn Marino     return result;
2896*86d7f5d3SJohn Marino #else
2897*86d7f5d3SJohn Marino     return 0;
2898*86d7f5d3SJohn Marino #endif
2899*86d7f5d3SJohn Marino }
2900*86d7f5d3SJohn Marino 
2901*86d7f5d3SJohn Marino 
2902*86d7f5d3SJohn Marino 
2903*86d7f5d3SJohn Marino int
joining(void)2904*86d7f5d3SJohn Marino joining (void)
2905*86d7f5d3SJohn Marino {
2906*86d7f5d3SJohn Marino     return join_rev1 || join_date1;
2907*86d7f5d3SJohn Marino }
2908