xref: /openbsd-src/gnu/usr.bin/cvs/src/update.c (revision c26070a5a87b8b908afc23542b77914040a7b4e9)
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 			  List *srcfiles, Vers_TS *vers_ts, char *update_dir));
46 #ifdef SERVER_SUPPORT
47 static int patch_file PROTO((char *file, char *repository, List *entries,
48 		       List *srcfiles, 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, List *srcfiles, Vers_TS *vers_ts,
65 		       char *update_dir, List *entries, char *repository));
66 #else
67 static void join_file PROTO((char *file, List *srcfiles, 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);
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);
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->srcfiles, &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->srcfiles,
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->srcfiles, 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 			    if (finfo->update_dir[0] == '\0')
553 				error (1, errno,
554 				"fork failed while examining conflict in `%s'",
555 				       finfo->file);
556 			    else
557 				error (1, errno,
558 			     "fork failed while examining conflict in `%s/%s'",
559 				       finfo->update_dir, finfo->file);
560 			}
561 		    }
562 		    if (!retcode)
563 		    {
564 			(void) write_letter (finfo->file, 'C', finfo->update_dir);
565 			retval = 1;
566 		    }
567 		    else
568 		    {
569 			/* Reregister to clear conflict flag. */
570 			Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_rcs,
571 				  vers->options, vers->tag,
572 				  vers->date, (char *)0);
573 		    }
574 		}
575 		if (!retval)
576 		    retval = write_letter (finfo->file, 'M', finfo->update_dir);
577 		break;
578 #ifdef SERVER_SUPPORT
579 	    case T_PATCH:		/* needs patch */
580 		if (patches)
581 		{
582 		    int docheckout;
583 		    struct stat file_info;
584 		    unsigned char checksum[16];
585 
586 		    retval = patch_file (finfo->file, finfo->repository, finfo->entries, finfo->srcfiles,
587 					 vers, finfo->update_dir, &docheckout,
588 					 &file_info, checksum);
589 		    if (! docheckout)
590 		    {
591 		        if (server_active && retval == 0)
592 			    server_updated (finfo->file, finfo->update_dir, finfo->repository,
593 					    SERVER_PATCHED, &file_info,
594 					    checksum);
595 			break;
596 		    }
597 		}
598 		/* Fall through.  */
599 		/* If we're not running as a server, just check the
600 		   file out.  It's simpler and faster than starting up
601 		   two new processes (diff and patch).  */
602 		/* Fall through.  */
603 #endif
604 	    case T_CHECKOUT:		/* needs checkout */
605 		retval = checkout_file (finfo->file, finfo->repository, finfo->entries, finfo->srcfiles,
606 					vers, finfo->update_dir);
607 #ifdef SERVER_SUPPORT
608 		if (server_active && retval == 0)
609 		    server_updated (finfo->file, finfo->update_dir, finfo->repository,
610 				    SERVER_UPDATED, (struct stat *) NULL,
611 				    (unsigned char *) NULL);
612 #endif
613 		break;
614 	    case T_ADDED:		/* added but not committed */
615 		retval = write_letter (finfo->file, 'A', finfo->update_dir);
616 		break;
617 	    case T_REMOVED:		/* removed but not committed */
618 		retval = write_letter (finfo->file, 'R', finfo->update_dir);
619 		break;
620 	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
621 		retval = scratch_file (finfo->file, finfo->repository, finfo->entries, finfo->update_dir);
622 #ifdef SERVER_SUPPORT
623 		if (server_active && retval == 0)
624 		    server_updated (finfo->file, finfo->update_dir, finfo->repository,
625 				    SERVER_UPDATED, (struct stat *) NULL,
626 				    (unsigned char *) NULL);
627 #endif
628 		break;
629 	    default:			/* can't ever happen :-) */
630 		error (0, 0,
631 		       "unknown file status %d for file %s", status, finfo->file);
632 		retval = 0;
633 		break;
634 	}
635     }
636 
637     /* only try to join if things have gone well thus far */
638     if (retval == 0 && join_rev1)
639 #ifdef SERVER_SUPPORT
640 	join_file (finfo->file, finfo->srcfiles, vers, finfo->update_dir, finfo->entries, finfo->repository);
641 #else
642 	join_file (finfo->file, finfo->srcfiles, vers, finfo->update_dir, finfo->entries);
643 #endif
644 
645     /* if this directory has an ignore list, add this file to it */
646     if (ignlist)
647     {
648 	Node *p;
649 
650 	p = getnode ();
651 	p->type = FILES;
652 	p->key = xstrdup (finfo->file);
653 	if (addnode (ignlist, p) != 0)
654 	    freenode (p);
655     }
656 
657     freevers_ts (&vers);
658     return (retval);
659 }
660 
661 static void update_ignproc PROTO ((char *, char *));
662 
663 static void
664 update_ignproc (file, dir)
665     char *file;
666     char *dir;
667 {
668     (void) write_letter (file, '?', dir);
669 }
670 
671 /* ARGSUSED */
672 static int
673 update_filesdone_proc (err, repository, update_dir)
674     int err;
675     char *repository;
676     char *update_dir;
677 {
678     /* if this directory has an ignore list, process it then free it */
679     if (ignlist)
680     {
681 	ignore_files (ignlist, update_dir, update_ignproc);
682 	dellist (&ignlist);
683     }
684 
685     /* Clean up CVS admin dirs if we are export */
686     if (strcmp (command_name, "export") == 0)
687     {
688 	/* I'm not sure the existence_error is actually possible (except
689 	   in cases where we really should print a message), but since
690 	   this code used to ignore all errors, I'll play it safe.  */
691 	if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
692 	    error (0, errno, "cannot remove %s directory", CVSADM);
693     }
694 #ifdef SERVER_SUPPORT
695     else if (!server_active && !pipeout)
696 #else
697     else if (!pipeout)
698 #endif /* SERVER_SUPPORT */
699     {
700         /* If there is no CVS/Root file, add one */
701         if (!isfile (CVSADM_ROOT))
702 	    Create_Root( (char *) NULL, CVSroot );
703     }
704 
705     return (err);
706 }
707 
708 /*
709  * update_dirent_proc () is called back by the recursion processor before a
710  * sub-directory is processed for update.  In this case, update_dirent proc
711  * will probably create the directory unless -d isn't specified and this is a
712  * new directory.  A return code of 0 indicates the directory should be
713  * processed by the recursion code.  A return of non-zero indicates the
714  * recursion code should skip this directory.
715  */
716 static Dtype
717 update_dirent_proc (dir, repository, update_dir)
718     char *dir;
719     char *repository;
720     char *update_dir;
721 {
722     if (ignore_directory (update_dir))
723       {
724 	/* print the warm fuzzy message */
725 	if (!quiet)
726 	  error (0, 0, "Ignoring %s", update_dir);
727         return R_SKIP_ALL;
728       }
729 
730     if (!isdir (dir))
731     {
732 	/* if we aren't building dirs, blow it off */
733 	if (!update_build_dirs)
734 	    return (R_SKIP_ALL);
735 
736 	if (noexec)
737 	{
738 	    /* ignore the missing dir if -n is specified */
739 	    error (0, 0, "New directory `%s' -- ignored", dir);
740 	    return (R_SKIP_ALL);
741 	}
742 	else
743 	{
744 	    /* otherwise, create the dir and appropriate adm files */
745 	    make_directory (dir);
746 	    Create_Admin (dir, update_dir, repository, tag, date);
747 	}
748     }
749     else
750     {
751 	char *cvsadmdir;
752 
753 	/* The directory exists.  Check to see if it has a CVS
754 	   subdirectory.  */
755 
756 	cvsadmdir = xmalloc (strlen (dir) + 80);
757 	strcpy (cvsadmdir, dir);
758 	strcat (cvsadmdir, "/");
759 	strcat (cvsadmdir, CVSADM);
760 
761 	if (!isdir (cvsadmdir))
762 	{
763 	    /* We cannot successfully recurse into a directory without a CVS
764 	       subdirectory.  Generally we will have already printed
765 	       "? foo".  */
766 	    free (cvsadmdir);
767 	    return R_SKIP_ALL;
768 	}
769 	free (cvsadmdir);
770     }
771 
772     /*
773      * If we are building dirs and not going to stdout, we make sure there is
774      * no static entries file and write the tag file as appropriate
775      */
776     if (!pipeout)
777     {
778 	if (update_build_dirs)
779 	{
780 	    char tmp[PATH_MAX];
781 
782 	    (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
783 	    if (unlink_file (tmp) < 0 && ! existence_error (errno))
784 		error (1, errno, "cannot remove file %s", tmp);
785 #ifdef SERVER_SUPPORT
786 	    if (server_active)
787 		server_clear_entstat (update_dir, repository);
788 #endif
789 	}
790 
791 	/* keep the CVS/Tag file current with the specified arguments */
792 	if (aflag || tag || date)
793 	{
794 	    WriteTag (dir, tag, date);
795 #ifdef SERVER_SUPPORT
796 	    if (server_active)
797 		server_set_sticky (update_dir, repository, tag, date);
798 #endif
799 	}
800 
801 	/* initialize the ignore list for this directory */
802 	ignlist = getlist ();
803     }
804 
805     /* print the warm fuzzy message */
806     if (!quiet)
807 	error (0, 0, "Updating %s", update_dir);
808 
809     return (R_PROCESS);
810 }
811 
812 /*
813  * update_dirleave_proc () is called back by the recursion code upon leaving
814  * a directory.  It will prune empty directories if needed and will execute
815  * any appropriate update programs.
816  */
817 /* ARGSUSED */
818 static int
819 update_dirleave_proc (dir, err, update_dir)
820     char *dir;
821     int err;
822     char *update_dir;
823 {
824     FILE *fp;
825 
826     /* run the update_prog if there is one */
827     if (err == 0 && !pipeout && !noexec &&
828 	(fp = fopen (CVSADM_UPROG, "r")) != NULL)
829     {
830 	char *cp;
831 	char *repository;
832 	char line[MAXLINELEN];
833 
834 	repository = Name_Repository ((char *) NULL, update_dir);
835 	if (fgets (line, sizeof (line), fp) != NULL)
836 	{
837 	    if ((cp = strrchr (line, '\n')) != NULL)
838 		*cp = '\0';
839 	    run_setup ("%s %s", line, repository);
840 	    (void) printf ("%s %s: Executing '", program_name, command_name);
841 	    run_print (stdout);
842 	    (void) printf ("'\n");
843 	    (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
844 	}
845 	(void) fclose (fp);
846 	free (repository);
847     }
848 
849     /* FIXME: chdir ("..") loses with symlinks.  */
850     /* Prune empty dirs on the way out - if necessary */
851     (void) chdir ("..");
852     if (update_prune_dirs && isemptydir (dir))
853     {
854 	/* I'm not sure the existence_error is actually possible (except
855 	   in cases where we really should print a message), but since
856 	   this code used to ignore all errors, I'll play it safe.  */
857 	if (unlink_file_dir (dir) < 0 && !existence_error (errno))
858 	    error (0, errno, "cannot remove %s directory", dir);
859     }
860 
861     return (err);
862 }
863 
864 /*
865  * Returns 1 if the argument directory is completely empty, other than the
866  * existence of the CVS directory entry.  Zero otherwise.
867  */
868 static int
869 isemptydir (dir)
870     char *dir;
871 {
872     DIR *dirp;
873     struct dirent *dp;
874 
875     if ((dirp = opendir (dir)) == NULL)
876     {
877 	error (0, 0, "cannot open directory %s for empty check", dir);
878 	return (0);
879     }
880     while ((dp = readdir (dirp)) != NULL)
881     {
882 	if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 &&
883 	    strcmp (dp->d_name, CVSADM) != 0)
884 	{
885 	    (void) closedir (dirp);
886 	    return (0);
887 	}
888     }
889     (void) closedir (dirp);
890     return (1);
891 }
892 
893 /*
894  * scratch the Entries file entry associated with a file
895  */
896 static int
897 scratch_file (file, repository, entries, update_dir)
898     char *file;
899     char *repository;
900     List *entries;
901     char *update_dir;
902 {
903     history_write ('W', update_dir, "", file, repository);
904     Scratch_Entry (entries, file);
905     (void) unlink_file (file);
906     return (0);
907 }
908 
909 /*
910  * check out a file - essentially returns the result of the fork on "co".
911  */
912 static int
913 checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
914     char *file;
915     char *repository;
916     List *entries;
917     List *srcfiles;
918     Vers_TS *vers_ts;
919     char *update_dir;
920 {
921     char backup[PATH_MAX];
922     int set_time, retval = 0;
923     int retcode = 0;
924     int status;
925     int file_is_dead;
926 
927     /* don't screw with backup files if we're going to stdout */
928     if (!pipeout)
929     {
930 	(void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file);
931 	if (isfile (file))
932 	    rename_file (file, backup);
933 	else
934 	    (void) unlink_file (backup);
935     }
936 
937     file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
938 
939     if (!file_is_dead)
940     {
941 	/*
942 	 * if we are checking out to stdout, print a nice message to
943 	 * stderr, and add the -p flag to the command */
944 	if (pipeout)
945 	{
946 	    if (!quiet)
947 	    {
948 		(void) fprintf (stderr, "\
949 ===================================================================\n");
950 		if (update_dir[0])
951 		    (void) fprintf (stderr, "Checking out %s/%s\n",
952 				    update_dir, file);
953 		else
954 		    (void) fprintf (stderr, "Checking out %s\n", file);
955 		(void) fprintf (stderr, "RCS:  %s\n", vers_ts->srcfile->path);
956 		(void) fprintf (stderr, "VERS: %s\n", vers_ts->vn_rcs);
957 		(void) fprintf (stderr, "***************\n");
958 	    }
959 	}
960 
961 	status = RCS_checkout (vers_ts->srcfile->path,
962 			       pipeout ? NULL : file, vers_ts->vn_tag,
963 			       vers_ts->options, RUN_TTY, 0, 0);
964     }
965     if (file_is_dead || status == 0)
966     {
967 	if (!pipeout)
968 	{
969 	    Vers_TS *xvers_ts;
970 	    int resurrecting;
971 
972 	    resurrecting = 0;
973 
974 	    if (file_is_dead && joining())
975 	    {
976 		if (RCS_getversion (vers_ts->srcfile, join_rev1,
977 				    date_rev1, 1, 0)
978 		    || (join_rev2 != NULL &&
979 			RCS_getversion (vers_ts->srcfile, join_rev2,
980 					date_rev2, 1, 0)))
981 		{
982 		    /* when joining, we need to get dead files checked
983 		       out.  Try harder.  */
984 		    /* I think that RCS_FLAGS_FORCE is here only because
985 		       passing -f to co used to enable checking out
986 		       a dead revision in the old version of death
987 		       support which used a hacked RCS instead of using
988 		       the RCS state.  */
989 		    retcode = RCS_checkout (vers_ts->srcfile->path, file,
990 		                            vers_ts->vn_rcs,
991 		                            vers_ts->options, RUN_TTY,
992 		                            RCS_FLAGS_FORCE, 0);
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 
1013 	    if (cvswrite == TRUE
1014 		&& !file_is_dead
1015 		&& !fileattr_get (file, "_watched"))
1016 		xchmod (file, 1);
1017 
1018 	    {
1019 		/* A newly checked out file is never under the spell
1020 		   of "cvs edit".  If we think we were editing it
1021 		   from a previous life, clean up.  Would be better to
1022 		   check for same the working directory instead of
1023 		   same user, but that is hairy.  */
1024 
1025 		struct addremove_args args;
1026 
1027 		editor_set (file, getcaller (), NULL);
1028 
1029 		memset (&args, 0, sizeof args);
1030 		args.remove_temp = 1;
1031 		watch_modify_watchers (file, &args);
1032 	    }
1033 
1034 	    /* set the time from the RCS file iff it was unknown before */
1035 	    if (vers_ts->vn_user == NULL ||
1036 		strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
1037 	    {
1038 		set_time = 1;
1039 	    }
1040 	    else
1041 		set_time = 0;
1042 
1043 	    wrap_fromcvs_process_file (file);
1044 
1045 	    xvers_ts = Version_TS (repository, options, tag, date, file,
1046 			      force_tag_match, set_time, entries, srcfiles);
1047 	    if (strcmp (xvers_ts->options, "-V4") == 0)
1048 		xvers_ts->options[0] = '\0';
1049 	    /* If no keyword expansion was specified on command line,
1050 	       use whatever was in the file.  This is how we tell the client
1051 	       whether a file is binary.  */
1052 	    if (xvers_ts->options[0] == '\0')
1053 	    {
1054 		if (vers_ts->srcfile->expand != NULL)
1055 		{
1056 		    free (xvers_ts->options);
1057 		    xvers_ts->options =
1058 			xmalloc (strlen (vers_ts->srcfile->expand) + 3);
1059 		    strcpy (xvers_ts->options, "-k");
1060 		    strcat (xvers_ts->options, vers_ts->srcfile->expand);
1061 		}
1062 	    }
1063 
1064 	    (void) time (&last_register_time);
1065 
1066 	    if (file_is_dead)
1067 	    {
1068 		if (xvers_ts->vn_user != NULL)
1069 		{
1070 		    if (update_dir[0] == '\0')
1071 			error (0, 0,
1072 			       "warning: %s is not (any longer) pertinent",
1073 			       file);
1074 		    else
1075 			error (0, 0,
1076 			       "warning: %s/%s is not (any longer) pertinent",
1077 			       update_dir, file);
1078 		}
1079 		Scratch_Entry (entries, file);
1080 		if (unlink_file (file) < 0 && ! existence_error (errno))
1081 		{
1082 		    if (update_dir[0] == '\0')
1083 			error (0, errno, "cannot remove %s", file);
1084 		    else
1085 			error (0, errno, "cannot remove %s/%s", update_dir,
1086 			       file);
1087 		}
1088 	    }
1089 	    else
1090 	      Register (entries, file,
1091 			resurrecting ? "0" : xvers_ts->vn_rcs,
1092 			xvers_ts->ts_user, xvers_ts->options,
1093 			xvers_ts->tag, xvers_ts->date,
1094 			(char *)0); /* Clear conflict flag on fresh checkout */
1095 
1096 	    /* fix up the vers structure, in case it is used by join */
1097 	    if (join_rev1)
1098 	    {
1099 		if (vers_ts->vn_user != NULL)
1100 		    free (vers_ts->vn_user);
1101 		if (vers_ts->vn_rcs != NULL)
1102 		    free (vers_ts->vn_rcs);
1103 		vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1104 		vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1105 	    }
1106 
1107 	    /* If this is really Update and not Checkout, recode history */
1108 	    if (strcmp (command_name, "update") == 0)
1109 		history_write ('U', update_dir, xvers_ts->vn_rcs, file,
1110 			       repository);
1111 
1112 	    freevers_ts (&xvers_ts);
1113 
1114 	    if (!really_quiet && !file_is_dead)
1115 	    {
1116 		write_letter (file, 'U', update_dir);
1117 	    }
1118 	}
1119     }
1120     else
1121     {
1122 	int old_errno = errno;		/* save errno value over the rename */
1123 
1124 	if (!pipeout && isfile (backup))
1125 	    rename_file (backup, file);
1126 
1127 	error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1128 	       "could not check out %s", file);
1129 
1130 	retval = retcode;
1131     }
1132 
1133     if (!pipeout)
1134 	(void) unlink_file (backup);
1135 
1136     return (retval);
1137 }
1138 
1139 #ifdef SERVER_SUPPORT
1140 /* Patch a file.  Runs rcsdiff.  This is only done when running as the
1141  * server.  The hope is that the diff will be smaller than the file
1142  * itself.
1143  */
1144 static int
1145 patch_file (file, repository, entries, srcfiles, vers_ts, update_dir,
1146 	    docheckout, file_info, checksum)
1147     char *file;
1148     char *repository;
1149     List *entries;
1150     List *srcfiles;
1151     Vers_TS *vers_ts;
1152     char *update_dir;
1153     int *docheckout;
1154     struct stat *file_info;
1155     unsigned char *checksum;
1156 {
1157     char backup[PATH_MAX];
1158     char file1[PATH_MAX];
1159     char file2[PATH_MAX];
1160     int retval = 0;
1161     int retcode = 0;
1162     int fail;
1163     FILE *e;
1164 
1165     *docheckout = 0;
1166 
1167     if (pipeout || joining ())
1168     {
1169 	*docheckout = 1;
1170 	return 0;
1171     }
1172 
1173     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file);
1174     if (isfile (file))
1175         rename_file (file, backup);
1176     else
1177         (void) unlink_file (backup);
1178 
1179     (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, file);
1180     (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, file);
1181 
1182     fail = 0;
1183 
1184     /* We need to check out both revisions first, to see if either one
1185        has a trailing newline.  Because of this, we don't use rcsdiff,
1186        but just use diff.  */
1187     if (noexec)
1188 	retcode = 0;
1189     else
1190 	retcode = RCS_checkout (vers_ts->srcfile->path, NULL,
1191 	                        vers_ts->vn_user,
1192 	                        vers_ts->options, file1, 0, 0);
1193     if (retcode != 0)
1194         fail = 1;
1195     else
1196     {
1197         e = fopen (file1, "r");
1198 	if (e == NULL)
1199 	    fail = 1;
1200 	else
1201 	{
1202 	    if (fseek (e, (long) -1, SEEK_END) == 0
1203 		&& getc (e) != '\n')
1204 	    {
1205 	        fail = 1;
1206 	    }
1207 	    fclose (e);
1208 	}
1209     }
1210 
1211     if (! fail)
1212     {
1213         /* Check it out into file, and then move to file2, so that we
1214            can get the right modes into *FILE_INFO.  We can't check it
1215            out directly into file2 because co doesn't understand how
1216            to do that.  */
1217 	retcode = RCS_checkout (vers_ts->srcfile->path, file,
1218 	                        vers_ts->vn_rcs,
1219 	                        vers_ts->options, RUN_TTY, 0, 0);
1220 	if (retcode != 0)
1221 	    fail = 1;
1222 	else
1223 	{
1224 	    if (!isreadable (file))
1225 	    {
1226 	        /* File is dead.  */
1227 	        fail = 1;
1228 	    }
1229 	    else
1230 	    {
1231 	        rename_file (file, file2);
1232 		if (cvswrite == TRUE
1233 		    && !fileattr_get (file, "_watched"))
1234 		    xchmod (file2, 1);
1235 		e = fopen (file2, "r");
1236 		if (e == NULL)
1237 		    fail = 1;
1238 		else
1239 		{
1240 		    struct MD5Context context;
1241 		    int nl;
1242 		    unsigned char buf[8192];
1243 		    unsigned len;
1244 
1245 		    nl = 0;
1246 
1247 		    /* Compute the MD5 checksum and make sure there is
1248                        a trailing newline.  */
1249 		    MD5Init (&context);
1250 		    while ((len = fread (buf, 1, sizeof buf, e)) != 0)
1251 		    {
1252 			nl = buf[len - 1] == '\n';
1253 		        MD5Update (&context, buf, len);
1254 		    }
1255 		    MD5Final (checksum, &context);
1256 
1257 		    if (ferror (e) || ! nl)
1258 		    {
1259 		        fail = 1;
1260 		    }
1261 
1262 		    fclose (e);
1263 		}
1264 	    }
1265 	}
1266     }
1267 
1268     retcode = 0;
1269     if (! fail)
1270     {
1271 	/* FIXME: This whole thing with diff/patch is rather more
1272 	   convoluted than necessary (lots of forks and execs, need to
1273 	   worry about versions of diff and patch, etc.).  Also, we
1274 	   send context lines which aren't needed (in the rare case in
1275 	   which the diff doesn't apply, the checksum would catches it).
1276 	   Solution perhaps is to librarify the RCS routines which apply
1277 	   deltas or something equivalent.  */
1278 	/* This is -c, not -u, because we have no way of knowing which
1279 	   DIFF is in use.  */
1280 	run_setup ("%s -c %s %s", DIFF, file1, file2);
1281 
1282 	/* A retcode of 0 means no differences.  1 means some differences.  */
1283 	if ((retcode = run_exec (RUN_TTY, file, RUN_TTY, RUN_NORMAL)) != 0
1284 	    && retcode != 1)
1285 	{
1286 	    fail = 1;
1287 	}
1288 	else
1289 	{
1290 #define BINARY "Binary"
1291 	    char buf[sizeof BINARY];
1292 	    unsigned int c;
1293 
1294 	    /* Check the diff output to make sure patch will be handle it.  */
1295 	    e = fopen (file, "r");
1296 	    if (e == NULL)
1297 		error (1, errno, "could not open diff output file %s", file);
1298 	    c = fread (buf, 1, sizeof BINARY - 1, e);
1299 	    buf[c] = '\0';
1300 	    if (strcmp (buf, BINARY) == 0)
1301 	    {
1302 		/* These are binary files.  We could use diff -a, but
1303 		   patch can't handle that.  */
1304 		fail = 1;
1305 	    }
1306 	    fclose (e);
1307 	}
1308     }
1309 
1310     if (! fail)
1311     {
1312         Vers_TS *xvers_ts;
1313 
1314         /* This stuff is just copied blindly from checkout_file.  I
1315 	   don't really know what it does.  */
1316         xvers_ts = Version_TS (repository, options, tag, date, file,
1317 			       force_tag_match, 0, entries, srcfiles);
1318 	if (strcmp (xvers_ts->options, "-V4") == 0)
1319 	    xvers_ts->options[0] = '\0';
1320 
1321 	Register (entries, file, xvers_ts->vn_rcs,
1322 		  xvers_ts->ts_user, xvers_ts->options,
1323 		  xvers_ts->tag, xvers_ts->date, NULL);
1324 
1325 	if (stat (file2, file_info) < 0)
1326 	    error (1, errno, "could not stat %s", file2);
1327 
1328 	/* If this is really Update and not Checkout, recode history */
1329 	if (strcmp (command_name, "update") == 0)
1330 	    history_write ('P', update_dir, xvers_ts->vn_rcs, file,
1331 			   repository);
1332 
1333 	freevers_ts (&xvers_ts);
1334 
1335 	if (!really_quiet)
1336 	{
1337 	    write_letter (file, 'P', update_dir);
1338 	}
1339     }
1340     else
1341     {
1342 	int old_errno = errno;		/* save errno value over the rename */
1343 
1344 	if (isfile (backup))
1345 	    rename_file (backup, file);
1346 
1347 	if (retcode != 0 && retcode != 1)
1348 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1349 		   "could not diff %s", file);
1350 
1351 	*docheckout = 1;
1352 	retval = retcode;
1353     }
1354 
1355     (void) unlink_file (backup);
1356     (void) unlink_file (file1);
1357     (void) unlink_file (file2);
1358 
1359     return (retval);
1360 }
1361 #endif
1362 
1363 /*
1364  * Several of the types we process only print a bit of information consisting
1365  * of a single letter and the name.
1366  */
1367 static int
1368 write_letter (file, letter, update_dir)
1369     char *file;
1370     int letter;
1371     char *update_dir;
1372 {
1373     if (!really_quiet)
1374     {
1375 	char buf[2];
1376 	buf[0] = letter;
1377 	buf[1] = ' ';
1378 	cvs_output (buf, 2);
1379 	if (update_dir[0])
1380 	{
1381 	    cvs_output (update_dir, 0);
1382 	    cvs_output ("/", 1);
1383 	}
1384 	cvs_output (file, 0);
1385 	cvs_output ("\n", 1);
1386     }
1387     return (0);
1388 }
1389 
1390 /*
1391  * Do all the magic associated with a file which needs to be merged
1392  */
1393 static int
1394 merge_file (file, repository, entries, vers, update_dir)
1395     char *file;
1396     char *repository;
1397     List *entries;
1398     Vers_TS *vers;
1399     char *update_dir;
1400 {
1401     char user[PATH_MAX];
1402     char backup[PATH_MAX];
1403     int status;
1404     int retcode = 0;
1405 
1406     /*
1407      * The users currently modified file is moved to a backup file name
1408      * ".#filename.version", so that it will stay around for a few days
1409      * before being automatically removed by some cron daemon.  The "version"
1410      * is the version of the file that the user was most up-to-date with
1411      * before the merge.
1412      */
1413     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
1414     if (update_dir[0])
1415 	(void) sprintf (user, "%s/%s", update_dir, file);
1416     else
1417 	(void) strcpy (user, file);
1418 
1419     (void) unlink_file (backup);
1420     copy_file (file, backup);
1421     xchmod (file, 1);
1422 
1423     status = RCS_merge(vers->srcfile->path,
1424 		       vers->options, vers->vn_user, vers->vn_rcs);
1425     if (status != 0 && status != 1)
1426     {
1427 	error (0, status == -1 ? errno : 0,
1428 	       "could not merge revision %s of %s", vers->vn_user, user);
1429 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1430 	       user, backup);
1431 	rename_file (backup, file);
1432 	return (1);
1433     }
1434 
1435     if (strcmp (vers->options, "-V4") == 0)
1436 	vers->options[0] = '\0';
1437     (void) time (&last_register_time);
1438     {
1439 	char *cp = 0;
1440 
1441 	if (status)
1442 	    cp = time_stamp (file);
1443 	Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
1444 		  vers->tag, vers->date, cp);
1445 	if (cp)
1446 	    free (cp);
1447     }
1448 
1449     /* fix up the vers structure, in case it is used by join */
1450     if (join_rev1)
1451     {
1452 	if (vers->vn_user != NULL)
1453 	    free (vers->vn_user);
1454 	vers->vn_user = xstrdup (vers->vn_rcs);
1455     }
1456 
1457 #ifdef SERVER_SUPPORT
1458     /* Send the new contents of the file before the message.  If we
1459        wanted to be totally correct, we would have the client write
1460        the message only after the file has safely been written.  */
1461     if (server_active)
1462     {
1463         server_copy_file (file, update_dir, repository, backup);
1464 	server_updated (file, update_dir, repository, SERVER_MERGED,
1465 			(struct stat *) NULL, (unsigned char *) NULL);
1466     }
1467 #endif
1468 
1469     if (!noexec && !xcmp (backup, file))
1470     {
1471 	printf ("%s already contains the differences between %s and %s\n",
1472 		user, vers->vn_user, vers->vn_rcs);
1473 	history_write ('G', update_dir, vers->vn_rcs, file, repository);
1474 	return (0);
1475     }
1476 
1477     if (status == 1)
1478     {
1479 	if (!noexec)
1480 	    error (0, 0, "conflicts found in %s", user);
1481 
1482 	write_letter (file, 'C', update_dir);
1483 
1484 	history_write ('C', update_dir, vers->vn_rcs, file, repository);
1485 
1486     }
1487     else if (retcode == -1)
1488     {
1489 	error (1, errno, "fork failed while examining update of %s", user);
1490     }
1491     else
1492     {
1493 	write_letter (file, 'M', update_dir);
1494 	history_write ('G', update_dir, vers->vn_rcs, file, repository);
1495     }
1496     return (0);
1497 }
1498 
1499 /*
1500  * Do all the magic associated with a file which needs to be joined
1501  * (-j option)
1502  */
1503 static void
1504 #ifdef SERVER_SUPPORT
1505 join_file (file, srcfiles, vers, update_dir, entries, repository)
1506     char *repository;
1507 #else
1508 join_file (file, srcfiles, vers, update_dir, entries)
1509 #endif
1510     char *file;
1511     List *srcfiles;
1512     Vers_TS *vers;
1513     char *update_dir;
1514     List *entries;
1515 {
1516     char user[PATH_MAX];
1517     char backup[PATH_MAX];
1518     char *options;
1519     int status;
1520 
1521     char *rev1;
1522     char *rev2;
1523     char *jrev1;
1524     char *jrev2;
1525     char *jdate1;
1526     char *jdate2;
1527 
1528     jrev1 = join_rev1;
1529     jrev2 = join_rev2;
1530     jdate1 = date_rev1;
1531     jdate2 = date_rev2;
1532 
1533     if (wrap_merge_is_copy (file))
1534     {
1535 	/* FIXME: Should be including update_dir in message.  */
1536 	error (0, 0,
1537 	       "Cannot merge %s because it is a merge-by-copy file.", file);
1538 	return;
1539     }
1540 
1541     /* determine if we need to do anything at all */
1542     if (vers->srcfile == NULL ||
1543 	vers->srcfile->path == NULL)
1544     {
1545 	return;
1546     }
1547 
1548     /* in all cases, use two revs. */
1549 
1550     /* if only one rev is specified, it becomes the second rev */
1551     if (jrev2 == NULL)
1552     {
1553 	jrev2 = jrev1;
1554 	jrev1 = NULL;
1555 	jdate2 = jdate1;
1556 	jdate1 = NULL;
1557     }
1558 
1559     /* The file in the working directory doesn't exist in CVS/Entries.
1560        FIXME: Shouldn't this case result in additional processing (if
1561        the file was added going from rev1 to rev2, then do the equivalent
1562        of a "cvs add")?  (yes; easier said than done.. :-) */
1563     if (vers->vn_user == NULL)
1564     {
1565 	/* No merge possible YET. */
1566 	if (jdate2 != NULL)
1567 	    error (0, 0,
1568 		   "file %s is present in revision %s as of %s",
1569 		   file, jrev2, jdate2);
1570 	else
1571 	    error (0, 0,
1572 		   "file %s is present in revision %s",
1573 		   file, jrev2);
1574 	return;
1575     }
1576 
1577     /* Fix for bug CVS/193:
1578      * Used to dump core if the file had been removed on the current branch.
1579      */
1580     if (strcmp(vers->vn_user, "0") == 0)
1581     {
1582         error(0, 0,
1583               "file %s has been deleted",
1584               file);
1585         return;
1586     }
1587 
1588     /* convert the second rev spec, walking branches and dates. */
1589 
1590     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, 0);
1591     if (rev2 == NULL)
1592     {
1593 	if (!quiet)
1594 	{
1595 	    if (jdate2 != NULL)
1596 		error (0, 0,
1597 		       "cannot find revision %s as of %s in file %s",
1598 		       jrev2, jdate2, file);
1599 	    else
1600 		error (0, 0,
1601 		       "cannot find revision %s in file %s",
1602 		       jrev2, file);
1603 	}
1604 	return;
1605     }
1606 
1607     /* skip joining identical revs */
1608     if (strcmp (rev2, vers->vn_user) == 0)
1609     {
1610 	/* No merge necessary.  */
1611 	free (rev2);
1612 	return;
1613     }
1614 
1615     if (jrev1 == NULL)
1616     {
1617 	char *tst;
1618 	/* if the first rev is missing, then it is implied to be the
1619 	   greatest common ancestor of both the join rev, and the
1620 	   checked out rev. */
1621 
1622 	/* FIXME: What is this check for '!' about?  If it is legal to
1623 	   have '!' in the first character of vn_user, it isn't
1624 	   documented at struct vers_ts in cvs.h.  */
1625 	tst = vers->vn_user;
1626 	if (*tst == '!')
1627 	{
1628 	    /* file was dead.  merge anyway and pretend it's been
1629 	       added. */
1630 	    ++tst;
1631 	    Register (entries, file, "0", vers->ts_user, vers->options,
1632 		      vers->tag, (char *) 0, (char *) 0);
1633 	}
1634 	rev1 = gca (tst, rev2);
1635 	if (rev1 == NULL)
1636 	{
1637 	    /* this should not be possible */
1638 	    error (0, 0, "bad gca");
1639 	    abort();
1640 	}
1641 
1642 	tst = RCS_gettag (vers->srcfile, rev2, 1, 0);
1643 	if (tst == NULL)
1644 	{
1645 	    /* this should not be possible. */
1646 	    error (0, 0, "cannot find gca");
1647 	    abort();
1648 	}
1649 
1650 	free (tst);
1651 
1652 	/* these two cases are noops */
1653 	if (strcmp (rev1, rev2) == 0)
1654 	{
1655 	    free (rev1);
1656 	    free (rev2);
1657 	    return;
1658 	}
1659     }
1660     else
1661     {
1662 	/* otherwise, convert the first rev spec, walking branches and
1663 	   dates.  */
1664 
1665 	rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, 0);
1666 	if (rev1 == NULL)
1667 	{
1668 	  if (!quiet) {
1669 	    if (jdate1 != NULL)
1670 		error (0, 0,
1671 		       "cannot find revision %s as of %s in file %s",
1672 		       jrev1, jdate1, file);
1673 	    else
1674 		error (0, 0,
1675 		       "cannot find revision %s in file %s",
1676 		       jrev1, file);
1677 	  }
1678 	  return;
1679 	}
1680     }
1681 
1682     /* do the join */
1683 
1684 #if 0
1685     dome {
1686 	/* special handling when two revisions are specified */
1687 	if (join_rev1 && join_rev2)
1688 	{
1689 	    rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1, 0);
1690 	    if (rev == NULL)
1691 	    {
1692 		if (!quiet && date_rev2 == NULL)
1693 		    error (0, 0,
1694 			   "cannot find revision %s in file %s", join_rev2, file);
1695 		return;
1696 	    }
1697 
1698 	    baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
1699 	    if (baserev == NULL)
1700 	    {
1701 		if (!quiet && date_rev1 == NULL)
1702 		    error (0, 0,
1703 			   "cannot find revision %s in file %s", join_rev1, file);
1704 		free (rev);
1705 		return;
1706 	    }
1707 
1708 	    /*
1709 	     * nothing to do if:
1710 	     *	second revision matches our BASE revision (vn_user) &&
1711 	     *	both revisions are on the same branch
1712 	     */
1713 	    if (strcmp (vers->vn_user, rev) == 0 &&
1714 		numdots (baserev) == numdots (rev))
1715 	    {
1716 		/* might be the same branch.  take a real look */
1717 		char *dot = strrchr (baserev, '.');
1718 		int len = (dot - baserev) + 1;
1719 
1720 		if (strncmp (baserev, rev, len) == 0)
1721 		    return;
1722 	    }
1723 	}
1724 	else
1725 	{
1726 	    rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
1727 	    if (rev == NULL)
1728 		return;
1729 	    if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */
1730 	    {
1731 		free (rev);
1732 		return;
1733 	    }
1734 
1735 	    baserev = RCS_whatbranch (file, join_rev1, srcfiles);
1736 	    if (baserev)
1737 	    {
1738 		char *cp;
1739 
1740 		/* we get a branch -- turn it into a revision, or NULL if trunk */
1741 		if ((cp = strrchr (baserev, '.')) == NULL)
1742 		{
1743 		    free (baserev);
1744 		    baserev = (char *) NULL;
1745 		}
1746 		else
1747 		    *cp = '\0';
1748 	    }
1749 	}
1750 	if (baserev && strcmp (baserev, rev) == 0)
1751 	{
1752 	    /* they match -> nothing to do */
1753 	    free (rev);
1754 	    free (baserev);
1755 	    return;
1756 	}
1757     }
1758 #endif
1759 
1760     /* OK, so we have two revisions; continue on */
1761 
1762 #ifdef SERVER_SUPPORT
1763     if (server_active && !isreadable (file))
1764     {
1765 	int retcode;
1766 	/* The file is up to date.  Need to check out the current contents.  */
1767 	retcode = RCS_checkout (vers->srcfile->path, "", vers->vn_user, NULL,
1768 	                        RUN_TTY, 0, 0);
1769 	if (retcode != 0)
1770 	    error (1, retcode == -1 ? errno : 0,
1771 		   "failed to check out %s file", file);
1772     }
1773 #endif
1774 
1775     /*
1776      * The users currently modified file is moved to a backup file name
1777      * ".#filename.version", so that it will stay around for a few days
1778      * before being automatically removed by some cron daemon.  The "version"
1779      * is the version of the file that the user was most up-to-date with
1780      * before the merge.
1781      */
1782     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
1783     if (update_dir[0])
1784 	(void) sprintf (user, "%s/%s", update_dir, file);
1785     else
1786 	(void) strcpy (user, file);
1787 
1788     (void) unlink_file (backup);
1789     copy_file (file, backup);
1790     xchmod (file, 1);
1791 
1792     options = vers->options;
1793 #ifdef HAVE_RCS5
1794 #if 0
1795     if (*options == '\0')
1796 	options = "-kk";		/* to ignore keyword expansions */
1797 #endif
1798 #endif
1799 
1800     status = RCS_merge (vers->srcfile->path, options, rev1, rev2);
1801     if (status != 0 && status != 1)
1802     {
1803 	error (0, status == -1 ? errno : 0,
1804 	       "could not merge revision %s of %s", rev2, user);
1805 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1806 	       user, backup);
1807 	rename_file (backup, file);
1808     }
1809     free (rev1);
1810     free (rev2);
1811 
1812 #ifdef SERVER_SUPPORT
1813     /*
1814      * If we're in server mode, then we need to re-register the file
1815      * even if there were no conflicts (status == 0).
1816      * This tells server_updated() to send the modified file back to
1817      * the client.
1818      */
1819     if (status == 1 || (status == 0 && server_active))
1820 #else
1821     if (status == 1)
1822 #endif
1823     {
1824 	char *cp = 0;
1825 
1826 	if (status)
1827 	    cp = time_stamp (file);
1828 	Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
1829 		  vers->tag, vers->date, cp);
1830 	if (cp)
1831 	    free(cp);
1832     }
1833 
1834 #ifdef SERVER_SUPPORT
1835     if (server_active)
1836     {
1837 	server_copy_file (file, update_dir, repository, backup);
1838 	server_updated (file, update_dir, repository, SERVER_MERGED,
1839 			(struct stat *) NULL, (unsigned char *) NULL);
1840     }
1841 #endif
1842 }
1843 
1844 int
1845 joining ()
1846 {
1847     return (join_rev1 != NULL);
1848 }
1849