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