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