xref: /openbsd-src/gnu/usr.bin/cvs/src/update.c (revision a5efd72c1ea322c16fcf4f56c4a28cfa28e9a0cc)
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     long file_size;
1164     FILE *e;
1165 
1166     *docheckout = 0;
1167 
1168     if (pipeout || joining ())
1169     {
1170 	*docheckout = 1;
1171 	return 0;
1172     }
1173 
1174     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file);
1175     if (isfile (file))
1176         rename_file (file, backup);
1177     else
1178         (void) unlink_file (backup);
1179 
1180     (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, file);
1181     (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, file);
1182 
1183     fail = 0;
1184 
1185     /* We need to check out both revisions first, to see if either one
1186        has a trailing newline.  Because of this, we don't use rcsdiff,
1187        but just use diff.  */
1188     if (noexec)
1189 	retcode = 0;
1190     else
1191 	retcode = RCS_checkout (vers_ts->srcfile->path, NULL,
1192 	                        vers_ts->vn_user,
1193 	                        vers_ts->options, file1, 0, 0);
1194     if (retcode != 0)
1195         fail = 1;
1196     else
1197     {
1198         e = fopen (file1, "r");
1199 	if (e == NULL)
1200 	    fail = 1;
1201 	else
1202 	{
1203 	    if (fseek (e, (long) -1, SEEK_END) == 0
1204 		&& getc (e) != '\n')
1205 	    {
1206 	        fail = 1;
1207 	    }
1208 	    fclose (e);
1209 	}
1210     }
1211 
1212     if (! fail)
1213     {
1214         /* Check it out into file, and then move to file2, so that we
1215            can get the right modes into *FILE_INFO.  We can't check it
1216            out directly into file2 because co doesn't understand how
1217            to do that.  */
1218 	retcode = RCS_checkout (vers_ts->srcfile->path, file,
1219 	                        vers_ts->vn_rcs,
1220 	                        vers_ts->options, RUN_TTY, 0, 0);
1221 	if (retcode != 0)
1222 	    fail = 1;
1223 	else
1224 	{
1225 	    if (!isreadable (file))
1226 	    {
1227 	        /* File is dead.  */
1228 	        fail = 1;
1229 	    }
1230 	    else
1231 	    {
1232 	        rename_file (file, file2);
1233 		if (cvswrite == TRUE
1234 		    && !fileattr_get (file, "_watched"))
1235 		    xchmod (file2, 1);
1236 		e = fopen (file2, "r");
1237 		if (e == NULL)
1238 		    fail = 1;
1239 		else
1240 		{
1241 		    struct MD5Context context;
1242 		    int nl;
1243 		    unsigned char buf[8192];
1244 		    unsigned len;
1245 
1246 		    nl = 0;
1247 
1248 		    /* Compute the MD5 checksum and make sure there is
1249                        a trailing newline.  */
1250 		    MD5Init (&context);
1251 		    while ((len = fread (buf, 1, sizeof buf, e)) != 0)
1252 		    {
1253 			nl = buf[len - 1] == '\n';
1254 		        MD5Update (&context, buf, len);
1255 		    }
1256 		    MD5Final (checksum, &context);
1257 
1258 		    if (ferror (e) || ! nl)
1259 		    {
1260 		        fail = 1;
1261 		    }
1262 
1263 		    fseek(e, 0L, SEEK_END);
1264 		    file_size = ftell(e);
1265 
1266 		    fclose (e);
1267 		}
1268 	    }
1269 	}
1270     }
1271 
1272     retcode = 0;
1273     if (! fail)
1274     {
1275 	/* FIXME: This whole thing with diff/patch is rather more
1276 	   convoluted than necessary (lots of forks and execs, need to
1277 	   worry about versions of diff and patch, etc.).  Also, we
1278 	   send context lines which aren't needed (in the rare case in
1279 	   which the diff doesn't apply, the checksum would catches it).
1280 	   Solution perhaps is to librarify the RCS routines which apply
1281 	   deltas or something equivalent.  */
1282 	/* This is -c, not -u, because we have no way of knowing which
1283 	   DIFF is in use.  */
1284 	run_setup ("%s -c %s %s", DIFF, file1, file2);
1285 
1286 	/* A retcode of 0 means no differences.  1 means some differences.  */
1287 	if ((retcode = run_exec (RUN_TTY, file, RUN_TTY, RUN_NORMAL)) != 0
1288 	    && retcode != 1)
1289 	{
1290 	    fail = 1;
1291 	}
1292 	else
1293 	{
1294 #define BINARY "Binary"
1295 	    char buf[sizeof BINARY];
1296 	    unsigned int c;
1297 
1298 	    /* Check the diff output to make sure patch will be handle it.  */
1299 	    e = fopen (file, "r");
1300 	    if (e == NULL)
1301 		error (1, errno, "could not open diff output file %s", file);
1302 	    c = fread (buf, 1, sizeof BINARY - 1, e);
1303 	    buf[c] = '\0';
1304 	    if (strcmp (buf, BINARY) == 0)
1305 	    {
1306 		/* These are binary files.  We could use diff -a, but
1307 		   patch can't handle that.  */
1308 		fail = 1;
1309 	    }
1310 	    else {
1311 		/*
1312 		 * Don't send a diff if just sending the entire file
1313 		 * would be smaller
1314 		 */
1315 		fseek(e, 0L, SEEK_END);
1316 		if (file_size < ftell(e))
1317 		    fail = 1;
1318 	    }
1319 
1320 	    fclose (e);
1321 	}
1322     }
1323 
1324     if (! fail)
1325     {
1326         Vers_TS *xvers_ts;
1327 
1328         /* This stuff is just copied blindly from checkout_file.  I
1329 	   don't really know what it does.  */
1330         xvers_ts = Version_TS (repository, options, tag, date, file,
1331 			       force_tag_match, 0, entries, srcfiles);
1332 	if (strcmp (xvers_ts->options, "-V4") == 0)
1333 	    xvers_ts->options[0] = '\0';
1334 
1335 	Register (entries, file, xvers_ts->vn_rcs,
1336 		  xvers_ts->ts_user, xvers_ts->options,
1337 		  xvers_ts->tag, xvers_ts->date, NULL);
1338 
1339 	if (stat (file2, file_info) < 0)
1340 	    error (1, errno, "could not stat %s", file2);
1341 
1342 	/* If this is really Update and not Checkout, recode history */
1343 	if (strcmp (command_name, "update") == 0)
1344 	    history_write ('P', update_dir, xvers_ts->vn_rcs, file,
1345 			   repository);
1346 
1347 	freevers_ts (&xvers_ts);
1348 
1349 	if (!really_quiet)
1350 	{
1351 	    write_letter (file, 'P', update_dir);
1352 	}
1353     }
1354     else
1355     {
1356 	int old_errno = errno;		/* save errno value over the rename */
1357 
1358 	if (isfile (backup))
1359 	    rename_file (backup, file);
1360 
1361 	if (retcode != 0 && retcode != 1)
1362 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1363 		   "could not diff %s", file);
1364 
1365 	*docheckout = 1;
1366 	retval = retcode;
1367     }
1368 
1369     (void) unlink_file (backup);
1370     (void) unlink_file (file1);
1371     (void) unlink_file (file2);
1372 
1373     return (retval);
1374 }
1375 #endif
1376 
1377 /*
1378  * Several of the types we process only print a bit of information consisting
1379  * of a single letter and the name.
1380  */
1381 static int
1382 write_letter (file, letter, update_dir)
1383     char *file;
1384     int letter;
1385     char *update_dir;
1386 {
1387     if (!really_quiet)
1388     {
1389 	char buf[2];
1390 	buf[0] = letter;
1391 	buf[1] = ' ';
1392 	cvs_output (buf, 2);
1393 	if (update_dir[0])
1394 	{
1395 	    cvs_output (update_dir, 0);
1396 	    cvs_output ("/", 1);
1397 	}
1398 	cvs_output (file, 0);
1399 	cvs_output ("\n", 1);
1400     }
1401     return (0);
1402 }
1403 
1404 /*
1405  * Do all the magic associated with a file which needs to be merged
1406  */
1407 static int
1408 merge_file (file, repository, entries, vers, update_dir)
1409     char *file;
1410     char *repository;
1411     List *entries;
1412     Vers_TS *vers;
1413     char *update_dir;
1414 {
1415     char user[PATH_MAX];
1416     char backup[PATH_MAX];
1417     int status;
1418     int retcode = 0;
1419 
1420     /*
1421      * The users currently modified file is moved to a backup file name
1422      * ".#filename.version", so that it will stay around for a few days
1423      * before being automatically removed by some cron daemon.  The "version"
1424      * is the version of the file that the user was most up-to-date with
1425      * before the merge.
1426      */
1427     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
1428     if (update_dir[0])
1429 	(void) sprintf (user, "%s/%s", update_dir, file);
1430     else
1431 	(void) strcpy (user, file);
1432 
1433     (void) unlink_file (backup);
1434     copy_file (file, backup);
1435     xchmod (file, 1);
1436 
1437     status = RCS_merge(vers->srcfile->path,
1438 		       vers->options, vers->vn_user, vers->vn_rcs);
1439     if (status != 0 && status != 1)
1440     {
1441 	error (0, status == -1 ? errno : 0,
1442 	       "could not merge revision %s of %s", vers->vn_user, user);
1443 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1444 	       user, backup);
1445 	rename_file (backup, file);
1446 	return (1);
1447     }
1448 
1449     if (strcmp (vers->options, "-V4") == 0)
1450 	vers->options[0] = '\0';
1451     (void) time (&last_register_time);
1452     {
1453 	char *cp = 0;
1454 
1455 	if (status)
1456 	    cp = time_stamp (file);
1457 	Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
1458 		  vers->tag, vers->date, cp);
1459 	if (cp)
1460 	    free (cp);
1461     }
1462 
1463     /* fix up the vers structure, in case it is used by join */
1464     if (join_rev1)
1465     {
1466 	if (vers->vn_user != NULL)
1467 	    free (vers->vn_user);
1468 	vers->vn_user = xstrdup (vers->vn_rcs);
1469     }
1470 
1471 #ifdef SERVER_SUPPORT
1472     /* Send the new contents of the file before the message.  If we
1473        wanted to be totally correct, we would have the client write
1474        the message only after the file has safely been written.  */
1475     if (server_active)
1476     {
1477         server_copy_file (file, update_dir, repository, backup);
1478 	server_updated (file, update_dir, repository, SERVER_MERGED,
1479 			(struct stat *) NULL, (unsigned char *) NULL);
1480     }
1481 #endif
1482 
1483     if (!noexec && !xcmp (backup, file))
1484     {
1485 	printf ("%s already contains the differences between %s and %s\n",
1486 		user, vers->vn_user, vers->vn_rcs);
1487 	history_write ('G', update_dir, vers->vn_rcs, file, repository);
1488 	return (0);
1489     }
1490 
1491     if (status == 1)
1492     {
1493 	if (!noexec)
1494 	    error (0, 0, "conflicts found in %s", user);
1495 
1496 	write_letter (file, 'C', update_dir);
1497 
1498 	history_write ('C', update_dir, vers->vn_rcs, file, repository);
1499 
1500     }
1501     else if (retcode == -1)
1502     {
1503 	error (1, errno, "fork failed while examining update of %s", user);
1504     }
1505     else
1506     {
1507 	write_letter (file, 'M', update_dir);
1508 	history_write ('G', update_dir, vers->vn_rcs, file, repository);
1509     }
1510     return (0);
1511 }
1512 
1513 /*
1514  * Do all the magic associated with a file which needs to be joined
1515  * (-j option)
1516  */
1517 static void
1518 #ifdef SERVER_SUPPORT
1519 join_file (file, srcfiles, vers, update_dir, entries, repository)
1520     char *repository;
1521 #else
1522 join_file (file, srcfiles, vers, update_dir, entries)
1523 #endif
1524     char *file;
1525     List *srcfiles;
1526     Vers_TS *vers;
1527     char *update_dir;
1528     List *entries;
1529 {
1530     char user[PATH_MAX];
1531     char backup[PATH_MAX];
1532     char *options;
1533     int status;
1534 
1535     char *rev1;
1536     char *rev2;
1537     char *jrev1;
1538     char *jrev2;
1539     char *jdate1;
1540     char *jdate2;
1541 
1542     jrev1 = join_rev1;
1543     jrev2 = join_rev2;
1544     jdate1 = date_rev1;
1545     jdate2 = date_rev2;
1546 
1547     if (wrap_merge_is_copy (file))
1548     {
1549 	/* FIXME: Should be including update_dir in message.  */
1550 	error (0, 0,
1551 	       "Cannot merge %s because it is a merge-by-copy file.", file);
1552 	return;
1553     }
1554 
1555     /* determine if we need to do anything at all */
1556     if (vers->srcfile == NULL ||
1557 	vers->srcfile->path == NULL)
1558     {
1559 	return;
1560     }
1561 
1562     /* in all cases, use two revs. */
1563 
1564     /* if only one rev is specified, it becomes the second rev */
1565     if (jrev2 == NULL)
1566     {
1567 	jrev2 = jrev1;
1568 	jrev1 = NULL;
1569 	jdate2 = jdate1;
1570 	jdate1 = NULL;
1571     }
1572 
1573     /* The file in the working directory doesn't exist in CVS/Entries.
1574        FIXME: Shouldn't this case result in additional processing (if
1575        the file was added going from rev1 to rev2, then do the equivalent
1576        of a "cvs add")?  (yes; easier said than done.. :-) */
1577     if (vers->vn_user == NULL)
1578     {
1579 	/* No merge possible YET. */
1580 	if (jdate2 != NULL)
1581 	    error (0, 0,
1582 		   "file %s is present in revision %s as of %s",
1583 		   file, jrev2, jdate2);
1584 	else
1585 	    error (0, 0,
1586 		   "file %s is present in revision %s",
1587 		   file, jrev2);
1588 	return;
1589     }
1590 
1591     /* Fix for bug CVS/193:
1592      * Used to dump core if the file had been removed on the current branch.
1593      */
1594     if (strcmp(vers->vn_user, "0") == 0)
1595     {
1596         error(0, 0,
1597               "file %s has been deleted",
1598               file);
1599         return;
1600     }
1601 
1602     /* convert the second rev spec, walking branches and dates. */
1603 
1604     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, 0);
1605     if (rev2 == NULL)
1606     {
1607 	if (!quiet)
1608 	{
1609 	    if (jdate2 != NULL)
1610 		error (0, 0,
1611 		       "cannot find revision %s as of %s in file %s",
1612 		       jrev2, jdate2, file);
1613 	    else
1614 		error (0, 0,
1615 		       "cannot find revision %s in file %s",
1616 		       jrev2, file);
1617 	}
1618 	return;
1619     }
1620 
1621     /* skip joining identical revs */
1622     if (strcmp (rev2, vers->vn_user) == 0)
1623     {
1624 	/* No merge necessary.  */
1625 	free (rev2);
1626 	return;
1627     }
1628 
1629     if (jrev1 == NULL)
1630     {
1631 	char *tst;
1632 	/* if the first rev is missing, then it is implied to be the
1633 	   greatest common ancestor of both the join rev, and the
1634 	   checked out rev. */
1635 
1636 	/* FIXME: What is this check for '!' about?  If it is legal to
1637 	   have '!' in the first character of vn_user, it isn't
1638 	   documented at struct vers_ts in cvs.h.  */
1639 	tst = vers->vn_user;
1640 	if (*tst == '!')
1641 	{
1642 	    /* file was dead.  merge anyway and pretend it's been
1643 	       added. */
1644 	    ++tst;
1645 	    Register (entries, file, "0", vers->ts_user, vers->options,
1646 		      vers->tag, (char *) 0, (char *) 0);
1647 	}
1648 	rev1 = gca (tst, rev2);
1649 	if (rev1 == NULL)
1650 	{
1651 	    /* this should not be possible */
1652 	    error (0, 0, "bad gca");
1653 	    abort();
1654 	}
1655 
1656 	tst = RCS_gettag (vers->srcfile, rev2, 1, 0);
1657 	if (tst == NULL)
1658 	{
1659 	    /* this should not be possible. */
1660 	    error (0, 0, "cannot find gca");
1661 	    abort();
1662 	}
1663 
1664 	free (tst);
1665 
1666 	/* these two cases are noops */
1667 	if (strcmp (rev1, rev2) == 0)
1668 	{
1669 	    free (rev1);
1670 	    free (rev2);
1671 	    return;
1672 	}
1673     }
1674     else
1675     {
1676 	/* otherwise, convert the first rev spec, walking branches and
1677 	   dates.  */
1678 
1679 	rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, 0);
1680 	if (rev1 == NULL)
1681 	{
1682 	  if (!quiet) {
1683 	    if (jdate1 != NULL)
1684 		error (0, 0,
1685 		       "cannot find revision %s as of %s in file %s",
1686 		       jrev1, jdate1, file);
1687 	    else
1688 		error (0, 0,
1689 		       "cannot find revision %s in file %s",
1690 		       jrev1, file);
1691 	  }
1692 	  return;
1693 	}
1694     }
1695 
1696     /* do the join */
1697 
1698 #if 0
1699     dome {
1700 	/* special handling when two revisions are specified */
1701 	if (join_rev1 && join_rev2)
1702 	{
1703 	    rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1, 0);
1704 	    if (rev == NULL)
1705 	    {
1706 		if (!quiet && date_rev2 == NULL)
1707 		    error (0, 0,
1708 			   "cannot find revision %s in file %s", join_rev2, file);
1709 		return;
1710 	    }
1711 
1712 	    baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
1713 	    if (baserev == NULL)
1714 	    {
1715 		if (!quiet && date_rev1 == NULL)
1716 		    error (0, 0,
1717 			   "cannot find revision %s in file %s", join_rev1, file);
1718 		free (rev);
1719 		return;
1720 	    }
1721 
1722 	    /*
1723 	     * nothing to do if:
1724 	     *	second revision matches our BASE revision (vn_user) &&
1725 	     *	both revisions are on the same branch
1726 	     */
1727 	    if (strcmp (vers->vn_user, rev) == 0 &&
1728 		numdots (baserev) == numdots (rev))
1729 	    {
1730 		/* might be the same branch.  take a real look */
1731 		char *dot = strrchr (baserev, '.');
1732 		int len = (dot - baserev) + 1;
1733 
1734 		if (strncmp (baserev, rev, len) == 0)
1735 		    return;
1736 	    }
1737 	}
1738 	else
1739 	{
1740 	    rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
1741 	    if (rev == NULL)
1742 		return;
1743 	    if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */
1744 	    {
1745 		free (rev);
1746 		return;
1747 	    }
1748 
1749 	    baserev = RCS_whatbranch (file, join_rev1, srcfiles);
1750 	    if (baserev)
1751 	    {
1752 		char *cp;
1753 
1754 		/* we get a branch -- turn it into a revision, or NULL if trunk */
1755 		if ((cp = strrchr (baserev, '.')) == NULL)
1756 		{
1757 		    free (baserev);
1758 		    baserev = (char *) NULL;
1759 		}
1760 		else
1761 		    *cp = '\0';
1762 	    }
1763 	}
1764 	if (baserev && strcmp (baserev, rev) == 0)
1765 	{
1766 	    /* they match -> nothing to do */
1767 	    free (rev);
1768 	    free (baserev);
1769 	    return;
1770 	}
1771     }
1772 #endif
1773 
1774     /* OK, so we have two revisions; continue on */
1775 
1776 #ifdef SERVER_SUPPORT
1777     if (server_active && !isreadable (file))
1778     {
1779 	int retcode;
1780 	/* The file is up to date.  Need to check out the current contents.  */
1781 	retcode = RCS_checkout (vers->srcfile->path, "", vers->vn_user, NULL,
1782 	                        RUN_TTY, 0, 0);
1783 	if (retcode != 0)
1784 	    error (1, retcode == -1 ? errno : 0,
1785 		   "failed to check out %s file", file);
1786     }
1787 #endif
1788 
1789     /*
1790      * The users currently modified file is moved to a backup file name
1791      * ".#filename.version", so that it will stay around for a few days
1792      * before being automatically removed by some cron daemon.  The "version"
1793      * is the version of the file that the user was most up-to-date with
1794      * before the merge.
1795      */
1796     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
1797     if (update_dir[0])
1798 	(void) sprintf (user, "%s/%s", update_dir, file);
1799     else
1800 	(void) strcpy (user, file);
1801 
1802     (void) unlink_file (backup);
1803     copy_file (file, backup);
1804     xchmod (file, 1);
1805 
1806     options = vers->options;
1807 #ifdef HAVE_RCS5
1808 #if 0
1809     if (*options == '\0')
1810 	options = "-kk";		/* to ignore keyword expansions */
1811 #endif
1812 #endif
1813 
1814     status = RCS_merge (vers->srcfile->path, options, rev1, rev2);
1815     if (status != 0 && status != 1)
1816     {
1817 	error (0, status == -1 ? errno : 0,
1818 	       "could not merge revision %s of %s", rev2, user);
1819 	error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1820 	       user, backup);
1821 	rename_file (backup, file);
1822     }
1823     free (rev1);
1824     free (rev2);
1825 
1826 #ifdef SERVER_SUPPORT
1827     /*
1828      * If we're in server mode, then we need to re-register the file
1829      * even if there were no conflicts (status == 0).
1830      * This tells server_updated() to send the modified file back to
1831      * the client.
1832      */
1833     if (status == 1 || (status == 0 && server_active))
1834 #else
1835     if (status == 1)
1836 #endif
1837     {
1838 	char *cp = 0;
1839 
1840 	if (status)
1841 	    cp = time_stamp (file);
1842 	Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
1843 		  vers->tag, vers->date, cp);
1844 	if (cp)
1845 	    free(cp);
1846     }
1847 
1848 #ifdef SERVER_SUPPORT
1849     if (server_active)
1850     {
1851 	server_copy_file (file, update_dir, repository, backup);
1852 	server_updated (file, update_dir, repository, SERVER_MERGED,
1853 			(struct stat *) NULL, (unsigned char *) NULL);
1854     }
1855 #endif
1856 }
1857 
1858 int
1859 joining ()
1860 {
1861     return (join_rev1 != NULL);
1862 }
1863