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