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