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