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