xref: /openbsd-src/gnu/usr.bin/cvs/src/update.c (revision ae5e8a9bc5efbe41ac6d96a58870257739c0284d)
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 1.4 kit.
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 
45 static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts,
46 				 int adding));
47 #ifdef SERVER_SUPPORT
48 static int patch_file PROTO ((struct file_info *finfo,
49 			      Vers_TS *vers_ts,
50 			      int *docheckout, struct stat *file_info,
51 			      unsigned char *checksum));
52 #endif
53 static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers));
54 static int scratch_file PROTO((struct file_info *finfo));
55 static Dtype update_dirent_proc PROTO ((void *callerdat, char *dir,
56 					char *repository, char *update_dir,
57 					List *entries));
58 static int update_dirleave_proc PROTO ((void *callerdat, char *dir,
59 					int err, char *update_dir,
60 					List *entries));
61 static int update_fileproc PROTO ((void *callerdat, struct file_info *));
62 static int update_filesdone_proc PROTO ((void *callerdat, int err,
63 					 char *repository, char *update_dir,
64 					 List *entries));
65 static int write_letter PROTO((char *file, int letter, char *update_dir));
66 #ifdef SERVER_SUPPORT
67 static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
68 #else
69 static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
70 #endif
71 
72 static char *options = NULL;
73 static char *tag = NULL;
74 static char *date = NULL;
75 static char *join_rev1, *date_rev1;
76 static char *join_rev2, *date_rev2;
77 static int aflag = 0;
78 static int force_tag_match = 1;
79 static int update_build_dirs = 0;
80 static int update_prune_dirs = 0;
81 static int pipeout = 0;
82 #ifdef SERVER_SUPPORT
83 static int patches = 0;
84 #endif
85 static List *ignlist = (List *) NULL;
86 static time_t last_register_time;
87 static const char *const update_usage[] =
88 {
89     "Usage: %s %s [-APdflRp] [-k kopt] [-r rev|-D date] [-j rev]\n",
90     "    [-I ign] [-W spec] [files...]\n",
91     "\t-A\tReset any sticky tags/date/kopts.\n",
92     "\t-P\tPrune empty directories.\n",
93     "\t-d\tBuild directories, like checkout does.\n",
94     "\t-f\tForce a head revision match if tag/date not found.\n",
95     "\t-l\tLocal directory only, no recursion.\n",
96     "\t-R\tProcess directories recursively.\n",
97     "\t-p\tSend updates to standard output (avoids stickiness).\n",
98     "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
99     "\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
100     "\t-D date\tSet date to update from (is sticky).\n",
101     "\t-j rev\tMerge in changes made between current revision and rev.\n",
102     "\t-I ign\tMore files to ignore (! to reset).\n",
103     "\t-W spec\tWrappers specification line.\n",
104     NULL
105 };
106 
107 /*
108  * update is the argv,argc based front end for arg parsing
109  */
110 int
111 update (argc, argv)
112     int argc;
113     char **argv;
114 {
115     int c, err;
116     int local = 0;			/* recursive by default */
117     int which;				/* where to look for files and dirs */
118 
119     if (argc == -1)
120 	usage (update_usage);
121 
122     ign_setup ();
123     wrap_setup ();
124 
125     /* parse the args */
126     optind = 1;
127     while ((c = getopt (argc, argv, "ApPflRQqduk:r:D:j:I:W:")) != -1)
128     {
129 	switch (c)
130 	{
131 	    case 'A':
132 		aflag = 1;
133 		break;
134 	    case 'I':
135 		ign_add (optarg, 0);
136 		break;
137 	    case 'W':
138 		wrap_add (optarg, 0);
139 		break;
140 	    case 'k':
141 		if (options)
142 		    free (options);
143 		options = RCS_check_kflag (optarg);
144 		break;
145 	    case 'l':
146 		local = 1;
147 		break;
148 	    case 'R':
149 		local = 0;
150 		break;
151 	    case 'Q':
152 	    case 'q':
153 #ifdef SERVER_SUPPORT
154 		/* The CVS 1.5 client sends these options (in addition to
155 		   Global_option requests), so we must ignore them.  */
156 		if (!server_active)
157 #endif
158 		    error (1, 0,
159 			   "-q or -Q must be specified before \"%s\"",
160 			   command_name);
161 		break;
162 	    case 'd':
163 		update_build_dirs = 1;
164 		break;
165 	    case 'f':
166 		force_tag_match = 0;
167 		break;
168 	    case 'r':
169 		tag = optarg;
170 		break;
171 	    case 'D':
172 		date = Make_Date (optarg);
173 		break;
174 	    case 'P':
175 		update_prune_dirs = 1;
176 		break;
177 	    case 'p':
178 		pipeout = 1;
179 		noexec = 1;		/* so no locks will be created */
180 		break;
181 	    case 'j':
182 		if (join_rev2)
183 		    error (1, 0, "only two -j options can be specified");
184 		if (join_rev1)
185 		    join_rev2 = optarg;
186 		else
187 		    join_rev1 = optarg;
188 		break;
189 	    case 'u':
190 #ifdef SERVER_SUPPORT
191 		if (server_active)
192 		    patches = 1;
193 		else
194 #endif
195 		    usage (update_usage);
196 		break;
197 	    case '?':
198 	    default:
199 		usage (update_usage);
200 		break;
201 	}
202     }
203     argc -= optind;
204     argv += optind;
205 
206 #ifdef CLIENT_SUPPORT
207     if (client_active)
208     {
209 	/* The first pass does the regular update.  If we receive at least
210 	   one patch which failed, we do a second pass and just fetch
211 	   those files whose patches failed.  */
212 	do
213 	{
214 	    int status;
215 
216 	    start_server ();
217 
218 	    if (local)
219 		send_arg("-l");
220 	    if (update_build_dirs)
221 		send_arg("-d");
222 	    if (pipeout)
223 		send_arg("-p");
224 	    if (!force_tag_match)
225 		send_arg("-f");
226 	    if (aflag)
227 		send_arg("-A");
228 	    if (update_prune_dirs)
229 		send_arg("-P");
230 	    client_prune_dirs = update_prune_dirs;
231 	    option_with_arg ("-r", tag);
232 	    if (options && options[0] != '\0')
233 		send_arg (options);
234 	    if (date)
235 		client_senddate (date);
236 	    if (join_rev1)
237 		option_with_arg ("-j", join_rev1);
238 	    if (join_rev2)
239 		option_with_arg ("-j", join_rev2);
240 	    wrap_send ();
241 
242 	    /* If the server supports the command "update-patches", that means
243 	       that it knows how to handle the -u argument to update, which
244 	       means to send patches instead of complete files.  */
245 	    if (failed_patches == NULL)
246 	    {
247 #ifndef DONT_USE_PATCH
248 		/* Systems which don't have the patch program ported to them
249 		   will want to define DONT_USE_PATCH; then CVS won't try to
250 		   invoke patch.  */
251 		if (supported_request ("update-patches"))
252 		    send_arg ("-u");
253 #endif
254 	    }
255 
256 	    if (failed_patches == NULL)
257 	    {
258 		send_file_names (argc, argv, SEND_EXPAND_WILD);
259 		send_files (argc, argv, local, aflag, update_build_dirs);
260 	    }
261 	    else
262 	    {
263 		int i;
264 
265 		(void) printf ("%s client: refetching unpatchable files\n",
266 			       program_name);
267 
268 		if (toplevel_wd != NULL
269 		    && CVS_CHDIR (toplevel_wd) < 0)
270 		{
271 		    error (1, errno, "could not chdir to %s", toplevel_wd);
272 		}
273 
274 		for (i = 0; i < failed_patches_count; i++)
275 		    (void) unlink_file (failed_patches[i]);
276 		send_file_names (failed_patches_count, failed_patches, 0);
277 		send_files (failed_patches_count, failed_patches, local,
278 			    aflag, update_build_dirs);
279 	    }
280 
281 	    failed_patches = NULL;
282 	    failed_patches_count = 0;
283 
284 	    send_to_server ("update\012", 0);
285 
286 	    status = get_responses_and_close ();
287 	    if (status != 0)
288 		return status;
289 
290 	} while (failed_patches != NULL);
291 
292 	return 0;
293     }
294 #endif
295 
296     if (tag != NULL)
297 	tag_check_valid (tag, argc, argv, local, aflag, "");
298     if (join_rev1 != NULL)
299         tag_check_valid_join (join_rev1, argc, argv, local, aflag, "");
300     if (join_rev2 != NULL)
301         tag_check_valid_join (join_rev2, argc, argv, local, aflag, "");
302 
303     /*
304      * If we are updating the entire directory (for real) and building dirs
305      * as we go, we make sure there is no static entries file and write the
306      * tag file as appropriate
307      */
308     if (argc <= 0 && !pipeout)
309     {
310 	if (update_build_dirs)
311 	{
312 	    if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
313 		error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
314 #ifdef SERVER_SUPPORT
315 	    if (server_active)
316 		server_clear_entstat (".", Name_Repository (NULL, NULL));
317 #endif
318 	}
319 
320 	/* keep the CVS/Tag file current with the specified arguments */
321 	if (aflag || tag || date)
322 	{
323 	    WriteTag ((char *) NULL, tag, date);
324 #ifdef SERVER_SUPPORT
325 	    if (server_active)
326 		server_set_sticky (".", Name_Repository (NULL, NULL), tag, date);
327 #endif
328 	}
329     }
330 
331     /* look for files/dirs locally and in the repository */
332     which = W_LOCAL | W_REPOS;
333 
334     /* look in the attic too if a tag or date is specified */
335     if (tag != NULL || date != NULL || joining())
336 	which |= W_ATTIC;
337 
338     /* call the command line interface */
339     err = do_update (argc, argv, options, tag, date, force_tag_match,
340 		     local, update_build_dirs, aflag, update_prune_dirs,
341 		     pipeout, which, join_rev1, join_rev2, (char *) NULL);
342 
343     /* free the space Make_Date allocated if necessary */
344     if (date != NULL)
345 	free (date);
346 
347     return (err);
348 }
349 
350 /*
351  * Command line interface to update (used by checkout)
352  */
353 int
354 do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
355 	   xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir)
356     int argc;
357     char **argv;
358     char *xoptions;
359     char *xtag;
360     char *xdate;
361     int xforce;
362     int local;
363     int xbuild;
364     int xaflag;
365     int xprune;
366     int xpipeout;
367     int which;
368     char *xjoin_rev1;
369     char *xjoin_rev2;
370     char *preload_update_dir;
371 {
372     int err = 0;
373     char *cp;
374 
375     /* fill in the statics */
376     options = xoptions;
377     tag = xtag;
378     date = xdate;
379     force_tag_match = xforce;
380     update_build_dirs = xbuild;
381     aflag = xaflag;
382     update_prune_dirs = xprune;
383     pipeout = xpipeout;
384 
385     /* setup the join support */
386     join_rev1 = xjoin_rev1;
387     join_rev2 = xjoin_rev2;
388     if (join_rev1 && (cp = strchr (join_rev1, ':')) != NULL)
389     {
390 	*cp++ = '\0';
391 	date_rev1 = Make_Date (cp);
392     }
393     else
394 	date_rev1 = (char *) NULL;
395     if (join_rev2 && (cp = strchr (join_rev2, ':')) != NULL)
396     {
397 	*cp++ = '\0';
398 	date_rev2 = Make_Date (cp);
399     }
400     else
401 	date_rev2 = (char *) NULL;
402 
403     /* call the recursion processor */
404     err = start_recursion (update_fileproc, update_filesdone_proc,
405 			   update_dirent_proc, update_dirleave_proc, NULL,
406 			   argc, argv, local, which, aflag, 1,
407 			   preload_update_dir, 1);
408 
409     /* see if we need to sleep before returning */
410     if (last_register_time)
411     {
412 	time_t now;
413 
414 	(void) time (&now);
415 	if (now == last_register_time)
416 	    sleep (1);			/* to avoid time-stamp races */
417     }
418 
419     return (err);
420 }
421 
422 /*
423  * This is the callback proc for update.  It is called for each file in each
424  * directory by the recursion code.  The current directory is the local
425  * instantiation.  file is the file name we are to operate on. update_dir is
426  * set to the path relative to where we started (for pretty printing).
427  * repository is the repository. entries and srcfiles are the pre-parsed
428  * entries and source control files.
429  *
430  * This routine decides what needs to be done for each file and does the
431  * appropriate magic for checkout
432  */
433 static int
434 update_fileproc (callerdat, finfo)
435     void *callerdat;
436     struct file_info *finfo;
437 {
438     int retval;
439     Ctype status;
440     Vers_TS *vers;
441     int resurrecting;
442 
443     resurrecting = 0;
444 
445     status = Classify_File (finfo, tag, date, options, force_tag_match,
446 			    aflag, &vers, pipeout);
447     if (pipeout)
448     {
449 	/*
450 	 * We just return success without doing anything if any of the really
451 	 * funky cases occur
452 	 *
453 	 * If there is still a valid RCS file, do a regular checkout type
454 	 * operation
455 	 */
456 	switch (status)
457 	{
458 	    case T_UNKNOWN:		/* unknown file was explicitly asked
459 					 * about */
460 	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
461 	    case T_ADDED:		/* added but not committed */
462 		retval = 0;
463 		break;
464 	    case T_CONFLICT:		/* old punt-type errors */
465 		retval = 1;
466 		break;
467 	    case T_UPTODATE:		/* file was already up-to-date */
468 	    case T_NEEDS_MERGE:		/* needs merging */
469 	    case T_MODIFIED:		/* locally modified */
470 	    case T_REMOVED:		/* removed but not committed */
471 	    case T_CHECKOUT:		/* needs checkout */
472 #ifdef SERVER_SUPPORT
473 	    case T_PATCH:		/* needs patch */
474 #endif
475 		retval = checkout_file (finfo, vers, 0);
476 		break;
477 
478 	    default:			/* can't ever happen :-) */
479 		error (0, 0,
480 		       "unknown file status %d for file %s", status, finfo->file);
481 		retval = 0;
482 		break;
483 	}
484     }
485     else
486     {
487 	switch (status)
488 	{
489 	    case T_UNKNOWN:		/* unknown file was explicitly asked
490 					 * about */
491 	    case T_UPTODATE:		/* file was already up-to-date */
492 		retval = 0;
493 		break;
494 	    case T_CONFLICT:		/* old punt-type errors */
495 		retval = 1;
496 		(void) write_letter (finfo->file, 'C', finfo->update_dir);
497 		break;
498 	    case T_NEEDS_MERGE:		/* needs merging */
499 		if (noexec)
500 		{
501 		    retval = 1;
502 		    (void) write_letter (finfo->file, 'C', finfo->update_dir);
503 		}
504 		else
505 		{
506 		    if (wrap_merge_is_copy (finfo->file))
507 			/* Should we be warning the user that we are
508 			 * overwriting the user's copy of the file?  */
509 			retval =
510 			  checkout_file (finfo, vers, 0);
511 		    else
512 			retval = merge_file (finfo, vers);
513 		}
514 		break;
515 	    case T_MODIFIED:		/* locally modified */
516 		retval = 0;
517 		if (vers->ts_conflict)
518 		{
519 		    char *filestamp;
520 		    int retcode;
521 
522 		    /*
523 		     * If the timestamp has changed and no conflict indicators
524 		     * are found, it isn't a 'C' any more.
525 		     */
526 #ifdef SERVER_SUPPORT
527 		    if (server_active)
528 			retcode = vers->ts_conflict[0] != '=';
529 		    else {
530 			filestamp = time_stamp (finfo->file);
531 			retcode = strcmp (vers->ts_conflict, filestamp);
532 			free (filestamp);
533 		    }
534 #else
535 		    filestamp = time_stamp (finfo->file);
536 		    retcode = strcmp (vers->ts_conflict, filestamp);
537 		    free (filestamp);
538 #endif
539 
540 		    if (retcode)
541 		    {
542 			/* The timestamps differ.  But if there are conflict
543 			   markers print 'C' anyway.  */
544 			retcode = !file_has_markers (finfo);
545 		    }
546 
547 		    if (!retcode)
548 		    {
549 			(void) write_letter (finfo->file, 'C', finfo->update_dir);
550 			retval = 1;
551 		    }
552 		    else
553 		    {
554 			/* Reregister to clear conflict flag. */
555 			Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_rcs,
556 				  vers->options, vers->tag,
557 				  vers->date, (char *)0);
558 		    }
559 		}
560 		if (!retval)
561 		    retval = write_letter (finfo->file, 'M', finfo->update_dir);
562 		break;
563 #ifdef SERVER_SUPPORT
564 	    case T_PATCH:		/* needs patch */
565 		if (patches)
566 		{
567 		    int docheckout;
568 		    struct stat file_info;
569 		    unsigned char checksum[16];
570 
571 		    retval = patch_file (finfo,
572 					 vers, &docheckout,
573 					 &file_info, checksum);
574 		    if (! docheckout)
575 		    {
576 		        if (server_active && retval == 0)
577 			    server_updated (finfo, vers,
578 					    SERVER_PATCHED, &file_info,
579 					    checksum);
580 			break;
581 		    }
582 		}
583 		/* Fall through.  */
584 		/* If we're not running as a server, just check the
585 		   file out.  It's simpler and faster than starting up
586 		   two new processes (diff and patch).  */
587 		/* Fall through.  */
588 #endif
589 	    case T_CHECKOUT:		/* needs checkout */
590 		retval = checkout_file (finfo, vers, 0);
591 #ifdef SERVER_SUPPORT
592 		if (server_active && retval == 0)
593 		    server_updated (finfo, vers,
594 				    SERVER_UPDATED, (struct stat *) NULL,
595 				    (unsigned char *) NULL);
596 #endif
597 		break;
598 	    case T_ADDED:		/* added but not committed */
599 		retval = write_letter (finfo->file, 'A', finfo->update_dir);
600 		break;
601 	    case T_REMOVED:		/* removed but not committed */
602 		retval = write_letter (finfo->file, 'R', finfo->update_dir);
603 		break;
604 	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
605 		retval = scratch_file (finfo);
606 #ifdef SERVER_SUPPORT
607 		if (server_active && retval == 0)
608 		{
609 		    if (vers->ts_user == NULL)
610 			server_scratch_entry_only ();
611 		    server_updated (finfo, vers,
612 				    SERVER_UPDATED, (struct stat *) NULL,
613 				    (unsigned char *) NULL);
614 		}
615 #endif
616 		break;
617 	    default:			/* can't ever happen :-) */
618 		error (0, 0,
619 		       "unknown file status %d for file %s", status, finfo->file);
620 		retval = 0;
621 		break;
622 	}
623     }
624 
625     /* only try to join if things have gone well thus far */
626     if (retval == 0 && join_rev1)
627 	join_file (finfo, vers);
628 
629     /* if this directory has an ignore list, add this file to it */
630     if (ignlist)
631     {
632 	Node *p;
633 
634 	p = getnode ();
635 	p->type = FILES;
636 	p->key = xstrdup (finfo->file);
637 	if (addnode (ignlist, p) != 0)
638 	    freenode (p);
639     }
640 
641     freevers_ts (&vers);
642     return (retval);
643 }
644 
645 static void update_ignproc PROTO ((char *, char *));
646 
647 static void
648 update_ignproc (file, dir)
649     char *file;
650     char *dir;
651 {
652     (void) write_letter (file, '?', dir);
653 }
654 
655 /* ARGSUSED */
656 static int
657 update_filesdone_proc (callerdat, err, repository, update_dir, entries)
658     void *callerdat;
659     int err;
660     char *repository;
661     char *update_dir;
662     List *entries;
663 {
664     /* if this directory has an ignore list, process it then free it */
665     if (ignlist)
666     {
667 	ignore_files (ignlist, entries, update_dir, update_ignproc);
668 	dellist (&ignlist);
669     }
670 
671     /* Clean up CVS admin dirs if we are export */
672     if (strcmp (command_name, "export") == 0)
673     {
674 	/* I'm not sure the existence_error is actually possible (except
675 	   in cases where we really should print a message), but since
676 	   this code used to ignore all errors, I'll play it safe.  */
677 	if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
678 	    error (0, errno, "cannot remove %s directory", CVSADM);
679     }
680 #ifdef SERVER_SUPPORT
681     else if (!server_active && !pipeout)
682 #else
683     else if (!pipeout)
684 #endif /* SERVER_SUPPORT */
685     {
686         /* If there is no CVS/Root file, add one */
687         if (!isfile (CVSADM_ROOT))
688 	    Create_Root ((char *) NULL, CVSroot_original);
689     }
690 
691     return (err);
692 }
693 
694 /*
695  * update_dirent_proc () is called back by the recursion processor before a
696  * sub-directory is processed for update.  In this case, update_dirent proc
697  * will probably create the directory unless -d isn't specified and this is a
698  * new directory.  A return code of 0 indicates the directory should be
699  * processed by the recursion code.  A return of non-zero indicates the
700  * recursion code should skip this directory.
701  */
702 static Dtype
703 update_dirent_proc (callerdat, dir, repository, update_dir, entries)
704     void *callerdat;
705     char *dir;
706     char *repository;
707     char *update_dir;
708     List *entries;
709 {
710     if (ignore_directory (update_dir))
711     {
712 	/* print the warm fuzzy message */
713 	if (!quiet)
714 	  error (0, 0, "Ignoring %s", update_dir);
715         return R_SKIP_ALL;
716     }
717 
718     if (!isdir (dir))
719     {
720 	/* if we aren't building dirs, blow it off */
721 	if (!update_build_dirs)
722 	    return (R_SKIP_ALL);
723 
724 	if (noexec)
725 	{
726 	    /* ignore the missing dir if -n is specified */
727 	    error (0, 0, "New directory `%s' -- ignored", dir);
728 	    return (R_SKIP_ALL);
729 	}
730 	else
731 	{
732 	    /* otherwise, create the dir and appropriate adm files */
733 	    make_directory (dir);
734 	    Create_Admin (dir, update_dir, repository, tag, date);
735 	    Subdir_Register (entries, (char *) NULL, dir);
736 	}
737     }
738     /* Do we need to check noexec here? */
739     else if (!pipeout)
740     {
741 	char *cvsadmdir;
742 
743 	/* The directory exists.  Check to see if it has a CVS
744 	   subdirectory.  */
745 
746 	cvsadmdir = xmalloc (strlen (dir) + 80);
747 	strcpy (cvsadmdir, dir);
748 	strcat (cvsadmdir, "/");
749 	strcat (cvsadmdir, CVSADM);
750 
751 	if (!isdir (cvsadmdir))
752 	{
753 	    /* We cannot successfully recurse into a directory without a CVS
754 	       subdirectory.  Generally we will have already printed
755 	       "? foo".  */
756 	    free (cvsadmdir);
757 	    return R_SKIP_ALL;
758 	}
759 	free (cvsadmdir);
760     }
761 
762     /*
763      * If we are building dirs and not going to stdout, we make sure there is
764      * no static entries file and write the tag file as appropriate
765      */
766     if (!pipeout)
767     {
768 	if (update_build_dirs)
769 	{
770 	    char tmp[PATH_MAX];
771 
772 	    (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
773 	    if (unlink_file (tmp) < 0 && ! existence_error (errno))
774 		error (1, errno, "cannot remove file %s", tmp);
775 #ifdef SERVER_SUPPORT
776 	    if (server_active)
777 		server_clear_entstat (update_dir, repository);
778 #endif
779 	}
780 
781 	/* keep the CVS/Tag file current with the specified arguments */
782 	if (aflag || tag || date)
783 	{
784 	    WriteTag (dir, tag, date);
785 #ifdef SERVER_SUPPORT
786 	    if (server_active)
787 		server_set_sticky (update_dir, repository, tag, date);
788 #endif
789 	}
790 
791 	/* initialize the ignore list for this directory */
792 	ignlist = getlist ();
793     }
794 
795     /* print the warm fuzzy message */
796     if (!quiet)
797 	error (0, 0, "Updating %s", update_dir);
798 
799     return (R_PROCESS);
800 }
801 
802 /*
803  * update_dirleave_proc () is called back by the recursion code upon leaving
804  * a directory.  It will prune empty directories if needed and will execute
805  * any appropriate update programs.
806  */
807 /* ARGSUSED */
808 static int
809 update_dirleave_proc (callerdat, dir, err, update_dir, entries)
810     void *callerdat;
811     char *dir;
812     int err;
813     char *update_dir;
814     List *entries;
815 {
816     FILE *fp;
817 
818     /* run the update_prog if there is one */
819     if (err == 0 && !pipeout && !noexec &&
820 	(fp = CVS_FOPEN (CVSADM_UPROG, "r")) != NULL)
821     {
822 	char *cp;
823 	char *repository;
824 	char line[MAXLINELEN];
825 
826 	repository = Name_Repository ((char *) NULL, update_dir);
827 	if (fgets (line, sizeof (line), fp) != NULL)
828 	{
829 	    if ((cp = strrchr (line, '\n')) != NULL)
830 		*cp = '\0';
831 	    run_setup ("%s %s", line, repository);
832 	    cvs_output (program_name, 0);
833 	    cvs_output (" ", 1);
834 	    cvs_output (command_name, 0);
835 	    cvs_output (": Executing '", 0);
836 	    run_print (stdout);
837 	    cvs_output ("'\n", 0);
838 	    (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
839 	}
840 	(void) fclose (fp);
841 	free (repository);
842     }
843 
844     if (strchr (dir, '/') == NULL)
845     {
846 	/* FIXME: chdir ("..") loses with symlinks.  */
847 	/* Prune empty dirs on the way out - if necessary */
848 	(void) CVS_CHDIR ("..");
849 	if (update_prune_dirs && isemptydir (dir, 0))
850 	{
851 	    /* I'm not sure the existence_error is actually possible (except
852 	       in cases where we really should print a message), but since
853 	       this code used to ignore all errors, I'll play it safe.	*/
854 	    if (unlink_file_dir (dir) < 0 && !existence_error (errno))
855 		error (0, errno, "cannot remove %s directory", dir);
856 	    Subdir_Deregister (entries, (char *) NULL, dir);
857 	}
858     }
859 
860     return (err);
861 }
862 
863 static int isremoved PROTO ((Node *, void *));
864 
865 /* Returns 1 if the file indicated by node has been removed.  */
866 static int
867 isremoved (node, closure)
868     Node *node;
869     void *closure;
870 {
871     Entnode *entdata = (Entnode*) node->data;
872 
873     /* If the first character of the version is a '-', the file has been
874        removed. */
875     return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
876 }
877 
878 /* Returns 1 if the argument directory is completely empty, other than the
879    existence of the CVS directory entry.  Zero otherwise.  If MIGHT_NOT_EXIST
880    and the directory doesn't exist, then just return 0.  */
881 int
882 isemptydir (dir, might_not_exist)
883     char *dir;
884     int might_not_exist;
885 {
886     DIR *dirp;
887     struct dirent *dp;
888 
889     if ((dirp = CVS_OPENDIR (dir)) == NULL)
890     {
891 	if (might_not_exist && existence_error (errno))
892 	    return 0;
893 	error (0, errno, "cannot open directory %s for empty check", dir);
894 	return (0);
895     }
896     errno = 0;
897     while ((dp = readdir (dirp)) != NULL)
898     {
899 	if (strcmp (dp->d_name, ".") != 0
900 	    && strcmp (dp->d_name, "..") != 0)
901 	{
902 	    if (strcmp (dp->d_name, CVSADM) != 0)
903 	    {
904 		/* An entry other than the CVS directory.  The directory
905 		   is certainly not empty. */
906 		(void) closedir (dirp);
907 		return (0);
908 	    }
909 	    else
910 	    {
911 		/* The CVS directory entry.  We don't have to worry about
912 		   this unless the Entries file indicates that files have
913 		   been removed, but not committed, in this directory.
914 		   (Removing the directory would prevent people from
915 		   comitting the fact that they removed the files!) */
916 		List *l;
917 		int files_removed;
918 		struct saved_cwd cwd;
919 
920 		if (save_cwd (&cwd))
921 		    error_exit ();
922 
923 		if (CVS_CHDIR (dir) < 0)
924 		    error (1, errno, "cannot change directory to %s", dir);
925 		l = Entries_Open (0);
926 		files_removed = walklist (l, isremoved, 0);
927 		Entries_Close (l);
928 
929 		if (restore_cwd (&cwd, NULL))
930 		    error_exit ();
931 		free_cwd (&cwd);
932 
933 		if (files_removed != 0)
934 		{
935 		    /* There are files that have been removed, but not
936 		       committed!  Do not consider the directory empty. */
937 		    (void) closedir (dirp);
938 		    return (0);
939 		}
940 	    }
941 	}
942 	errno = 0;
943     }
944     if (errno != 0)
945     {
946 	error (0, errno, "cannot read directory %s", dir);
947 	(void) closedir (dirp);
948 	return (0);
949     }
950     (void) closedir (dirp);
951     return (1);
952 }
953 
954 /*
955  * scratch the Entries file entry associated with a file
956  */
957 static int
958 scratch_file (finfo)
959     struct file_info *finfo;
960 {
961     history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
962     Scratch_Entry (finfo->entries, finfo->file);
963     if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
964 	error (0, errno, "unable to remove %s", finfo->fullname);
965     return (0);
966 }
967 
968 /*
969  * Check out a file.
970  */
971 static int
972 checkout_file (finfo, vers_ts, adding)
973     struct file_info *finfo;
974     Vers_TS *vers_ts;
975     int adding;
976 {
977     char backup[PATH_MAX];
978     int set_time, retval = 0;
979     int retcode = 0;
980     int status;
981     int file_is_dead;
982 
983     /* don't screw with backup files if we're going to stdout */
984     if (!pipeout)
985     {
986 	(void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
987 	if (isfile (finfo->file))
988 	    rename_file (finfo->file, backup);
989 	else
990 	    (void) unlink_file (backup);
991     }
992 
993     file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
994 
995     if (!file_is_dead)
996     {
997 	/*
998 	 * if we are checking out to stdout, print a nice message to
999 	 * stderr, and add the -p flag to the command */
1000 	if (pipeout)
1001 	{
1002 	    if (!quiet)
1003 	    {
1004 		cvs_outerr ("\
1005 ===================================================================\n\
1006 Checking out ", 0);
1007 		cvs_outerr (finfo->fullname, 0);
1008 		cvs_outerr ("\n\
1009 RCS:  ", 0);
1010 		cvs_outerr (vers_ts->srcfile->path, 0);
1011 		cvs_outerr ("\n\
1012 VERS: ", 0);
1013 		cvs_outerr (vers_ts->vn_rcs, 0);
1014 		cvs_outerr ("\n***************\n", 0);
1015 	    }
1016 	}
1017 
1018 	status = RCS_checkout (vers_ts->srcfile,
1019 			       pipeout ? NULL : finfo->file,
1020 			       vers_ts->vn_rcs, vers_ts->vn_tag,
1021 			       vers_ts->options, RUN_TTY);
1022     }
1023     if (file_is_dead || status == 0)
1024     {
1025 	if (!pipeout)
1026 	{
1027 	    Vers_TS *xvers_ts;
1028 
1029 	    if (cvswrite == TRUE
1030 		&& !file_is_dead
1031 		&& !fileattr_get (finfo->file, "_watched"))
1032 		xchmod (finfo->file, 1);
1033 
1034 	    {
1035 		/* A newly checked out file is never under the spell
1036 		   of "cvs edit".  If we think we were editing it
1037 		   from a previous life, clean up.  Would be better to
1038 		   check for same the working directory instead of
1039 		   same user, but that is hairy.  */
1040 
1041 		struct addremove_args args;
1042 
1043 		editor_set (finfo->file, getcaller (), NULL);
1044 
1045 		memset (&args, 0, sizeof args);
1046 		args.remove_temp = 1;
1047 		watch_modify_watchers (finfo->file, &args);
1048 	    }
1049 
1050 	    /* set the time from the RCS file iff it was unknown before */
1051 	    if (vers_ts->vn_user == NULL ||
1052 		strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
1053 	    {
1054 		set_time = 1;
1055 	    }
1056 	    else
1057 		set_time = 0;
1058 
1059 	    wrap_fromcvs_process_file (finfo->file);
1060 
1061 	    xvers_ts = Version_TS (finfo, options, tag, date,
1062 				   force_tag_match, set_time);
1063 	    if (strcmp (xvers_ts->options, "-V4") == 0)
1064 		xvers_ts->options[0] = '\0';
1065 
1066 	    (void) time (&last_register_time);
1067 
1068 	    if (file_is_dead)
1069 	    {
1070 		if (xvers_ts->vn_user != NULL)
1071 		{
1072 		    error (0, 0,
1073 			   "warning: %s is not (any longer) pertinent",
1074 			   finfo->fullname);
1075 		}
1076 		Scratch_Entry (finfo->entries, finfo->file);
1077 #ifdef SERVER_SUPPORT
1078 		if (server_active && xvers_ts->ts_user == NULL)
1079 		    server_scratch_entry_only ();
1080 #endif
1081 		/* FIXME: Rather than always unlink'ing, and ignoring the
1082 		   existence_error, we should do the unlink only if
1083 		   vers_ts->ts_user is non-NULL.  Then there would be no
1084 		   need to ignore an existence_error (for example, if the
1085 		   user removes the file while we are running).  */
1086 		if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1087 		{
1088 		    error (0, errno, "cannot remove %s", finfo->fullname);
1089 		}
1090 	    }
1091 	    else
1092 		Register (finfo->entries, finfo->file,
1093 			  adding ? "0" : xvers_ts->vn_rcs,
1094 			  xvers_ts->ts_user, xvers_ts->options,
1095 			  xvers_ts->tag, xvers_ts->date,
1096 			  (char *)0); /* Clear conflict flag on fresh checkout */
1097 
1098 	    /* fix up the vers structure, in case it is used by join */
1099 	    if (join_rev1)
1100 	    {
1101 		if (vers_ts->vn_user != NULL)
1102 		    free (vers_ts->vn_user);
1103 		if (vers_ts->vn_rcs != NULL)
1104 		    free (vers_ts->vn_rcs);
1105 		vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1106 		vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1107 	    }
1108 
1109 	    /* If this is really Update and not Checkout, recode history */
1110 	    if (strcmp (command_name, "update") == 0)
1111 		history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1112 			       finfo->repository);
1113 
1114 	    freevers_ts (&xvers_ts);
1115 
1116 	    if (!really_quiet && !file_is_dead)
1117 	    {
1118 		write_letter (finfo->file, 'U', finfo->update_dir);
1119 	    }
1120 	}
1121     }
1122     else
1123     {
1124 	int old_errno = errno;		/* save errno value over the rename */
1125 
1126 	if (!pipeout && isfile (backup))
1127 	    rename_file (backup, finfo->file);
1128 
1129 	error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1130 	       "could not check out %s", finfo->fullname);
1131 
1132 	retval = retcode;
1133     }
1134 
1135     if (!pipeout)
1136 	(void) unlink_file (backup);
1137 
1138     return (retval);
1139 }
1140 
1141 #ifdef SERVER_SUPPORT
1142 /* Patch a file.  Runs rcsdiff.  This is only done when running as the
1143  * server.  The hope is that the diff will be smaller than the file
1144  * itself.
1145  */
1146 static int
1147 patch_file (finfo, vers_ts, docheckout, file_info, checksum)
1148     struct file_info *finfo;
1149     Vers_TS *vers_ts;
1150     int *docheckout;
1151     struct stat *file_info;
1152     unsigned char *checksum;
1153 {
1154     char backup[PATH_MAX];
1155     char file1[PATH_MAX];
1156     char file2[PATH_MAX];
1157     int retval = 0;
1158     int retcode = 0;
1159     int fail;
1160     long file_size;
1161     FILE *e;
1162 
1163     *docheckout = 0;
1164 
1165     if (pipeout || joining ())
1166     {
1167 	*docheckout = 1;
1168 	return 0;
1169     }
1170 
1171     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1172     if (isfile (finfo->file))
1173         rename_file (finfo->file, backup);
1174     else
1175         (void) unlink_file (backup);
1176 
1177     (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
1178     (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
1179 
1180     fail = 0;
1181 
1182     /* We need to check out both revisions first, to see if either one
1183        has a trailing newline.  Because of this, we don't use rcsdiff,
1184        but just use diff.  */
1185     if (noexec)
1186 	retcode = 0;
1187     else
1188 	retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1189 				vers_ts->vn_user, (char *) NULL,
1190 				vers_ts->options, file1);
1191     if (retcode != 0)
1192         fail = 1;
1193     else
1194     {
1195         e = CVS_FOPEN (file1, "r");
1196 	if (e == NULL)
1197 	    fail = 1;
1198 	else
1199 	{
1200 	    if (fseek (e, (long) -1, SEEK_END) == 0
1201 		&& getc (e) != '\n')
1202 	    {
1203 	        fail = 1;
1204 	    }
1205 	    fclose (e);
1206 	}
1207     }
1208 
1209     if (! fail)
1210     {
1211         /* Check it out into finfo->file, and then move to file2, so that we
1212            can get the right modes into *FILE_INFO.  We can't check it
1213            out directly into file2 because co doesn't understand how
1214            to do that.  */
1215 	retcode = RCS_checkout (vers_ts->srcfile, finfo->file,
1216 				vers_ts->vn_rcs, (char *) NULL,
1217 				vers_ts->options, RUN_TTY);
1218 	if (retcode != 0)
1219 	    fail = 1;
1220 	else
1221 	{
1222 	    if (!isreadable (finfo->file))
1223 	    {
1224 	        /* File is dead.  */
1225 	        fail = 1;
1226 	    }
1227 	    else
1228 	    {
1229 	        rename_file (finfo->file, file2);
1230 		if (cvswrite == TRUE
1231 		    && !fileattr_get (finfo->file, "_watched"))
1232 		    xchmod (file2, 1);
1233 		e = CVS_FOPEN (file2, "r");
1234 		if (e == NULL)
1235 		    fail = 1;
1236 		else
1237 		{
1238 		    struct MD5Context context;
1239 		    int nl;
1240 		    unsigned char buf[8192];
1241 		    unsigned len;
1242 
1243 		    nl = 0;
1244 
1245 		    /* Compute the MD5 checksum and make sure there is
1246                        a trailing newline.  */
1247 		    MD5Init (&context);
1248 		    while ((len = fread (buf, 1, sizeof buf, e)) != 0)
1249 		    {
1250 			nl = buf[len - 1] == '\n';
1251 		        MD5Update (&context, buf, len);
1252 		    }
1253 		    MD5Final (checksum, &context);
1254 
1255 		    if (ferror (e) || ! nl)
1256 		    {
1257 		        fail = 1;
1258 		    }
1259 
1260 		    fseek(e, 0L, SEEK_END);
1261 		    file_size = ftell(e);
1262 
1263 		    fclose (e);
1264 		}
1265 	    }
1266 	}
1267     }
1268 
1269     retcode = 0;
1270     if (! fail)
1271     {
1272 	/* FIXME: This whole thing with diff/patch is rather more
1273 	   convoluted than necessary (lots of forks and execs, need to
1274 	   worry about versions of diff and patch, etc.).  Also, we
1275 	   send context lines which aren't needed (in the rare case in
1276 	   which the diff doesn't apply, the checksum would catches it).
1277 	   Solution perhaps is to librarify the RCS routines which apply
1278 	   deltas or something equivalent.  */
1279 	/* This is -c, not -u, because we have no way of knowing which
1280 	   DIFF is in use.  */
1281 	run_setup ("%s -c %s %s", DIFF, file1, file2);
1282 
1283 	/* A retcode of 0 means no differences.  1 means some differences.  */
1284 	if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0
1285 	    && retcode != 1)
1286 	{
1287 	    fail = 1;
1288 	}
1289 	else
1290 	{
1291 #define BINARY "Binary"
1292 	    char buf[sizeof BINARY];
1293 	    unsigned int c;
1294 
1295 	    /* Check the diff output to make sure patch will be handle it.  */
1296 	    e = CVS_FOPEN (finfo->file, "r");
1297 	    if (e == NULL)
1298 		error (1, errno, "could not open diff output file %s",
1299 		       finfo->fullname);
1300 	    c = fread (buf, 1, sizeof BINARY - 1, e);
1301 	    buf[c] = '\0';
1302 	    if (strcmp (buf, BINARY) == 0)
1303 	    {
1304 		/* These are binary files.  We could use diff -a, but
1305 		   patch can't handle that.  */
1306 		fail = 1;
1307 	    }
1308 	    else {
1309 		/*
1310 		 * Don't send a diff if just sending the entire file
1311 		 * would be smaller
1312 		 */
1313 		fseek(e, 0L, SEEK_END);
1314 		if (file_size < ftell(e))
1315 		    fail = 1;
1316 	    }
1317 
1318 	    fclose (e);
1319 	}
1320     }
1321 
1322     if (! fail)
1323     {
1324         Vers_TS *xvers_ts;
1325 
1326         /* This stuff is just copied blindly from checkout_file.  I
1327 	   don't really know what it does.  */
1328         xvers_ts = Version_TS (finfo, options, tag, date,
1329 			       force_tag_match, 0);
1330 	if (strcmp (xvers_ts->options, "-V4") == 0)
1331 	    xvers_ts->options[0] = '\0';
1332 
1333 	Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
1334 		  xvers_ts->ts_user, xvers_ts->options,
1335 		  xvers_ts->tag, xvers_ts->date, NULL);
1336 
1337 	if ( CVS_STAT (file2, file_info) < 0)
1338 	    error (1, errno, "could not stat %s", file2);
1339 
1340 	/* If this is really Update and not Checkout, recode history */
1341 	if (strcmp (command_name, "update") == 0)
1342 	    history_write ('P', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1343 			   finfo->repository);
1344 
1345 	freevers_ts (&xvers_ts);
1346 
1347 	if (!really_quiet)
1348 	{
1349 	    write_letter (finfo->file, 'P', finfo->update_dir);
1350 	}
1351     }
1352     else
1353     {
1354 	int old_errno = errno;		/* save errno value over the rename */
1355 
1356 	if (isfile (backup))
1357 	    rename_file (backup, finfo->file);
1358 
1359 	if (retcode != 0 && retcode != 1)
1360 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1361 		   "could not diff %s", finfo->fullname);
1362 
1363 	*docheckout = 1;
1364 	retval = retcode;
1365     }
1366 
1367     (void) unlink_file (backup);
1368     (void) unlink_file (file1);
1369     (void) unlink_file (file2);
1370 
1371     return (retval);
1372 }
1373 #endif
1374 
1375 /*
1376  * Several of the types we process only print a bit of information consisting
1377  * of a single letter and the name.
1378  */
1379 static int
1380 write_letter (file, letter, update_dir)
1381     char *file;
1382     int letter;
1383     char *update_dir;
1384 {
1385     if (!really_quiet)
1386     {
1387 	char buf[2];
1388 	buf[0] = letter;
1389 	buf[1] = ' ';
1390 	cvs_output (buf, 2);
1391 	if (update_dir[0])
1392 	{
1393 	    cvs_output (update_dir, 0);
1394 	    cvs_output ("/", 1);
1395 	}
1396 	cvs_output (file, 0);
1397 	cvs_output ("\n", 1);
1398     }
1399     return (0);
1400 }
1401 
1402 /*
1403  * Do all the magic associated with a file which needs to be merged
1404  */
1405 static int
1406 merge_file (finfo, vers)
1407     struct file_info *finfo;
1408     Vers_TS *vers;
1409 {
1410     char backup[PATH_MAX];
1411     int status;
1412     int retcode = 0;
1413 
1414     /*
1415      * The users currently modified file is moved to a backup file name
1416      * ".#filename.version", so that it will stay around for a few days
1417      * before being automatically removed by some cron daemon.  The "version"
1418      * is the version of the file that the user was most up-to-date with
1419      * before the merge.
1420      */
1421     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
1422 
1423     (void) unlink_file (backup);
1424     copy_file (finfo->file, backup);
1425     xchmod (finfo->file, 1);
1426 
1427     if (strcmp (vers->options, "-kb") == 0)
1428     {
1429 	/* For binary files, a merge is always a conflict.  We give the
1430 	   user the two files, and let them resolve it.  It is possible
1431 	   that we should require a "touch foo" or similar step before
1432 	   we allow a checkin.  */
1433 	status = checkout_file (finfo, vers, 0);
1434 #ifdef SERVER_SUPPORT
1435 	/* Send the new contents of the file before the message.  If we
1436 	   wanted to be totally correct, we would have the client write
1437 	   the message only after the file has safely been written.  */
1438 	if (server_active)
1439 	{
1440 	    server_copy_file (finfo->file, finfo->update_dir,
1441 			      finfo->repository, backup);
1442 	    server_updated (finfo, vers, SERVER_MERGED,
1443 			    (struct stat *) NULL, (unsigned char *) NULL);
1444 	}
1445 #endif
1446 	error (0, 0, "binary file needs merge");
1447 	error (0, 0, "revision %s from repository is now in %s",
1448 	       vers->vn_rcs, finfo->fullname);
1449 	error (0, 0, "file from working directory is now in %s", backup);
1450 	write_letter (finfo->file, 'C', finfo->update_dir);
1451 
1452 	history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
1453 	return 0;
1454     }
1455 
1456     status = RCS_merge(vers->srcfile->path,
1457 		       vers->options, vers->vn_user, vers->vn_rcs);
1458     if (status != 0 && status != 1)
1459     {
1460 	error (0, status == -1 ? errno : 0,
1461 	       "could not merge revision %s of %s", vers->vn_user, finfo->fullname);
1462 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1463 	       finfo->fullname, backup);
1464 	rename_file (backup, finfo->file);
1465 	return (1);
1466     }
1467 
1468     if (strcmp (vers->options, "-V4") == 0)
1469 	vers->options[0] = '\0';
1470     (void) time (&last_register_time);
1471     {
1472 	char *cp = 0;
1473 
1474 	if (status)
1475 	    cp = time_stamp (finfo->file);
1476 	Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_rcs, vers->options,
1477 		  vers->tag, vers->date, cp);
1478 	if (cp)
1479 	    free (cp);
1480     }
1481 
1482     /* fix up the vers structure, in case it is used by join */
1483     if (join_rev1)
1484     {
1485 	if (vers->vn_user != NULL)
1486 	    free (vers->vn_user);
1487 	vers->vn_user = xstrdup (vers->vn_rcs);
1488     }
1489 
1490 #ifdef SERVER_SUPPORT
1491     /* Send the new contents of the file before the message.  If we
1492        wanted to be totally correct, we would have the client write
1493        the message only after the file has safely been written.  */
1494     if (server_active)
1495     {
1496         server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
1497 			  backup);
1498 	server_updated (finfo, vers, SERVER_MERGED,
1499 			(struct stat *) NULL, (unsigned char *) NULL);
1500     }
1501 #endif
1502 
1503     if (!noexec && !xcmp (backup, finfo->file))
1504     {
1505 	printf ("%s already contains the differences between %s and %s\n",
1506 		finfo->fullname, vers->vn_user, vers->vn_rcs);
1507 	history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
1508 	return (0);
1509     }
1510 
1511     if (status == 1)
1512     {
1513 	if (!noexec)
1514 	    error (0, 0, "conflicts found in %s", finfo->fullname);
1515 
1516 	write_letter (finfo->file, 'C', finfo->update_dir);
1517 
1518 	history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
1519 
1520     }
1521     else if (retcode == -1)
1522     {
1523 	error (1, errno, "fork failed while examining update of %s", finfo->fullname);
1524     }
1525     else
1526     {
1527 	write_letter (finfo->file, 'M', finfo->update_dir);
1528 	history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
1529     }
1530     return (0);
1531 }
1532 
1533 /*
1534  * Do all the magic associated with a file which needs to be joined
1535  * (-j option)
1536  */
1537 static void
1538 join_file (finfo, vers)
1539     struct file_info *finfo;
1540     Vers_TS *vers;
1541 {
1542     char backup[PATH_MAX];
1543     char *options;
1544     int status;
1545 
1546     char *rev1;
1547     char *rev2;
1548     char *jrev1;
1549     char *jrev2;
1550     char *jdate1;
1551     char *jdate2;
1552 
1553     jrev1 = join_rev1;
1554     jrev2 = join_rev2;
1555     jdate1 = date_rev1;
1556     jdate2 = date_rev2;
1557 
1558     if (wrap_merge_is_copy (finfo->file))
1559     {
1560 	error (0, 0,
1561 	       "Cannot merge %s because it is a merge-by-copy file.",
1562 	       finfo->fullname);
1563 	return;
1564     }
1565 
1566     /* Determine if we need to do anything at all.  */
1567     if (vers->srcfile == NULL ||
1568 	vers->srcfile->path == NULL)
1569     {
1570 	return;
1571     }
1572 
1573     /* If only one join revision is specified, it becomes the second
1574        revision.  */
1575     if (jrev2 == NULL)
1576     {
1577 	jrev2 = jrev1;
1578 	jrev1 = NULL;
1579 	jdate2 = jdate1;
1580 	jdate1 = NULL;
1581     }
1582 
1583     /* Convert the second revision, walking branches and dates.  */
1584     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, (int *) NULL);
1585 
1586     /* If this is a merge of two revisions, get the first revision.
1587        If only one join tag was specified, then the first revision is
1588        the greatest common ancestor of the second revision and the
1589        working file.  */
1590     if (jrev1 != NULL)
1591 	rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, (int *) NULL);
1592     else
1593     {
1594 	/* Note that we use vn_rcs here, since vn_user may contain a
1595            special string such as "-nn".  */
1596 	if (vers->vn_rcs == NULL)
1597 	    rev1 = NULL;
1598 	else if (rev2 == NULL)
1599 	{
1600 	    /* This means that the file never existed on the branch.
1601                It does not mean that the file was removed on the
1602                branch: that case is represented by a dead rev2.  If
1603                the file never existed on the branch, then we have
1604                nothing to merge, so we just return.  */
1605 	    return;
1606 	}
1607 	else
1608 	    rev1 = gca (vers->vn_rcs, rev2);
1609     }
1610 
1611     /* Handle a nonexistent or dead merge target.  */
1612     if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
1613     {
1614 	char *mrev;
1615 
1616 	if (rev2 != NULL)
1617 	    free (rev2);
1618 
1619 	/* If the first revision doesn't exist either, then there is
1620            no change between the two revisions, so we don't do
1621            anything.  */
1622 	if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
1623 	{
1624 	    if (rev1 != NULL)
1625 		free (rev1);
1626 	    return;
1627 	}
1628 
1629 	/* If we are merging two revisions, then the file was removed
1630 	   between the first revision and the second one.  In this
1631 	   case we want to mark the file for removal.
1632 
1633 	   If we are merging one revision, then the file has been
1634 	   removed between the greatest common ancestor and the merge
1635 	   revision.  From the perspective of the branch on to which
1636 	   we ar emerging, which may be the trunk, either 1) the file
1637 	   does not currently exist on the target, or 2) the file has
1638 	   not been modified on the target branch since the greatest
1639 	   common ancestor, or 3) the file has been modified on the
1640 	   target branch since the greatest common ancestor.  In case
1641 	   1 there is nothing to do.  In case 2 we mark the file for
1642 	   removal.  In case 3 we have a conflict.
1643 
1644 	   Note that the handling is slightly different depending upon
1645 	   whether one or two join targets were specified.  If two
1646 	   join targets were specified, we don't check whether the
1647 	   file was modified since a given point.  My reasoning is
1648 	   that if you ask for an explicit merge between two tags,
1649 	   then you want to merge in whatever was changed between
1650 	   those two tags.  If a file was removed between the two
1651 	   tags, then you want it to be removed.  However, if you ask
1652 	   for a merge of a branch, then you want to merge in all
1653 	   changes which were made on the branch.  If a file was
1654 	   removed on the branch, that is a change to the file.  If
1655 	   the file was also changed on the main line, then that is
1656 	   also a change.  These two changes--the file removal and the
1657 	   modification--must be merged.  This is a conflict.  */
1658 
1659 	/* If the user file is dead, or does not exist, or has been
1660            marked for removal, then there is nothing to do.  */
1661 	if (vers->vn_user == NULL
1662 	    || vers->vn_user[0] == '-'
1663 	    || RCS_isdead (vers->srcfile, vers->vn_user))
1664 	{
1665 	    if (rev1 != NULL)
1666 		free (rev1);
1667 	    return;
1668 	}
1669 
1670 	/* If the user file has been marked for addition, or has been
1671 	   locally modified, then we have a conflict which we can not
1672 	   resolve.  No_Difference will already have been called in
1673 	   this case, so comparing the timestamps is sufficient to
1674 	   determine whether the file is locally modified.  */
1675 	if (strcmp (vers->vn_user, "0") == 0
1676 	    || (vers->ts_user != NULL
1677 		&& strcmp (vers->ts_user, vers->ts_rcs) != 0))
1678 	{
1679 	    if (jdate2 != NULL)
1680 		error (0, 0,
1681 		       "file %s is locally modified, but has been removed in revision %s as of %s",
1682 		       finfo->fullname, jrev2, jdate2);
1683 	    else
1684 		error (0, 0,
1685 		       "file %s is locally modified, but has been removed in revision %s",
1686 		       finfo->fullname, jrev2);
1687 
1688 	    /* FIXME: Should we arrange to return a non-zero exit
1689                status?  */
1690 
1691 	    if (rev1 != NULL)
1692 		free (rev1);
1693 
1694 	    return;
1695 	}
1696 
1697 	/* If only one join tag was specified, and the user file has
1698            been changed since the greatest common ancestor (rev1),
1699            then there is a conflict we can not resolve.  See above for
1700            the rationale.  */
1701 	if (join_rev2 == NULL
1702 	    && strcmp (rev1, vers->vn_user) != 0)
1703 	{
1704 	    if (jdate2 != NULL)
1705 		error (0, 0,
1706 		       "file %s has been modified, but has been removed in revision %s as of %s",
1707 		       finfo->fullname, jrev2, jdate2);
1708 	    else
1709 		error (0, 0,
1710 		       "file %s has been modified, but has been removed in revision %s",
1711 		       finfo->fullname, jrev2);
1712 
1713 	    /* FIXME: Should we arrange to return a non-zero exit
1714                status?  */
1715 
1716 	    if (rev1 != NULL)
1717 		free (rev1);
1718 
1719 	    return;
1720 	}
1721 
1722 	if (rev1 != NULL)
1723 	    free (rev1);
1724 
1725 	/* The user file exists and has not been modified.  Mark it
1726            for removal.  FIXME: If we are doing a checkout, this has
1727            the effect of first checking out the file, and then
1728            removing it.  It would be better to just register the
1729            removal.  */
1730 #ifdef SERVER_SUPPORT
1731 	if (server_active)
1732 	{
1733 	    server_scratch (finfo->file);
1734 	    server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL,
1735 			    (unsigned char *) NULL);
1736 	}
1737 #endif
1738 	mrev = xmalloc (strlen (vers->vn_user) + 2);
1739 	sprintf (mrev, "-%s", vers->vn_user);
1740 	Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
1741 		  vers->options, vers->tag, vers->date, vers->ts_conflict);
1742 	free (mrev);
1743 	/* We need to check existence_error here because if we are
1744            running as the server, and the file is up to date in the
1745            working directory, the client will not have sent us a copy.  */
1746 	if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1747 	    error (0, errno, "cannot remove file %s", finfo->fullname);
1748 #ifdef SERVER_SUPPORT
1749 	if (server_active)
1750 	    server_checked_in (finfo->file, finfo->update_dir,
1751 			       finfo->repository);
1752 #endif
1753 	if (! really_quiet)
1754 	    error (0, 0, "scheduling %s for removal", finfo->fullname);
1755 
1756 	return;
1757     }
1758 
1759     /* If the target of the merge is the same as the working file
1760        revision, then there is nothing to do.  */
1761     if (vers->vn_user != NULL && strcmp (rev2, vers->vn_user) == 0)
1762     {
1763 	if (rev1 != NULL)
1764 	    free (rev1);
1765 	free (rev2);
1766 	return;
1767     }
1768 
1769     /* If rev1 is dead or does not exist, then the file was added
1770        between rev1 and rev2.  */
1771     if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
1772     {
1773 	if (rev1 != NULL)
1774 	    free (rev1);
1775 	free (rev2);
1776 
1777 	/* If the file does not exist in the working directory, then
1778            we can just check out the new revision and mark it for
1779            addition.  */
1780 	if (vers->vn_user == NULL)
1781 	{
1782 	    Vers_TS *xvers;
1783 
1784 	    xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
1785 
1786 	    /* FIXME: If checkout_file fails, we should arrange to
1787                return a non-zero exit status.  */
1788 	    status = checkout_file (finfo, xvers, 1);
1789 
1790 #ifdef SERVER_SUPPORT
1791 	    if (server_active && status == 0)
1792 		server_updated (finfo, xvers,
1793 				SERVER_UPDATED, (struct stat *) NULL,
1794 				(unsigned char *) NULL);
1795 #endif
1796 
1797 	    freevers_ts (&xvers);
1798 
1799 	    return;
1800 	}
1801 
1802 	/* The file currently exists in the working directory, so we
1803            have a conflict which we can not resolve.  Note that this
1804            is true even if the file is marked for addition or removal.  */
1805 
1806 	if (jdate2 != NULL)
1807 	    error (0, 0,
1808 		   "file %s exists, but has been added in revision %s as of %s",
1809 		   finfo->fullname, jrev2, jdate2);
1810 	else
1811 	    error (0, 0,
1812 		   "file %s exists, but has been added in revision %s",
1813 		   finfo->fullname, jrev2);
1814 
1815 	return;
1816     }
1817 
1818     /* If the two merge revisions are the same, then there is nothing
1819        to do.  */
1820     if (strcmp (rev1, rev2) == 0)
1821     {
1822 	free (rev1);
1823 	free (rev2);
1824 	return;
1825     }
1826 
1827     /* If there is no working file, then we can't do the merge.  */
1828     if (vers->vn_user == NULL)
1829     {
1830 	free (rev1);
1831 	free (rev2);
1832 
1833 	if (jdate2 != NULL)
1834 	    error (0, 0,
1835 		   "file %s is present in revision %s as of %s",
1836 		   finfo->fullname, jrev2, jdate2);
1837 	else
1838 	    error (0, 0,
1839 		   "file %s is present in revision %s",
1840 		   finfo->fullname, jrev2);
1841 
1842 	/* FIXME: Should we arrange to return a non-zero exit status?  */
1843 
1844 	return;
1845     }
1846 
1847 #ifdef SERVER_SUPPORT
1848     if (server_active && !isreadable (finfo->file))
1849     {
1850 	int retcode;
1851 	/* The file is up to date.  Need to check out the current contents.  */
1852 	retcode = RCS_checkout (vers->srcfile, finfo->file,
1853 				vers->vn_user, (char *) NULL,
1854 				(char *) NULL, RUN_TTY);
1855 	if (retcode != 0)
1856 	    error (1, retcode == -1 ? errno : 0,
1857 		   "failed to check out %s file", finfo->fullname);
1858     }
1859 #endif
1860 
1861     /*
1862      * The users currently modified file is moved to a backup file name
1863      * ".#filename.version", so that it will stay around for a few days
1864      * before being automatically removed by some cron daemon.  The "version"
1865      * is the version of the file that the user was most up-to-date with
1866      * before the merge.
1867      */
1868     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
1869 
1870     (void) unlink_file (backup);
1871     copy_file (finfo->file, backup);
1872     xchmod (finfo->file, 1);
1873 
1874     options = vers->options;
1875 #ifdef HAVE_RCS5
1876 #if 0
1877     if (*options == '\0')
1878 	options = "-kk";		/* to ignore keyword expansions */
1879 #endif
1880 #endif
1881 
1882     status = RCS_merge (vers->srcfile->path, options, rev1, rev2);
1883     if (status != 0 && status != 1)
1884     {
1885 	error (0, status == -1 ? errno : 0,
1886 	       "could not merge revision %s of %s", rev2, finfo->fullname);
1887 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1888 	       finfo->fullname, backup);
1889 	rename_file (backup, finfo->file);
1890     }
1891     free (rev1);
1892     free (rev2);
1893 
1894 #ifdef SERVER_SUPPORT
1895     /*
1896      * If we're in server mode, then we need to re-register the file
1897      * even if there were no conflicts (status == 0).
1898      * This tells server_updated() to send the modified file back to
1899      * the client.
1900      */
1901     if (status == 1 || (status == 0 && server_active))
1902 #else
1903     if (status == 1)
1904 #endif
1905     {
1906 	char *cp = 0;
1907 
1908 	if (status)
1909 	    cp = time_stamp (finfo->file);
1910 	Register (finfo->entries, finfo->file,
1911 		  vers->vn_rcs, vers->ts_rcs, vers->options,
1912 		  vers->tag, vers->date, cp);
1913 	if (cp)
1914 	    free(cp);
1915     }
1916 
1917 #ifdef SERVER_SUPPORT
1918     if (server_active)
1919     {
1920 	server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
1921 			  backup);
1922 	server_updated (finfo, vers, SERVER_MERGED,
1923 			(struct stat *) NULL, (unsigned char *) NULL);
1924     }
1925 #endif
1926 }
1927 
1928 int
1929 joining ()
1930 {
1931     return (join_rev1 != NULL);
1932 }
1933