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