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