PageRenderTime 103ms CodeModel.GetById 16ms app.highlight 71ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/cvs/src/update.c

https://bitbucket.org/freebsd/freebsd-head/
C | 3049 lines | 2026 code | 336 blank | 687 comment | 583 complexity | b0fed84b6bd263cbe9cf98c38fdca803 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
   3 *
   4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
   5 *                                  and others.
   6 *
   7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
   8 * Portions Copyright (C) 1989-1992, Brian Berliner
   9 *
  10 * You may distribute under the terms of the GNU General Public License as
  11 * specified in the README file that comes with the CVS source distribution.
  12 *
  13 * "update" updates the version in the present directory with respect to the RCS
  14 * repository.  The present version must have been created by "checkout". The
  15 * user can keep up-to-date by calling "update" whenever he feels like it.
  16 *
  17 * The present version can be committed by "commit", but this keeps the version
  18 * in tact.
  19 *
  20 * Arguments following the options are taken to be file names to be updated,
  21 * rather than updating the entire directory.
  22 *
  23 * Modified or non-existent RCS files are checked out and reported as U
  24 * <user_file>
  25 *
  26 * Modified user files are reported as M <user_file>.  If both the RCS file and
  27 * the user file have been modified, the user file is replaced by the result
  28 * of rcsmerge, and a backup file is written for the user in .#file.version.
  29 * If this throws up irreconcilable differences, the file is reported as C
  30 * <user_file>, and as M <user_file> otherwise.
  31 *
  32 * Files added but not yet committed are reported as A <user_file>. Files
  33 * removed but not yet committed are reported as R <user_file>.
  34 *
  35 * If the current directory contains subdirectories that hold concurrent
  36 * versions, these are updated too.  If the -d option was specified, new
  37 * directories added to the repository are automatically created and updated
  38 * as well.
  39 *
  40 * $FreeBSD$
  41 */
  42
  43#include "cvs.h"
  44#include <assert.h>
  45#include "savecwd.h"
  46#ifdef SERVER_SUPPORT
  47# include "md5.h"
  48#endif
  49#include "watch.h"
  50#include "fileattr.h"
  51#include "edit.h"
  52#include "getline.h"
  53#include "buffer.h"
  54#include "hardlink.h"
  55
  56static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts,
  57				 int adding, int merging, int update_server));
  58#ifdef SERVER_SUPPORT
  59static void checkout_to_buffer PROTO ((void *, const char *, size_t));
  60static int patch_file PROTO ((struct file_info *finfo,
  61			      Vers_TS *vers_ts, 
  62			      int *docheckout, struct stat *file_info,
  63			      unsigned char *checksum));
  64static void patch_file_write PROTO ((void *, const char *, size_t));
  65#endif /* SERVER_SUPPORT */
  66static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers));
  67static int scratch_file PROTO((struct file_info *finfo, Vers_TS *vers));
  68static Dtype update_dirent_proc PROTO ((void *callerdat, const char *dir,
  69                                        const char *repository,
  70                                        const char *update_dir,
  71                                        List *entries));
  72static int update_dirleave_proc PROTO ((void *callerdat, const char *dir,
  73					int err, const char *update_dir,
  74					List *entries));
  75static int update_fileproc PROTO ((void *callerdat, struct file_info *));
  76static int update_filesdone_proc PROTO ((void *callerdat, int err,
  77                                         const char *repository,
  78                                         const char *update_dir,
  79                                         List *entries));
  80#ifdef PRESERVE_PERMISSIONS_SUPPORT
  81static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *));
  82#endif
  83static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
  84
  85static char *options = NULL;
  86static char *tag = NULL;
  87static char *date = NULL;
  88/* This is a bit of a kludge.  We call WriteTag at the beginning
  89   before we know whether nonbranch is set or not.  And then at the
  90   end, once we have the right value for nonbranch, we call WriteTag
  91   again.  I don't know whether the first call is necessary or not.
  92   rewrite_tag is nonzero if we are going to have to make that second
  93   call.  */
  94static int rewrite_tag;
  95static int nonbranch;
  96
  97/* If we set the tag or date for a subdirectory, we use this to undo
  98   the setting.  See update_dirent_proc.  */
  99static char *tag_update_dir;
 100
 101static char *join_rev1, *date_rev1;
 102static char *join_rev2, *date_rev2;
 103static int aflag = 0;
 104static int toss_local_changes = 0;
 105static int force_tag_match = 1;
 106static int pull_template = 0;
 107static int update_build_dirs = 0;
 108static int update_prune_dirs = 0;
 109static int pipeout = 0;
 110#ifdef SERVER_SUPPORT
 111static int patches = 0;
 112static int rcs_diff_patches = 0;
 113#endif
 114static List *ignlist = (List *) NULL;
 115static time_t last_register_time;
 116static const char *const update_usage[] =
 117{
 118    "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n",
 119    "    [-I ign] [-W spec] [files...]\n",
 120    "\t-A\tReset any sticky tags/date/kopts.\n",
 121    "\t-P\tPrune empty directories.\n",
 122    "\t-C\tOverwrite locally modified files with clean repository copies.\n",
 123    "\t-d\tBuild directories, like checkout does.\n",
 124    "\t-f\tForce a head revision match if tag/date not found.\n",
 125    "\t-l\tLocal directory only, no recursion.\n",
 126    "\t-R\tProcess directories recursively.\n",
 127    "\t-p\tSend updates to standard output (avoids stickiness).\n",
 128    "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
 129    "\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
 130    "\t-D date\tSet date to update from (is sticky).\n",
 131    "\t-j rev\tMerge in changes made between current revision and rev.\n",
 132    "\t-I ign\tMore files to ignore (! to reset).\n",
 133    "\t-W spec\tWrappers specification line.\n",
 134    "\t-T\tCreate CVS/Template.\n",
 135    "(Specify the --help global option for a list of other help options)\n",
 136    NULL
 137};
 138
 139/*
 140 * update is the argv,argc based front end for arg parsing
 141 */
 142int
 143update (argc, argv)
 144    int argc;
 145    char **argv;
 146{
 147    int c, err;
 148    int local = 0;			/* recursive by default */
 149    int which;				/* where to look for files and dirs */
 150    int xpull_template = 0;
 151
 152    if (argc == -1)
 153	usage (update_usage);
 154
 155    ign_setup ();
 156    wrap_setup ();
 157
 158    /* parse the args */
 159    optind = 0;
 160    while ((c = getopt (argc, argv, "+ApCPflRQTqduk:r:D:j:I:W:")) != -1)
 161    {
 162	switch (c)
 163	{
 164	    case 'A':
 165		aflag = 1;
 166		break;
 167	    case 'C':
 168		toss_local_changes = 1;
 169		break;
 170	    case 'I':
 171		ign_add (optarg, 0);
 172		break;
 173	    case 'W':
 174		wrap_add (optarg, 0);
 175		break;
 176	    case 'k':
 177		if (options)
 178		    free (options);
 179		options = RCS_check_kflag (optarg);
 180		break;
 181	    case 'l':
 182		local = 1;
 183		break;
 184	    case 'R':
 185		local = 0;
 186		break;
 187	    case 'Q':
 188	    case 'q':
 189		/* The CVS 1.5 client sends these options (in addition to
 190		   Global_option requests), so we must ignore them.  */
 191		if (!server_active)
 192		    error (1, 0,
 193			   "-q or -Q must be specified before \"%s\"",
 194			   cvs_cmd_name);
 195		break;
 196	    case 'T':
 197		xpull_template = 1;
 198		break;
 199	    case 'd':
 200		update_build_dirs = 1;
 201		break;
 202	    case 'f':
 203		force_tag_match = 0;
 204		break;
 205	    case 'r':
 206		tag = optarg;
 207		break;
 208	    case 'D':
 209		if (date) free (date);
 210		date = Make_Date (optarg);
 211		break;
 212	    case 'P':
 213		update_prune_dirs = 1;
 214		break;
 215	    case 'p':
 216		pipeout = 1;
 217		noexec = 1;		/* so no locks will be created */
 218		break;
 219	    case 'j':
 220		if (join_rev2)
 221		    error (1, 0, "only two -j options can be specified");
 222		if (join_rev1)
 223		    join_rev2 = optarg;
 224		else
 225		    join_rev1 = optarg;
 226		break;
 227	    case 'u':
 228#ifdef SERVER_SUPPORT
 229		if (server_active)
 230		{
 231		    patches = 1;
 232		    rcs_diff_patches = server_use_rcs_diff ();
 233		}
 234		else
 235#endif
 236		    usage (update_usage);
 237		break;
 238	    case '?':
 239	    default:
 240		usage (update_usage);
 241		break;
 242	}
 243    }
 244    argc -= optind;
 245    argv += optind;
 246
 247#ifdef CLIENT_SUPPORT
 248    if (current_parsed_root->isremote) 
 249    {
 250	int pass;
 251
 252	/* The first pass does the regular update.  If we receive at least
 253	   one patch which failed, we do a second pass and just fetch
 254	   those files whose patches failed.  */
 255	pass = 1;
 256	do
 257	{
 258	    int status;
 259
 260	    start_server ();
 261
 262	    if (local)
 263		send_arg("-l");
 264	    if (update_build_dirs)
 265		send_arg("-d");
 266	    if (pipeout)
 267		send_arg("-p");
 268	    if (!force_tag_match)
 269		send_arg("-f");
 270	    if (aflag)
 271		send_arg("-A");
 272	    if (toss_local_changes)
 273		send_arg("-C");
 274	    if (update_prune_dirs)
 275		send_arg("-P");
 276	    client_prune_dirs = update_prune_dirs;
 277	    option_with_arg ("-r", tag);
 278	    if (options && options[0] != '\0')
 279		send_arg (options);
 280	    if (date)
 281		client_senddate (date);
 282	    if (join_rev1)
 283		option_with_arg ("-j", join_rev1);
 284	    if (join_rev2)
 285		option_with_arg ("-j", join_rev2);
 286	    wrap_send ();
 287
 288	    if (failed_patches_count == 0)
 289	    {
 290                unsigned int flags = 0;
 291
 292		/* If the server supports the command "update-patches", that 
 293		   means that it knows how to handle the -u argument to update,
 294		   which means to send patches instead of complete files.
 295
 296		   We don't send -u if failed_patches != NULL, so that the
 297		   server doesn't try to send patches which will just fail
 298		   again.  At least currently, the client also clobbers the
 299		   file and tells the server it is lost, which also will get
 300		   a full file instead of a patch, but it seems clean to omit
 301		   -u.  */
 302		if (supported_request ("update-patches"))
 303		    send_arg ("-u");
 304
 305		send_arg ("--");
 306
 307                if (update_build_dirs)
 308                    flags |= SEND_BUILD_DIRS;
 309
 310                if (toss_local_changes) {
 311                    flags |= SEND_NO_CONTENTS;
 312                    flags |= BACKUP_MODIFIED_FILES;
 313                }
 314
 315		/* If noexec, probably could be setting SEND_NO_CONTENTS.
 316		   Same caveats as for "cvs status" apply.  */
 317
 318		send_files (argc, argv, local, aflag, flags);
 319		send_file_names (argc, argv, SEND_EXPAND_WILD);
 320	    }
 321	    else
 322	    {
 323		int i;
 324
 325		(void) printf ("%s client: refetching unpatchable files\n",
 326			       program_name);
 327
 328		if (toplevel_wd != NULL
 329		    && CVS_CHDIR (toplevel_wd) < 0)
 330		{
 331		    error (1, errno, "could not chdir to %s", toplevel_wd);
 332		}
 333
 334		send_arg ("--");
 335
 336		for (i = 0; i < failed_patches_count; i++)
 337		    if (unlink_file (failed_patches[i]) < 0
 338			&& !existence_error (errno))
 339			error (0, errno, "cannot remove %s",
 340			       failed_patches[i]);
 341		send_files (failed_patches_count, failed_patches, local,
 342			    aflag, update_build_dirs ? SEND_BUILD_DIRS : 0);
 343		send_file_names (failed_patches_count, failed_patches, 0);
 344		free_names (&failed_patches_count, failed_patches);
 345	    }
 346
 347	    send_to_server ("update\012", 0);
 348
 349	    status = get_responses_and_close ();
 350
 351	    /* If there are any conflicts, the server will return a
 352               non-zero exit status.  If any patches failed, we still
 353               want to run the update again.  We use a pass count to
 354               avoid an endless loop.  */
 355
 356	    /* Notes: (1) assuming that status != 0 implies a
 357	       potential conflict is the best we can cleanly do given
 358	       the current protocol.  I suppose that trying to
 359	       re-fetch in cases where there was a more serious error
 360	       is probably more or less harmless, but it isn't really
 361	       ideal.  (2) it would be nice to have a testsuite case for the
 362	       conflict-and-patch-failed case.  */
 363
 364	    if (status != 0
 365		&& (failed_patches_count == 0 || pass > 1))
 366	    {
 367		if (failed_patches_count > 0)
 368		    free_names (&failed_patches_count, failed_patches);
 369		return status;
 370	    }
 371
 372	    ++pass;
 373	} while (failed_patches_count > 0);
 374
 375	return 0;
 376    }
 377#endif
 378
 379    if (tag != NULL)
 380	tag_check_valid (tag, argc, argv, local, aflag, "");
 381    if (join_rev1 != NULL)
 382        tag_check_valid_join (join_rev1, argc, argv, local, aflag, "");
 383    if (join_rev2 != NULL)
 384        tag_check_valid_join (join_rev2, argc, argv, local, aflag, "");
 385
 386    /*
 387     * If we are updating the entire directory (for real) and building dirs
 388     * as we go, we make sure there is no static entries file and write the
 389     * tag file as appropriate
 390     */
 391    if (argc <= 0 && !pipeout)
 392    {
 393	if (update_build_dirs)
 394	{
 395	    if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
 396		error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
 397#ifdef SERVER_SUPPORT
 398	    if (server_active)
 399	    {
 400		char *repos = Name_Repository (NULL, NULL);
 401		server_clear_entstat (".", repos);
 402		free (repos);
 403	    }
 404#endif
 405	}
 406
 407	/* keep the CVS/Tag file current with the specified arguments */
 408	if (aflag || tag || date)
 409	{
 410	    char *repos = Name_Repository (NULL, NULL);
 411	    WriteTag ((char *) NULL, tag, date, 0, ".", repos);
 412	    free (repos);
 413	    rewrite_tag = 1;
 414	    nonbranch = 0;
 415	}
 416    }
 417
 418    /* look for files/dirs locally and in the repository */
 419    which = W_LOCAL | W_REPOS;
 420
 421    /* look in the attic too if a tag or date is specified */
 422    if (tag != NULL || date != NULL || joining())
 423	which |= W_ATTIC;
 424
 425    /* call the command line interface */
 426    err = do_update (argc, argv, options, tag, date, force_tag_match,
 427		     local, update_build_dirs, aflag, update_prune_dirs,
 428		     pipeout, which, join_rev1, join_rev2, (char *) NULL,
 429		     xpull_template, (char *) NULL);
 430
 431    /* free the space Make_Date allocated if necessary */
 432    if (date != NULL)
 433	free (date);
 434
 435    return err;
 436}
 437
 438
 439
 440/*
 441 * Command line interface to update (used by checkout)
 442 */
 443int
 444do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
 445	   xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir,
 446	   xpull_template, repository)
 447    int argc;
 448    char **argv;
 449    char *xoptions;
 450    char *xtag;
 451    char *xdate;
 452    int xforce;
 453    int local;
 454    int xbuild;
 455    int xaflag;
 456    int xprune;
 457    int xpipeout;
 458    int which;
 459    char *xjoin_rev1;
 460    char *xjoin_rev2;
 461    char *preload_update_dir;
 462    int xpull_template;
 463    char *repository;
 464{
 465    int err = 0;
 466    char *cp;
 467
 468    /* fill in the statics */
 469    options = xoptions;
 470    tag = xtag;
 471    date = xdate;
 472    force_tag_match = xforce;
 473    update_build_dirs = xbuild;
 474    aflag = xaflag;
 475    update_prune_dirs = xprune;
 476    pipeout = xpipeout;
 477    pull_template = xpull_template;
 478
 479    /* setup the join support */
 480    join_rev1 = xjoin_rev1;
 481    join_rev2 = xjoin_rev2;
 482    if (join_rev1 && (cp = strchr (join_rev1, ':')) != NULL)
 483    {
 484	*cp++ = '\0';
 485	date_rev1 = Make_Date (cp);
 486    }
 487    else
 488	date_rev1 = (char *) NULL;
 489    if (join_rev2 && (cp = strchr (join_rev2, ':')) != NULL)
 490    {
 491	*cp++ = '\0';
 492	date_rev2 = Make_Date (cp);
 493    }
 494    else
 495	date_rev2 = (char *) NULL;
 496
 497#ifdef PRESERVE_PERMISSIONS_SUPPORT
 498    if (preserve_perms)
 499    {
 500	/* We need to do an extra recursion, bleah.  It's to make sure
 501	   that we know as much as possible about file linkage. */
 502	hardlist = getlist();
 503	working_dir = xgetwd();		/* save top-level working dir */
 504
 505	/* FIXME-twp: the arguments to start_recursion make me dizzy.  This
 506	   function call was copied from the update_fileproc call that
 507	   follows it; someone should make sure that I did it right. */
 508	err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
 509			       (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
 510			       argc, argv, local, which, aflag, CVS_LOCK_READ,
 511			       preload_update_dir, 1, (char *) NULL);
 512	if (err)
 513	    return err;
 514
 515	/* FIXME-twp: at this point we should walk the hardlist
 516	   and update the `links' field of each hardlink_info struct
 517	   to list the files that are linked on dist.  That would make
 518	   it easier & more efficient to compare the disk linkage with
 519	   the repository linkage (a simple strcmp). */
 520    }
 521#endif
 522
 523    /* call the recursion processor */
 524    err = start_recursion (update_fileproc, update_filesdone_proc,
 525			   update_dirent_proc, update_dirleave_proc, NULL,
 526			   argc, argv, local, which, aflag, CVS_LOCK_READ,
 527			   preload_update_dir, 1, repository);
 528
 529    /* see if we need to sleep before returning to avoid time-stamp races */
 530    if (!server_active && last_register_time)
 531    {
 532	sleep_past (last_register_time);
 533    }
 534
 535    return err;
 536}
 537
 538#ifdef PRESERVE_PERMISSIONS_SUPPORT
 539/*
 540 * The get_linkinfo_proc callback adds each file to the hardlist
 541 * (see hardlink.c).
 542 */
 543
 544static int
 545get_linkinfo_proc (callerdat, finfo)
 546    void *callerdat;
 547    struct file_info *finfo;
 548{
 549    char *fullpath;
 550    Node *linkp;
 551    struct hardlink_info *hlinfo;
 552
 553    /* Get the full pathname of the current file. */
 554    fullpath = xmalloc (strlen(working_dir) +
 555			strlen(finfo->fullname) + 2);
 556    sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
 557
 558    /* To permit recursing into subdirectories, files
 559       are keyed on the full pathname and not on the basename. */
 560    linkp = lookup_file_by_inode (fullpath);
 561    if (linkp == NULL)
 562    {
 563	/* The file isn't on disk; we are probably restoring
 564	   a file that was removed. */
 565	return 0;
 566    }
 567    
 568    /* Create a new, empty hardlink_info node. */
 569    hlinfo = (struct hardlink_info *)
 570	xmalloc (sizeof (struct hardlink_info));
 571
 572    hlinfo->status = (Ctype) 0;	/* is this dumb? */
 573    hlinfo->checked_out = 0;
 574
 575    linkp->data = hlinfo;
 576
 577    return 0;
 578}
 579#endif
 580
 581
 582
 583/*
 584 * This is the callback proc for update.  It is called for each file in each
 585 * directory by the recursion code.  The current directory is the local
 586 * instantiation.  file is the file name we are to operate on. update_dir is
 587 * set to the path relative to where we started (for pretty printing).
 588 * repository is the repository. entries and srcfiles are the pre-parsed
 589 * entries and source control files.
 590 * 
 591 * This routine decides what needs to be done for each file and does the
 592 * appropriate magic for checkout
 593 */
 594static int
 595update_fileproc (callerdat, finfo)
 596    void *callerdat;
 597    struct file_info *finfo;
 598{
 599    int retval;
 600    Ctype status;
 601    Vers_TS *vers;
 602
 603    status = Classify_File (finfo, tag, date, options, force_tag_match,
 604			    aflag, &vers, pipeout);
 605
 606    /* Keep track of whether TAG is a branch tag.
 607       Note that if it is a branch tag in some files and a nonbranch tag
 608       in others, treat it as a nonbranch tag.  It is possible that case
 609       should elicit a warning or an error.  */
 610    if (rewrite_tag
 611	&& tag != NULL
 612	&& finfo->rcs != NULL)
 613    {
 614	char *rev = RCS_getversion (finfo->rcs, tag, date, 1, NULL);
 615	if (rev != NULL
 616	    && !RCS_nodeisbranch (finfo->rcs, tag))
 617	    nonbranch = 1;
 618	if (rev != NULL)
 619	    free (rev);
 620    }
 621
 622    if (pipeout)
 623    {
 624	/*
 625	 * We just return success without doing anything if any of the really
 626	 * funky cases occur
 627	 * 
 628	 * If there is still a valid RCS file, do a regular checkout type
 629	 * operation
 630	 */
 631	switch (status)
 632	{
 633	    case T_UNKNOWN:		/* unknown file was explicitly asked
 634					 * about */
 635	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
 636	    case T_ADDED:		/* added but not committed */
 637		retval = 0;
 638		break;
 639	    case T_CONFLICT:		/* old punt-type errors */
 640		retval = 1;
 641		break;
 642	    case T_UPTODATE:		/* file was already up-to-date */
 643	    case T_NEEDS_MERGE:		/* needs merging */
 644	    case T_MODIFIED:		/* locally modified */
 645	    case T_REMOVED:		/* removed but not committed */
 646	    case T_CHECKOUT:		/* needs checkout */
 647	    case T_PATCH:		/* needs patch */
 648		retval = checkout_file (finfo, vers, 0, 0, 0);
 649		break;
 650
 651	    default:			/* can't ever happen :-) */
 652		error (0, 0,
 653		       "unknown file status %d for file %s", status, finfo->file);
 654		retval = 0;
 655		break;
 656	}
 657    }
 658    else
 659    {
 660	switch (status)
 661	{
 662	    case T_UNKNOWN:		/* unknown file was explicitly asked
 663					 * about */
 664	    case T_UPTODATE:		/* file was already up-to-date */
 665		retval = 0;
 666		break;
 667	    case T_CONFLICT:		/* old punt-type errors */
 668		retval = 1;
 669		write_letter (finfo, 'C');
 670		break;
 671	    case T_NEEDS_MERGE:		/* needs merging */
 672		if (! toss_local_changes)
 673		{
 674		    retval = merge_file (finfo, vers);
 675		    break;
 676		}
 677		/* else FALL THROUGH */
 678	    case T_MODIFIED:		/* locally modified */
 679		retval = 0;
 680                if (toss_local_changes)
 681                {
 682                    char *bakname;
 683                    bakname = backup_file (finfo->file, vers->vn_user);
 684                    /* This behavior is sufficiently unexpected to
 685                       justify overinformativeness, I think. */
 686                    if (!really_quiet && !server_active)
 687                        (void) printf ("(Locally modified %s moved to %s)\n",
 688                                       finfo->file, bakname);
 689                    free (bakname);
 690
 691                    /* The locally modified file is still present, but
 692                       it will be overwritten by the repository copy
 693                       after this. */
 694                    status = T_CHECKOUT;
 695                    retval = checkout_file (finfo, vers, 0, 0, 1);
 696                }
 697                else 
 698                {
 699                    if (vers->ts_conflict)
 700                    {
 701			if (file_has_markers (finfo))
 702                        {
 703                            write_letter (finfo, 'C');
 704                            retval = 1;
 705                        }
 706                        else
 707                        {
 708                            /* Reregister to clear conflict flag. */
 709                            Register (finfo->entries, finfo->file, 
 710                                      vers->vn_rcs, vers->ts_rcs,
 711                                      vers->options, vers->tag,
 712                                      vers->date, (char *)0);
 713                        }
 714                    }
 715                    if (!retval)
 716                        write_letter (finfo, 'M');
 717                }
 718		break;
 719	    case T_PATCH:		/* needs patch */
 720#ifdef SERVER_SUPPORT
 721		if (patches)
 722		{
 723		    int docheckout;
 724		    struct stat file_info;
 725		    unsigned char checksum[16];
 726
 727		    retval = patch_file (finfo,
 728					 vers, &docheckout,
 729					 &file_info, checksum);
 730		    if (! docheckout)
 731		    {
 732		        if (server_active && retval == 0)
 733			    server_updated (finfo, vers,
 734					    (rcs_diff_patches
 735					     ? SERVER_RCS_DIFF
 736					     : SERVER_PATCHED),
 737					    file_info.st_mode, checksum,
 738					    (struct buffer *) NULL);
 739			break;
 740		    }
 741		}
 742#endif
 743		/* If we're not running as a server, just check the
 744		   file out.  It's simpler and faster than producing
 745		   and applying patches.  */
 746		/* Fall through.  */
 747	    case T_CHECKOUT:		/* needs checkout */
 748		retval = checkout_file (finfo, vers, 0, 0, 1);
 749		break;
 750	    case T_ADDED:		/* added but not committed */
 751		write_letter (finfo, 'A');
 752		retval = 0;
 753		break;
 754	    case T_REMOVED:		/* removed but not committed */
 755		write_letter (finfo, 'R');
 756		retval = 0;
 757		break;
 758	    case T_REMOVE_ENTRY:	/* needs to be un-registered */
 759		retval = scratch_file (finfo, vers);
 760		break;
 761	    default:			/* can't ever happen :-) */
 762		error (0, 0,
 763		       "unknown file status %d for file %s", status, finfo->file);
 764		retval = 0;
 765		break;
 766	}
 767    }
 768
 769    /* only try to join if things have gone well thus far */
 770    if (retval == 0 && join_rev1)
 771	join_file (finfo, vers);
 772
 773    /* if this directory has an ignore list, add this file to it */
 774    if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL))
 775    {
 776	Node *p;
 777
 778	p = getnode ();
 779	p->type = FILES;
 780	p->key = xstrdup (finfo->file);
 781	if (addnode (ignlist, p) != 0)
 782	    freenode (p);
 783    }
 784
 785    freevers_ts (&vers);
 786    return retval;
 787}
 788
 789
 790
 791static void update_ignproc PROTO ((const char *, const char *));
 792
 793static void
 794update_ignproc (file, dir)
 795    const char *file;
 796    const char *dir;
 797{
 798    struct file_info finfo;
 799    char *tmp;
 800
 801    memset (&finfo, 0, sizeof (finfo));
 802    finfo.file = file;
 803    finfo.update_dir = dir;
 804    if (dir[0] == '\0')
 805	tmp = xstrdup (file);
 806    else
 807    {
 808	tmp = xmalloc (strlen (file) + strlen (dir) + 10);
 809	strcpy (tmp, dir);
 810	strcat (tmp, "/");
 811	strcat (tmp, file);
 812    }
 813
 814    finfo.fullname = tmp;
 815    write_letter (&finfo, '?');
 816    free (tmp);
 817}
 818
 819
 820
 821/* ARGSUSED */
 822static int
 823update_filesdone_proc (callerdat, err, repository, update_dir, entries)
 824    void *callerdat;
 825    int err;
 826    const char *repository;
 827    const char *update_dir;
 828    List *entries;
 829{
 830    if (rewrite_tag)
 831    {
 832	WriteTag (NULL, tag, date, nonbranch, update_dir, repository);
 833	rewrite_tag = 0;
 834    }
 835
 836    /* if this directory has an ignore list, process it then free it */
 837    if (ignlist)
 838    {
 839	ignore_files (ignlist, entries, update_dir, update_ignproc);
 840	dellist (&ignlist);
 841    }
 842
 843    /* Clean up CVS admin dirs if we are export */
 844    if (strcmp (cvs_cmd_name, "export") == 0)
 845    {
 846	/* I'm not sure the existence_error is actually possible (except
 847	   in cases where we really should print a message), but since
 848	   this code used to ignore all errors, I'll play it safe.  */
 849	if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
 850	    error (0, errno, "cannot remove %s directory", CVSADM);
 851    }
 852    else if (!server_active && !pipeout)
 853    {
 854        /* If there is no CVS/Root file, add one */
 855        if (!isfile (CVSADM_ROOT))
 856	    Create_Root ((char *) NULL, current_parsed_root->original);
 857    }
 858
 859    return err;
 860}
 861
 862
 863
 864/*
 865 * update_dirent_proc () is called back by the recursion processor before a
 866 * sub-directory is processed for update.  In this case, update_dirent proc
 867 * will probably create the directory unless -d isn't specified and this is a
 868 * new directory.  A return code of 0 indicates the directory should be
 869 * processed by the recursion code.  A return of non-zero indicates the
 870 * recursion code should skip this directory.
 871 */
 872static Dtype
 873update_dirent_proc (callerdat, dir, repository, update_dir, entries)
 874    void *callerdat;
 875    const char *dir;
 876    const char *repository;
 877    const char *update_dir;
 878    List *entries;
 879{
 880    if (ignore_directory (update_dir))
 881    {
 882	/* print the warm fuzzy message */
 883	if (!quiet)
 884	  error (0, 0, "Ignoring %s", update_dir);
 885        return R_SKIP_ALL;
 886    }
 887
 888    if (!isdir (dir))
 889    {
 890	/* if we aren't building dirs, blow it off */
 891	if (!update_build_dirs)
 892	    return R_SKIP_ALL;
 893
 894	/* Various CVS administrators are in the habit of removing
 895	   the repository directory for things they don't want any
 896	   more.  I've even been known to do it myself (on rare
 897	   occasions).  Not the usual recommended practice, but we
 898	   want to try to come up with some kind of
 899	   reasonable/documented/sensible behavior.  Generally
 900	   the behavior is to just skip over that directory (see
 901	   dirs test in sanity.sh; the case which reaches here
 902	   is when update -d is specified, and the working directory
 903	   is gone but the subdirectory is still mentioned in
 904	   CVS/Entries).  */
 905	/* In the remote case, the client should refrain from
 906	   sending us the directory in the first place.  So we
 907	   want to continue to give an error, so clients make
 908	   sure to do this.  */
 909	if (!server_active && !isdir (repository))
 910	    return R_SKIP_ALL;
 911
 912	if (noexec)
 913	{
 914	    /* ignore the missing dir if -n is specified */
 915	    error (0, 0, "New directory `%s' -- ignored", update_dir);
 916	    return R_SKIP_ALL;
 917	}
 918	else
 919	{
 920	    /* otherwise, create the dir and appropriate adm files */
 921
 922	    /* If no tag or date were specified on the command line,
 923               and we're not using -A, we want the subdirectory to use
 924               the tag and date, if any, of the current directory.
 925               That way, update -d will work correctly when working on
 926               a branch.
 927
 928	       We use TAG_UPDATE_DIR to undo the tag setting in
 929	       update_dirleave_proc.  If we did not do this, we would
 930	       not correctly handle a working directory with multiple
 931	       tags (and maybe we should prohibit such working
 932	       directories, but they work now and we shouldn't make
 933	       them stop working without more thought).  */
 934	    if ((tag == NULL && date == NULL) && ! aflag)
 935	    {
 936		ParseTag (&tag, &date, &nonbranch);
 937		if (tag != NULL || date != NULL)
 938		    tag_update_dir = xstrdup (update_dir);
 939	    }
 940
 941	    make_directory (dir);
 942	    Create_Admin (dir, update_dir, repository, tag, date,
 943			  /* This is a guess.  We will rewrite it later
 944			     via WriteTag.  */
 945			  0,
 946			  0,
 947			  pull_template);
 948	    rewrite_tag = 1;
 949	    nonbranch = 0;
 950	    Subdir_Register (entries, (char *) NULL, dir);
 951	}
 952    }
 953    /* Do we need to check noexec here? */
 954    else if (!pipeout)
 955    {
 956	char *cvsadmdir;
 957
 958	/* The directory exists.  Check to see if it has a CVS
 959	   subdirectory.  */
 960
 961	cvsadmdir = xmalloc (strlen (dir) + 80);
 962	strcpy (cvsadmdir, dir);
 963	strcat (cvsadmdir, "/");
 964	strcat (cvsadmdir, CVSADM);
 965
 966	if (!isdir (cvsadmdir))
 967	{
 968	    /* We cannot successfully recurse into a directory without a CVS
 969	       subdirectory.  Generally we will have already printed
 970	       "? foo".  */
 971	    free (cvsadmdir);
 972	    return R_SKIP_ALL;
 973	}
 974	free (cvsadmdir);
 975    }
 976
 977    /*
 978     * If we are building dirs and not going to stdout, we make sure there is
 979     * no static entries file and write the tag file as appropriate
 980     */
 981    if (!pipeout)
 982    {
 983	if (update_build_dirs)
 984	{
 985	    char *tmp;
 986
 987	    tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ENTSTAT) + 10);
 988	    (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
 989	    if (unlink_file (tmp) < 0 && ! existence_error (errno))
 990		error (1, errno, "cannot remove file %s", tmp);
 991#ifdef SERVER_SUPPORT
 992	    if (server_active)
 993		server_clear_entstat (update_dir, repository);
 994#endif
 995	    free (tmp);
 996	}
 997
 998	/* keep the CVS/Tag file current with the specified arguments */
 999	if (aflag || tag || date)
1000	{
1001	    WriteTag (dir, tag, date, 0, update_dir, repository);
1002	    rewrite_tag = 1;
1003	    nonbranch = 0;
1004	}
1005
1006	/* keep the CVS/Template file current */
1007	if (pull_template) 
1008	{
1009	    WriteTemplate (dir, update_dir);
1010	}
1011
1012	/* initialize the ignore list for this directory */
1013	ignlist = getlist ();
1014    }
1015
1016    /* print the warm fuzzy message */
1017    if (!quiet)
1018	error (0, 0, "Updating %s", update_dir);
1019
1020    return R_PROCESS;
1021}
1022
1023
1024
1025/*
1026 * update_dirleave_proc () is called back by the recursion code upon leaving
1027 * a directory.  It will prune empty directories if needed and will execute
1028 * any appropriate update programs.
1029 */
1030/* ARGSUSED */
1031static int
1032update_dirleave_proc (callerdat, dir, err, update_dir, entries)
1033    void *callerdat;
1034    const char *dir;
1035    int err;
1036    const char *update_dir;
1037    List *entries;
1038{
1039    /* Delete the ignore list if it hasn't already been done.  */
1040    if (ignlist)
1041	dellist (&ignlist);
1042
1043    /* If we set the tag or date for a new subdirectory in
1044       update_dirent_proc, and we're now done with that subdirectory,
1045       undo the tag/date setting.  Note that we know that the tag and
1046       date were both originally NULL in this case.  */
1047    if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
1048    {
1049	if (tag != NULL)
1050	{
1051	    free (tag);
1052	    tag = NULL;
1053	}
1054	if (date != NULL)
1055	{
1056	    free (date);
1057	    date = NULL;
1058	}
1059	nonbranch = 0;
1060	free (tag_update_dir);
1061	tag_update_dir = NULL;
1062    }
1063
1064    if (strchr (dir, '/') == NULL)
1065    {
1066	/* FIXME: chdir ("..") loses with symlinks.  */
1067	/* Prune empty dirs on the way out - if necessary */
1068	(void) CVS_CHDIR ("..");
1069	if (update_prune_dirs && isemptydir (dir, 0))
1070	{
1071	    /* I'm not sure the existence_error is actually possible (except
1072	       in cases where we really should print a message), but since
1073	       this code used to ignore all errors, I'll play it safe.	*/
1074	    if (unlink_file_dir (dir) < 0 && !existence_error (errno))
1075		error (0, errno, "cannot remove %s directory", dir);
1076	    Subdir_Deregister (entries, (char *) NULL, dir);
1077	}
1078    }
1079
1080    return err;
1081}
1082
1083
1084
1085static int isremoved PROTO ((Node *, void *));
1086
1087/* Returns 1 if the file indicated by node has been removed.  */
1088static int
1089isremoved (node, closure)
1090    Node *node;
1091    void *closure;
1092{
1093    Entnode *entdata = node->data;
1094
1095    /* If the first character of the version is a '-', the file has been
1096       removed. */
1097    return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
1098}
1099
1100
1101
1102/* Returns 1 if the argument directory is completely empty, other than the
1103   existence of the CVS directory entry.  Zero otherwise.  If MIGHT_NOT_EXIST
1104   and the directory doesn't exist, then just return 0.  */
1105int
1106isemptydir (dir, might_not_exist)
1107    const char *dir;
1108    int might_not_exist;
1109{
1110    DIR *dirp;
1111    struct dirent *dp;
1112
1113    if ((dirp = CVS_OPENDIR (dir)) == NULL)
1114    {
1115	if (might_not_exist && existence_error (errno))
1116	    return 0;
1117	error (0, errno, "cannot open directory %s for empty check", dir);
1118	return 0;
1119    }
1120    errno = 0;
1121    while ((dp = CVS_READDIR (dirp)) != NULL)
1122    {
1123	if (strcmp (dp->d_name, ".") != 0
1124	    && strcmp (dp->d_name, "..") != 0)
1125	{
1126	    if (strcmp (dp->d_name, CVSADM) != 0)
1127	    {
1128		/* An entry other than the CVS directory.  The directory
1129		   is certainly not empty. */
1130		(void) CVS_CLOSEDIR (dirp);
1131		return 0;
1132	    }
1133	    else
1134	    {
1135		/* The CVS directory entry.  We don't have to worry about
1136		   this unless the Entries file indicates that files have
1137		   been removed, but not committed, in this directory.
1138		   (Removing the directory would prevent people from
1139		   comitting the fact that they removed the files!) */
1140		List *l;
1141		int files_removed;
1142		struct saved_cwd cwd;
1143
1144		if (save_cwd (&cwd))
1145		    error_exit ();
1146
1147		if (CVS_CHDIR (dir) < 0)
1148		    error (1, errno, "cannot change directory to %s", dir);
1149		l = Entries_Open (0, NULL);
1150		files_removed = walklist (l, isremoved, 0);
1151		Entries_Close (l);
1152
1153		if (restore_cwd (&cwd, NULL))
1154		    error_exit ();
1155		free_cwd (&cwd);
1156
1157		if (files_removed != 0)
1158		{
1159		    /* There are files that have been removed, but not
1160		       committed!  Do not consider the directory empty. */
1161		    (void) CVS_CLOSEDIR (dirp);
1162		    return 0;
1163		}
1164	    }
1165	}
1166	errno = 0;
1167    }
1168    if (errno != 0)
1169    {
1170	error (0, errno, "cannot read directory %s", dir);
1171	(void) CVS_CLOSEDIR (dirp);
1172	return 0;
1173    }
1174    (void) CVS_CLOSEDIR (dirp);
1175    return 1;
1176}
1177
1178
1179
1180/*
1181 * scratch the Entries file entry associated with a file
1182 */
1183static int
1184scratch_file (finfo, vers)
1185    struct file_info *finfo;
1186    Vers_TS *vers;
1187{
1188    history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
1189    Scratch_Entry (finfo->entries, finfo->file);
1190#ifdef SERVER_SUPPORT
1191    if (server_active)
1192    {
1193	if (vers->ts_user == NULL)
1194	    server_scratch_entry_only ();
1195	server_updated (finfo, vers,
1196		SERVER_UPDATED, (mode_t) -1,
1197		(unsigned char *) NULL,
1198		(struct buffer *) NULL);
1199    }
1200#endif
1201    if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1202	error (0, errno, "unable to remove %s", finfo->fullname);
1203    else if (!server_active)
1204    {
1205	/* skip this step when the server is running since
1206	 * server_updated should have handled it */
1207	/* keep the vers structure up to date in case we do a join
1208	 * - if there isn't a file, it can't very well have a version number, can it?
1209	 */
1210	if (vers->vn_user != NULL)
1211	{
1212	    free (vers->vn_user);
1213	    vers->vn_user = NULL;
1214	}
1215	if (vers->ts_user != NULL)
1216	{
1217	    free (vers->ts_user);
1218	    vers->ts_user = NULL;
1219	}
1220    }
1221    return 0;
1222}
1223
1224
1225
1226/*
1227 * Check out a file.
1228 */
1229static int
1230checkout_file (finfo, vers_ts, adding, merging, update_server)
1231    struct file_info *finfo;
1232    Vers_TS *vers_ts;
1233    int adding;
1234    int merging;
1235    int update_server;
1236{
1237    char *backup;
1238    int set_time, retval = 0;
1239    int status;
1240    int file_is_dead;
1241    struct buffer *revbuf;
1242
1243    backup = NULL;
1244    revbuf = NULL;
1245
1246    /* Don't screw with backup files if we're going to stdout, or if
1247       we are the server.  */
1248    if (!pipeout && !server_active)
1249    {
1250	backup = xmalloc (strlen (finfo->file)
1251			  + sizeof (CVSADM)
1252			  + sizeof (CVSPREFIX)
1253			  + 10);
1254	(void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1255	if (isfile (finfo->file))
1256	    rename_file (finfo->file, backup);
1257	else
1258	{
1259	    /* If -f/-t wrappers are being used to wrap up a directory,
1260	       then backup might be a directory instead of just a file.  */
1261	    if (unlink_file_dir (backup) < 0)
1262	    {
1263		/* Not sure if the existence_error check is needed here.  */
1264		if (!existence_error (errno))
1265		    /* FIXME: should include update_dir in message.  */
1266		    error (0, errno, "error removing %s", backup);
1267	    }
1268	    free (backup);
1269	    backup = NULL;
1270	}
1271    }
1272
1273    file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
1274
1275    if (!file_is_dead)
1276    {
1277	/*
1278	 * if we are checking out to stdout, print a nice message to
1279	 * stderr, and add the -p flag to the command */
1280	if (pipeout)
1281	{
1282	    if (!quiet)
1283	    {
1284		cvs_outerr ("\
1285===================================================================\n\
1286Checking out ", 0);
1287		cvs_outerr (finfo->fullname, 0);
1288		cvs_outerr ("\n\
1289RCS:  ", 0);
1290		cvs_outerr (vers_ts->srcfile->path, 0);
1291		cvs_outerr ("\n\
1292VERS: ", 0);
1293		cvs_outerr (vers_ts->vn_rcs, 0);
1294		cvs_outerr ("\n***************\n", 0);
1295	    }
1296	}
1297
1298#ifdef SERVER_SUPPORT
1299	if (update_server
1300	    && server_active
1301	    && ! pipeout
1302	    && ! file_gzip_level
1303	    && ! joining ()
1304	    && ! wrap_name_has (finfo->file, WRAP_FROMCVS))
1305	{
1306	    revbuf = buf_nonio_initialize ((BUFMEMERRPROC) NULL);
1307	    status = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1308				   vers_ts->vn_rcs, vers_ts->tag,
1309				   vers_ts->options, RUN_TTY,
1310				   checkout_to_buffer, revbuf);
1311	}
1312	else
1313#endif
1314	    status = RCS_checkout (vers_ts->srcfile,
1315				   pipeout ? NULL : finfo->file,
1316				   vers_ts->vn_rcs, vers_ts->tag,
1317				   vers_ts->options, RUN_TTY,
1318				   (RCSCHECKOUTPROC) NULL, (void *) NULL);
1319    }
1320    if (file_is_dead || status == 0)
1321    {
1322	mode_t mode;
1323
1324	mode = (mode_t) -1;
1325
1326	if (!pipeout)
1327	{
1328	    Vers_TS *xvers_ts;
1329
1330	    if (revbuf != NULL && !noexec)
1331	    {
1332		struct stat sb;
1333
1334		/* FIXME: We should have RCS_checkout return the mode.
1335		   That would also fix the kludge with noexec, above, which
1336		   is here only because noexec doesn't write srcfile->path
1337		   for us to stat.  */
1338		if (stat (vers_ts->srcfile->path, &sb) < 0)
1339		{
1340#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
1341		    buf_free (revbuf);
1342#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
1343		    error (1, errno, "cannot stat %s",
1344			   vers_ts->srcfile->path);
1345		}
1346		mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
1347	    }
1348
1349	    if (cvswrite
1350		&& !file_is_dead
1351		&& !fileattr_get (finfo->file, "_watched"))
1352	    {
1353		if (revbuf == NULL)
1354		    xchmod (finfo->file, 1);
1355		else
1356		{
1357		    /* We know that we are the server here, so
1358                       although xchmod checks umask, we don't bother.  */
1359		    mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
1360			     | ((mode & S_IRGRP) ? S_IWGRP : 0)
1361			     | ((mode & S_IROTH) ? S_IWOTH : 0));
1362		}
1363	    }
1364
1365	    {
1366		/* A newly checked out file is never under the spell
1367		   of "cvs edit".  If we think we were editing it
1368		   from a previous life, clean up.  Would be better to
1369		   check for same the working directory instead of
1370		   same user, but that is hairy.  */
1371
1372		struct addremove_args args;
1373
1374		editor_set (finfo->file, getcaller (), NULL);
1375
1376		memset (&args, 0, sizeof args);
1377		args.remove_temp = 1;
1378		watch_modify_watchers (finfo->file, &args);
1379	    }
1380
1381	    /* set the time from the RCS file iff it was unknown before */
1382	    set_time =
1383		(!noexec
1384		 && (vers_ts->vn_user == NULL ||
1385		     strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
1386		 && !file_is_dead);
1387
1388	    wrap_fromcvs_process_file (finfo->file);
1389
1390	    xvers_ts = Version_TS (finfo, options, tag, date, 
1391				   force_tag_match, set_time);
1392	    if (strcmp (xvers_ts->options, "-V4") == 0)
1393		xvers_ts->options[0] = '\0';
1394
1395	    if (revbuf != NULL)
1396	    {
1397		/* If we stored the file data into a buffer, then we
1398                   didn't create a file at all, so xvers_ts->ts_user
1399                   is wrong.  The correct value is to have it be the
1400                   same as xvers_ts->ts_rcs, meaning that the working
1401                   file is unchanged from the RCS file.
1402
1403		   FIXME: We should tell Version_TS not to waste time
1404		   statting the nonexistent file.
1405
1406		   FIXME: Actually, I don't think the ts_user value
1407		   matters at all here.  The only use I know of is
1408		   that it is printed in a trace message by
1409		   Server_Register.  */
1410
1411		if (xvers_ts->ts_user != NULL)
1412		    free (xvers_ts->ts_user);
1413		xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
1414	    }
1415
1416	    (void) time (&last_register_time);
1417
1418	    if (file_is_dead)
1419	    {
1420		if (xvers_ts->vn_user != NULL)
1421		{
1422		    error (0, 0,
1423			   "warning: %s is not (any longer) pertinent",
1424 			   finfo->fullname);
1425		}
1426		Scratch_Entry (finfo->entries, finfo->file);
1427#ifdef SERVER_SUPPORT
1428		if (server_active && xvers_ts->ts_user == NULL)
1429		    server_scratch_entry_only ();
1430#endif
1431		/* FIXME: Rather than always unlink'ing, and ignoring the
1432		   existence_error, we should do the unlink only if
1433		   vers_ts->ts_user is non-NULL.  Then there would be no
1434		   need to ignore an existence_error (for example, if the
1435		   user removes the file while we are running).  */
1436		if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1437		{
1438		    error (0, errno, "cannot remove %s", finfo->fullname);
1439		}
1440	    }
1441	    else
1442		Register (finfo->entries, finfo->file,
1443			  adding ? "0" : xvers_ts->vn_rcs,
1444			  xvers_ts->ts_user, xvers_ts->options,
1445			  xvers_ts->tag, xvers_ts->date,
1446			  (char *)0); /* Clear conflict flag on fresh checkout */
1447
1448	    /* fix up the vers structure, in case it is used by join */
1449	    if (join_rev1)
1450	    {
1451		/* FIXME: It seems like we should be preserving ts_user
1452		 * & ts_rcs here, but setting them causes problems in
1453		 * join_file().
1454		 */
1455		if (vers_ts->vn_user != NULL)
1456		    free (vers_ts->vn_user);
1457		if (vers_ts->vn_rcs != NULL)
1458		    free (vers_ts->vn_rcs);
1459		vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1460		vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1461	    }
1462
1463	    /* If this is really Update and not Checkout, recode history */
1464	    if (strcmp (cvs_cmd_name, "update") == 0)
1465		history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1466			       finfo->repository);
1467
1468	    freevers_ts (&xvers_ts);
1469
1470	    if (!really_quiet && !file_is_dead)
1471	    {
1472		write_letter (finfo, 'U');
1473	    }
1474	}
1475
1476#ifdef SERVER_SUPPORT
1477	if (update_server && server_active)
1478	    server_updated (finfo, vers_ts,
1479			    merging ? SERVER_MERGED : SERVER_UPDATED,
1480			    mode, (unsigned char *) NULL, revbuf);
1481#endif
1482    }
1483    else
1484    {
1485	if (backup != NULL)
1486	{
1487	    rename_file (backup, finfo->file);
1488	    free (backup);
1489	    backup = NULL;
1490	}
1491
1492	error (0, 0, "could not check out %s", finfo->fullname);
1493
1494	retval = status;
1495    }
1496
1497    if (backup != NULL)
1498    {
1499	/* If -f/-t wrappers are being used to wrap up a directory,
1500	   then backup might be a directory instead of just a file.  */
1501	if (unlink_file_dir (backup) < 0)
1502	{
1503	    /* Not sure if the existence_error check is needed here.  */
1504	    if (!existence_error (errno))
1505		/* FIXME: should include update_dir in message.  */
1506		error (0, errno, "error removing %s", backup);
1507	}
1508	free (backup);
1509    }
1510
1511#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
1512    if (revbuf != NULL)
1513	buf_free (revbuf);
1514#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
1515    return retval;
1516}
1517
1518
1519
1520#ifdef SERVER_SUPPORT
1521
1522/* This function is used to write data from a file being checked out
1523   into a buffer.  */
1524
1525static void
1526checkout_to_buffer (callerdat, data, len)
1527     void *callerdat;
1528     const char *data;
1529     size_t len;
1530{
1531    struct buffer *buf = (struct buffer *) callerdat;
1532
1533    buf_output (buf, data, len);
1534}
1535
1536#endif /* SERVER_SUPPORT */
1537
1538#ifdef SERVER_SUPPORT
1539
1540/* This structure is used to pass information between patch_file and
1541   patch_file_write.  */
1542
1543struct patch_file_data
1544{
1545    /* File name, for error messages.  */
1546    const char *filename;
1547    /* File to which to write.  */
1548    FILE *fp;
1549    /* Whether to compute the MD5 checksum.  */
1550    int compute_checksum;
1551    /* Data structure for computing the MD5 checksum.  */
1552    struct cvs_MD5Context context;
1553    /* Set if the file has a final newline.  */
1554    int final_nl;
1555};
1556
1557/* Patch a file.  Runs diff.  This is only done when running as the
1558 * server.  The hope is that the diff will be smaller than the file
1559 * itself.
1560 */
1561static int
1562patch_file (finfo, vers_ts, docheckout, file_info, checksum)
1563    struct file_info *finfo;
1564    Vers_TS *vers_ts;
1565    int *docheckout;
1566    struct stat *file_info;
1567    unsigned char *checksum;
1568{
1569    char *backup;
1570    char *file1;
1571    char *file2;
1572    int retval = 0;
1573    int retcode = 0;
1574    int fail;
1575    FILE *e;
1576    struct patch_file_data data;
1577
1578    *docheckout = 0;
1579
1580    if (noexec || pipeout || joining ())
1581    {
1582	*docheckout = 1;
1583	return 0;
1584    }
1585
1586    /* If this file has been marked as being binary, then never send a
1587       patch.  */
1588    if (strcmp (vers_ts->options, "-kb") == 0)
1589    {
1590	*docheckout = 1;
1591	return 0;
1592    }
1593
1594    /* First check that the first revision exists.  If it has been nuked
1595       by cvs admin -o, then just fall back to checking out entire
1596       revisions.  In some sense maybe we don't have to do this; after
1597       all cvs.texinfo says "Make sure that no-one has checked out a
1598       copy of the revision you outdate" but then again, that advice
1599       doesn't really make complete sense, because "cvs admin" operates
1600       on a working directory and so _someone_ will almost always have
1601       _some_ revision checked out.  */
1602    {
1603	char *rev;
1604
1605	rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
1606	if (rev == NULL)
1607	{
1608	    *docheckout = 1;
1609	    return 0;
1610	}
1611	else
1612	    free (rev);
1613    }
1614
1615    /* If the revision is dead, let checkout_file handle it rather
1616       than duplicating the processing here.  */
1617    if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
1618    {
1619	*docheckout = 1;
1620	return 0;
1621    }
1622
1623    backup = xmalloc (strlen (finfo->file)
1624		      + sizeof (CVSADM)
1625		      + sizeof (CVSPREFIX)
1626		      + 10);
1627    (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1628    if (isfile (finfo->file))
1629        rename_file (finfo->file, backup);
1630    else
1631    {
1632	if (unlink_file (backup) < 0
1633	    && !existence_error (errno))
1634	    error (0, errno, "cannot remove %s", backup);
1635    }
1636
1637    file1 = xmalloc (strlen (finfo->file)
1638		     + sizeof (CVSADM)
1639		     + sizeof (CVSPREFIX)
1640		     + 10);
1641    (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
1642    file2 = xmalloc (strlen (finfo->file)
1643		     + sizeof (CVSADM)
1644		     + sizeof (CVSPREFIX)
1645		     + 10);
1646    (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
1647
1648    fail = 0;
1649
1650    /* We need to check out both revisions first, to see if either one
1651       has a trailing newline.  Because of this, we don't use rcsdiff,
1652       but just use diff.  */
1653
1654    e = CVS_FOPEN (file1, "w");
1655    if (e == NULL)
1656	error (1, errno, "cannot open %s", file1);
1657
1658    data.filename = file1;
1659    data.fp = e;
1660    data.final_nl = 0;
1661    data.compute_checksum = 0;
1662
1663    /* Duplicating the client working file, so use the original sticky options.
1664     */
1665    retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1666			    vers_ts->vn_user, vers_ts->entdata->tag,
1667			    vers_ts->entdata->options, RUN_TTY,
1668			    patch_file_write, (void *) &data);
1669
1670    if (fclose (e) < 0)
1671	error (1, errno, "cannot close %s", file1);
1672
1673    if (retcode != 0 || ! data.final_nl)
1674	fail = 1;
1675
1676    if (! fail)
1677    {
1678	e = CVS_FOPEN (file2, "w");
1679	if (e == NULL)
1680	    error (1, errno, "cannot open %s", file2);
1681
1682	data.filename = file2;
1683	data.fp = e;
1684	data.final_nl = 0;
1685	data.compute_checksum = 1;
1686	cvs_MD5Init (&data.context);
1687
1688	retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1689				vers_ts->vn_rcs, vers_ts->tag,
1690				vers_ts->options, RUN_TTY,
1691				patch_file_write, (void *) &data);
1692
1693	if (fclose (e) < 0)
1694	    error (1, errno, "cannot close %s", file2);
1695
1696	if (retcode != 0 || ! data.final_nl)
1697	    fail = 1;
1698	else
1699	    cvs_MD5Final (checksum, &data.context);
1700    }	  
1701
1702    retcode = 0;
1703    if (! fail)
1704    {
1705	int dargc = 0;
1706	size_t darg_allocated = 0;
1707	char **dargv = NULL;
1708
1709	/* If the client does not support the Rcs-diff command, we
1710           send a context diff, and the client must invoke patch.
1711           That approach was problematical for various reasons.  The
1712           new approach only requires running diff in the server; the
1713           client can handle everything without invoking an external
1714           program.  */
1715	if (!rcs_diff_patches)
1716	    /* We use -c, not -u, because that is what CVS has
1717	       traditionally used.  Kind of a moot point, now that
1718	       Rcs-diff is preferred, so there is no point in making
1719	       the compatibility issues worse.  */
1720	    run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
1721	else
1722	    /* Now that diff is librarified, we could be passing -a if
1723	       we wanted to.  However, it is unclear to me whether we
1724	       would want to.  Does diff -a, in any significant
1725	       percentage of cases, produce patches which are smaller
1726	       than the files it is patching?  I guess maybe text
1727	       files with character sets which diff regards as
1728	       'binary'.  Conversely, do they tend to be much larger
1729	       in the bad cases?  This needs some more
1730	       thought/investigation, I suspect.  */
1731	    run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
1732	retcode = diff_exec (file1, file2, NULL, NULL, dargc, dargv,
1733			     finfo->file);
1734	run_arg_free_p (dargc, dargv);
1735	free (dargv);
1736
1737	/* A retcode of 0 means no differences.  1 means some differences.  */
1738	if (retcode != 0
1739	    && retcode != 1)
1740	{
1741	    fail = 1;
1742	}
1743    }
1744
1745    if (! fail)
1746    {
1747	struct stat file2_info;
1748
1749	/* Check to make sure the patch is really shorter */
1750	if (CVS_STAT (file2, &file2_info) < 0)
1751	    error (1, errno, "could not stat %s", file2);
1752	if (CVS_STAT (finfo->file, file_info) < 0)
1753	    error (1, errno, "could not stat %s", finfo->file);
1754	if (file2_info.st_size <= file_info->st_size)
1755	    fail = 1;
1756    }
1757
1758    if (! fail)
1759    {
1760# define BINARY "Binary"
1761	char buf[sizeof BINARY];
1762	unsigned int c;
1763
1764	/* Check the diff output to make sure patch will be handle it.  */
1765	e = CVS_FOPEN (finfo->file, "r");
1766	if (e == NULL)
1767	    error (1, errno, "could not open diff output file %s",
1768		   finfo->fullname);
1769	c = fread (buf, 1, sizeof BINARY - 1, e);
1770	buf[c] = '\0';
1771	if (strcmp (buf, BINARY) == 0)
1772	{
1773	    /* These are binary files.  We could useā€¦

Large files files are truncated, but you can click here to view the full file