PageRenderTime 93ms CodeModel.GetById 9ms app.highlight 71ms RepoModel.GetById 2ms app.codeStats 0ms

/contrib/cvs/src/import.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1653 lines | 1268 code | 130 blank | 255 comment | 315 complexity | 8ecc8e99959a6ed3afa37f1bf00c8e1e MD5 | raw 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 * "import" checks in the vendor release located in the current directory into
  14 * the CVS source repository.  The CVS vendor branch support is utilized.
  15 * 
  16 * At least three arguments are expected to follow the options:
  17 *	repository	Where the source belongs relative to the CVSROOT
  18 *	VendorTag	Vendor's major tag
  19 *	VendorReleTag	Tag for this particular release
  20 *
  21 * Additional arguments specify more Vendor Release Tags.
  22 */
  23
  24#include "cvs.h"
  25#include "savecwd.h"
  26#include <assert.h>
  27
  28static char *get_comment PROTO((const char *user));
  29static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile,
  30			  char *vers));
  31static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc,
  32		     char *targv[]));
  33static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[]));
  34static int import_descend_dir PROTO((char *message, char *dir, char *vtag,
  35			       int targc, char *targv[]));
  36static int process_import_file PROTO((char *message, char *vfile, char *vtag,
  37				int targc, char *targv[]));
  38static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int targc,
  39			    char *targv[], int inattic));
  40static void add_log PROTO((int ch, char *fname));
  41
  42static int repos_len;
  43static char *vhead;
  44static char *vbranch;
  45static FILE *logfp;
  46static char *repository;
  47static int conflicts;
  48static int use_file_modtime;
  49static char *keyword_opt = NULL;
  50
  51static const char *const import_usage[] =
  52{
  53    "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n",
  54    "    [-W spec] repository vendor-tag release-tags...\n",
  55    "\t-d\tUse the file's modification time as the time of import.\n",
  56    "\t-k sub\tSet default RCS keyword substitution mode.\n",
  57    "\t-I ign\tMore files to ignore (! to reset).\n",
  58    "\t-b bra\tVendor branch id.\n",
  59    "\t-m msg\tLog message.\n",
  60    "\t-W spec\tWrappers specification line.\n",
  61    "(Specify the --help global option for a list of other help options)\n",
  62    NULL
  63};
  64
  65int
  66import (argc, argv)
  67    int argc;
  68    char **argv;
  69{
  70    char *message = NULL;
  71    char *tmpfile;
  72    char *cp;
  73    int i, c, msglen, err;
  74    List *ulist;
  75    Node *p;
  76    struct logfile_info *li;
  77
  78    if (argc == -1)
  79	usage (import_usage);
  80
  81    ign_setup ();
  82    wrap_setup ();
  83
  84    vbranch = xstrdup (CVSBRANCH);
  85    optind = 0;
  86    while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:")) != -1)
  87    {
  88	switch (c)
  89	{
  90	    case 'Q':
  91	    case 'q':
  92		/* The CVS 1.5 client sends these options (in addition to
  93		   Global_option requests), so we must ignore them.  */
  94		if (!server_active)
  95		    error (1, 0,
  96			   "-q or -Q must be specified before \"%s\"",
  97			   cvs_cmd_name);
  98		break;
  99	    case 'd':
 100		if (server_active)
 101		{
 102		    /* CVS 1.10 and older clients will send this, but it
 103		       doesn't do any good.  So tell the user we can't
 104		       cope, rather than silently losing.  */
 105		    error (0, 0,
 106			   "warning: not setting the time of import from the file");
 107		    error (0, 0, "due to client limitations");
 108		}
 109		use_file_modtime = 1;
 110		break;
 111	    case 'b':
 112		free (vbranch);
 113		vbranch = xstrdup (optarg);
 114		break;
 115	    case 'm':
 116#ifdef FORCE_USE_EDITOR
 117		use_editor = 1;
 118#else
 119		use_editor = 0;
 120#endif
 121		if (message) free (message);
 122		message = xstrdup(optarg);
 123		break;
 124	    case 'I':
 125		ign_add (optarg, 0);
 126		break;
 127            case 'k':
 128		/* RCS_check_kflag returns strings of the form -kxx.  We
 129		   only use it for validation, so we can free the value
 130		   as soon as it is returned. */
 131		free (RCS_check_kflag (optarg));
 132		keyword_opt = optarg;
 133		break;
 134	    case 'W':
 135		wrap_add (optarg, 0);
 136		break;
 137	    case '?':
 138	    default:
 139		usage (import_usage);
 140		break;
 141	}
 142    }
 143    argc -= optind;
 144    argv += optind;
 145    if (argc < 3)
 146	usage (import_usage);
 147
 148    /* This is for handling the Checkin-time request.  It might seem a
 149       bit odd to enable the use_file_modtime code even in the case
 150       where Checkin-time was not sent for a particular file.  The
 151       effect is that we use the time of upload, rather than the time
 152       when we call RCS_checkin.  Since those times are both during
 153       CVS's run, that seems OK, and it is easier to implement than
 154       putting the "was Checkin-time sent" flag in CVS/Entries or some
 155       such place.  */
 156
 157    if (server_active)
 158	use_file_modtime = 1;
 159
 160    /* Don't allow "CVS" as any directory in module path.
 161     *
 162     * Could abstract this to valid_module_path, but I don't think we'll need
 163     * to call it from anywhere else.
 164     */
 165    /* for each "CVS" in path... */
 166    cp = argv[0];
 167    while ((cp = strstr(cp, "CVS")) != NULL)
 168    {
 169	if ( /* /^CVS/ OR m#/CVS#... */
 170	    (cp == argv[0] || ISDIRSEP(*(cp-1)))
 171	    /* ...AND /CVS$/ OR m#CVS/# */
 172	    && (*(cp+3) == '\0' || ISDIRSEP(*(cp+3)))
 173	   )
 174	{
 175	    error (0, 0,
 176		   "The word `CVS' is reserved by CVS and may not be used");
 177	    error (1, 0, "as a directory in a path or as a file name.");
 178	}
 179	cp += 3;
 180    }
 181
 182    for (i = 1; i < argc; i++)		/* check the tags for validity */
 183    {
 184	int j;
 185
 186	RCS_check_tag (argv[i]);
 187	for (j = 1; j < i; j++)
 188	    if (strcmp (argv[j], argv[i]) == 0)
 189		error (1, 0, "tag `%s' was specified more than once", argv[i]);
 190    }
 191
 192    /* XXX - this should be a module, not just a pathname */
 193    if (!isabsolute (argv[0]) && pathname_levels (argv[0]) == 0)
 194    {
 195	if (current_parsed_root == NULL)
 196	{
 197	    error (0, 0, "missing CVSROOT environment variable\n");
 198	    error (1, 0, "Set it or specify the '-d' option to %s.",
 199		   program_name);
 200	}
 201	repository = xmalloc (strlen (current_parsed_root->directory)
 202			      + strlen (argv[0])
 203			      + 2);
 204	(void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
 205	repos_len = strlen (current_parsed_root->directory);
 206    }
 207    else
 208    {
 209	/* It is somewhere between a security hole and "unexpected" to
 210	   let the client start mucking around outside the cvsroot
 211	   (wouldn't get the right CVSROOT configuration, &c).  */
 212	error (1, 0, "directory %s not relative within the repository",
 213	       argv[0]);
 214    }
 215
 216    /*
 217     * Consistency checks on the specified vendor branch.  It must be
 218     * composed of only numbers and dots ('.').  Also, for now we only
 219     * support branching to a single level, so the specified vendor branch
 220     * must only have two dots in it (like "1.1.1").
 221     */
 222    {
 223	regex_t pat;
 224	int ret = regcomp (&pat, "^[1-9][0-9]*\\.[1-9][0-9]*\\.[1-9][0-9]*$",
 225			   REG_EXTENDED);
 226	assert (!ret);
 227	if (regexec (&pat, vbranch, 0, NULL, 0))
 228	{
 229	    error (1, 0,
 230"Only numeric branch specifications with two dots are\n"
 231"supported by import, not `%s'.  For example: `1.1.1'.",
 232		   vbranch);
 233	}
 234	regfree (&pat);
 235    }
 236
 237    /* Set vhead to the branch's parent.  */
 238    vhead = xstrdup (vbranch);
 239    cp = strrchr (vhead, '.');
 240    *cp = '\0';
 241
 242#ifdef CLIENT_SUPPORT
 243    if (current_parsed_root->isremote)
 244    {
 245	/* For rationale behind calling start_server before do_editor, see
 246	   commit.c  */
 247	start_server ();
 248    }
 249#endif
 250
 251    if (!server_active && use_editor)
 252    {
 253	do_editor ((char *) NULL, &message,
 254		   current_parsed_root->isremote ? (char *) NULL : repository,
 255		   (List *) NULL);
 256    }
 257    do_verify (&message, repository);
 258    msglen = message == NULL ? 0 : strlen (message);
 259    if (msglen == 0 || message[msglen - 1] != '\n')
 260    {
 261	char *nm = xmalloc (msglen + 2);
 262	*nm = '\0';
 263	if (message != NULL)
 264	{
 265	    (void) strcpy (nm, message);
 266	    free (message);
 267	}
 268	(void) strcat (nm + msglen, "\n");
 269	message = nm;
 270    }
 271
 272#ifdef CLIENT_SUPPORT
 273    if (current_parsed_root->isremote)
 274    {
 275	int err;
 276
 277	if (vbranch[0] != '\0')
 278	    option_with_arg ("-b", vbranch);
 279	option_with_arg ("-m", message ? message : "");
 280	if (keyword_opt != NULL)
 281	    option_with_arg ("-k", keyword_opt);
 282	/* The only ignore processing which takes place on the server side
 283	   is the CVSROOT/cvsignore file.  But if the user specified -I !,
 284	   the documented behavior is to not process said file.  */
 285	if (ign_inhibit_server)
 286	{
 287	    send_arg ("-I");
 288	    send_arg ("!");
 289	}
 290	wrap_send ();
 291
 292	{
 293	    int i;
 294	    for (i = 0; i < argc; ++i)
 295		send_arg (argv[i]);
 296	}
 297
 298	logfp = stdin;
 299	client_import_setup (repository);
 300	err = import_descend (message, argv[1], argc - 2, argv + 2);
 301	client_import_done ();
 302	if (message)
 303	    free (message);
 304	free (repository);
 305	free (vbranch);
 306	free (vhead);
 307	send_to_server ("import\012", 0);
 308	err += get_responses_and_close ();
 309	return err;
 310    }
 311#endif
 312
 313    if (!safe_location ( NULL ))
 314    {
 315	error (1, 0, "attempt to import the repository");
 316    }
 317
 318    /*
 319     * Make all newly created directories writable.  Should really use a more
 320     * sophisticated security mechanism here.
 321     */
 322    (void) umask (cvsumask);
 323    make_directories (repository);
 324
 325    /* Create the logfile that will be logged upon completion */
 326    if ((logfp = cvs_temp_file (&tmpfile)) == NULL)
 327	error (1, errno, "cannot create temporary file `%s'",
 328	       tmpfile ? tmpfile : "(null)");
 329    /* On systems where we can unlink an open file, do so, so it will go
 330       away no matter how we exit.  FIXME-maybe: Should be checking for
 331       errors but I'm not sure which error(s) we get if we are on a system
 332       where one can't unlink open files.  */
 333    (void) CVS_UNLINK (tmpfile);
 334    (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
 335    (void) fprintf (logfp, "Release Tags:\t");
 336    for (i = 2; i < argc; i++)
 337	(void) fprintf (logfp, "%s\n\t\t", argv[i]);
 338    (void) fprintf (logfp, "\n");
 339
 340    /* Just Do It.  */
 341    err = import_descend (message, argv[1], argc - 2, argv + 2);
 342    if (conflicts)
 343    {
 344	if (!really_quiet)
 345	{
 346	    char buf[20];
 347
 348	    cvs_output_tagged ("+importmergecmd", NULL);
 349	    cvs_output_tagged ("newline", NULL);
 350	    sprintf (buf, "%d", conflicts);
 351	    cvs_output_tagged ("conflicts", buf);
 352	    cvs_output_tagged ("text", " conflicts created by this import.");
 353	    cvs_output_tagged ("newline", NULL);
 354	    cvs_output_tagged ("text",
 355			       "Use the following command to help the merge:");
 356	    cvs_output_tagged ("newline", NULL);
 357	    cvs_output_tagged ("newline", NULL);
 358	    cvs_output_tagged ("text", "\t");
 359	    cvs_output_tagged ("text", program_name);
 360	    if (CVSroot_cmdline != NULL)
 361	    {
 362		cvs_output_tagged ("text", " -d ");
 363		cvs_output_tagged ("text", CVSroot_cmdline);
 364	    }
 365	    cvs_output_tagged ("text", " checkout -j");
 366	    cvs_output_tagged ("mergetag1", "<prev_rel_tag>");
 367	    cvs_output_tagged ("text", " -j");
 368	    cvs_output_tagged ("mergetag2", argv[2]);
 369	    cvs_output_tagged ("text", " ");
 370	    cvs_output_tagged ("repository", argv[0]);
 371	    cvs_output_tagged ("newline", NULL);
 372	    cvs_output_tagged ("newline", NULL);
 373	    cvs_output_tagged ("-importmergecmd", NULL);
 374	}
 375
 376	/* FIXME: I'm not sure whether we need to put this information
 377           into the loginfo.  If we do, then note that it does not
 378           report any required -d option.  There is no particularly
 379           clean way to tell the server about the -d option used by
 380           the client.  */
 381	(void) fprintf (logfp, "\n%d conflicts created by this import.\n",
 382			conflicts);
 383	(void) fprintf (logfp,
 384			"Use the following command to help the merge:\n\n");
 385	(void) fprintf (logfp, "\t%s checkout ", program_name);
 386	(void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n",
 387			argv[1], argv[1], argv[0]);
 388    }
 389    else
 390    {
 391	if (!really_quiet)
 392	    cvs_output ("\nNo conflicts created by this import\n\n", 0);
 393	(void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
 394    }
 395
 396    /*
 397     * Write out the logfile and clean up.
 398     */
 399    ulist = getlist ();
 400    p = getnode ();
 401    p->type = UPDATE;
 402    p->delproc = update_delproc;
 403    p->key = xstrdup ("- Imported sources");
 404    li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
 405    li->type = T_TITLE;
 406    li->tag = xstrdup (vbranch);
 407    li->rev_old = li->rev_new = NULL;
 408    p->data = li;
 409    (void) addnode (ulist, p);
 410    Update_Logfile (repository, message, logfp, ulist);
 411    dellist (&ulist);
 412    if (fclose (logfp) < 0)
 413	error (0, errno, "error closing %s", tmpfile);
 414
 415    /* Make sure the temporary file goes away, even on systems that don't let
 416       you delete a file that's in use.  */
 417    if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno))
 418	error (0, errno, "cannot remove %s", tmpfile);
 419    free (tmpfile);
 420
 421    if (message)
 422	free (message);
 423    free (repository);
 424    free (vbranch);
 425    free (vhead);
 426
 427    return (err);
 428}
 429
 430/* Process all the files in ".", then descend into other directories.
 431   Returns 0 for success, or >0 on error (in which case a message
 432   will have been printed).  */
 433static int
 434import_descend (message, vtag, targc, targv)
 435    char *message;
 436    char *vtag;
 437    int targc;
 438    char *targv[];
 439{
 440    DIR *dirp;
 441    struct dirent *dp;
 442    int err = 0;
 443    List *dirlist = NULL;
 444
 445    /* first, load up any per-directory ignore lists */
 446    ign_add_file (CVSDOTIGNORE, 1);
 447    wrap_add_file (CVSDOTWRAPPER, 1);
 448
 449    if (!current_parsed_root->isremote)
 450	lock_dir_for_write (repository);
 451
 452    if ((dirp = CVS_OPENDIR (".")) == NULL)
 453    {
 454	error (0, errno, "cannot open directory");
 455	err++;
 456    }
 457    else
 458    {
 459	errno = 0;
 460	while ((dp = CVS_READDIR (dirp)) != NULL)
 461	{
 462	    if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
 463		goto one_more_time_boys;
 464
 465	    /* CVS directories are created in the temp directory by
 466	       server.c because it doesn't special-case import.  So
 467	       don't print a message about them, regardless of -I!.  */
 468	    if (server_active && strcmp (dp->d_name, CVSADM) == 0)
 469		goto one_more_time_boys;
 470
 471	    if (ign_name (dp->d_name))
 472	    {
 473		add_log ('I', dp->d_name);
 474		goto one_more_time_boys;
 475	    }
 476
 477	    if (
 478#ifdef DT_DIR
 479		(dp->d_type == DT_DIR
 480		 || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name)))
 481#else
 482		isdir (dp->d_name)
 483#endif
 484		&& !wrap_name_has (dp->d_name, WRAP_TOCVS)
 485		)
 486	    {
 487		Node *n;
 488
 489		if (dirlist == NULL)
 490		    dirlist = getlist();
 491
 492		n = getnode();
 493		n->key = xstrdup (dp->d_name);
 494		addnode(dirlist, n);
 495	    }
 496	    else if (
 497#ifdef DT_DIR
 498		     dp->d_type == DT_LNK
 499		     || (dp->d_type == DT_UNKNOWN && islink (dp->d_name))
 500#else
 501		     islink (dp->d_name)
 502#endif
 503		     )
 504	    {
 505		add_log ('L', dp->d_name);
 506		err++;
 507	    }
 508	    else
 509	    {
 510#ifdef CLIENT_SUPPORT
 511		if (current_parsed_root->isremote)
 512		    err += client_process_import_file (message, dp->d_name,
 513                                                       vtag, targc, targv,
 514                                                       repository,
 515                                                       keyword_opt != NULL &&
 516						       keyword_opt[0] == 'b',
 517						       use_file_modtime);
 518		else
 519#endif
 520		    err += process_import_file (message, dp->d_name,
 521						vtag, targc, targv);
 522	    }
 523	one_more_time_boys:
 524	    errno = 0;
 525	}
 526	if (errno != 0)
 527	{
 528	    error (0, errno, "cannot read directory");
 529	    ++err;
 530	}
 531	(void) CVS_CLOSEDIR (dirp);
 532    }
 533
 534    if (!current_parsed_root->isremote)
 535	Lock_Cleanup ();
 536
 537    if (dirlist != NULL)
 538    {
 539	Node *head, *p;
 540
 541	head = dirlist->list;
 542	for (p = head->next; p != head; p = p->next)
 543	{
 544	    err += import_descend_dir (message, p->key, vtag, targc, targv);
 545	}
 546
 547	dellist(&dirlist);
 548    }
 549
 550    return (err);
 551}
 552
 553/*
 554 * Process the argument import file.
 555 */
 556static int
 557process_import_file (message, vfile, vtag, targc, targv)
 558    char *message;
 559    char *vfile;
 560    char *vtag;
 561    int targc;
 562    char *targv[];
 563{
 564    char *rcs;
 565    int inattic = 0;
 566
 567    rcs = xmalloc (strlen (repository) + strlen (vfile) + sizeof (RCSEXT)
 568		   + 5);
 569    (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
 570    if (!isfile (rcs))
 571    {
 572	char *attic_name;
 573
 574	attic_name = xmalloc (strlen (repository) + strlen (vfile) +
 575			      sizeof (CVSATTIC) + sizeof (RCSEXT) + 10);
 576	(void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
 577			vfile, RCSEXT);
 578	if (!isfile (attic_name))
 579	{
 580	    int retval;
 581	    char *free_opt = NULL;
 582	    char *our_opt = keyword_opt;
 583
 584	    free (attic_name);
 585	    /*
 586	     * A new import source file; it doesn't exist as a ,v within the
 587	     * repository nor in the Attic -- create it anew.
 588	     */
 589	    add_log ('N', vfile);
 590
 591#ifdef SERVER_SUPPORT
 592	    /* The most reliable information on whether the file is binary
 593	       is what the client told us.  That is because if the client had
 594	       the wrong idea about binaryness, it corrupted the file, so
 595	       we might as well believe the client.  */
 596	    if (server_active)
 597	    {
 598		Node *node;
 599		List *entries;
 600
 601		/* Reading all the entries for each file is fairly silly, and
 602		   probably slow.  But I am too lazy at the moment to do
 603		   anything else.  */
 604		entries = Entries_Open (0, NULL);
 605		node = findnode_fn (entries, vfile);
 606		if (node != NULL)
 607		{
 608		    Entnode *entdata = node->data;
 609
 610		    if (entdata->type == ENT_FILE)
 611		    {
 612			assert (entdata->options[0] == '-'
 613				&& entdata->options[1] == 'k');
 614			our_opt = xstrdup (entdata->options + 2);
 615			free_opt = our_opt;
 616		    }
 617		}
 618		Entries_Close (entries);
 619	    }
 620#endif
 621
 622	    retval = add_rcs_file (message, rcs, vfile, vhead, our_opt,
 623				   vbranch, vtag, targc, targv,
 624				   NULL, 0, logfp);
 625	    if (free_opt != NULL)
 626		free (free_opt);
 627	    free (rcs);
 628	    return retval;
 629	}
 630	free (attic_name);
 631	inattic = 1;
 632    }
 633
 634    free (rcs);
 635    /*
 636     * an rcs file exists. have to do things the official, slow, way.
 637     */
 638    return (update_rcs_file (message, vfile, vtag, targc, targv, inattic));
 639}
 640
 641/*
 642 * The RCS file exists; update it by adding the new import file to the
 643 * (possibly already existing) vendor branch.
 644 */
 645static int
 646update_rcs_file (message, vfile, vtag, targc, targv, inattic)
 647    char *message;
 648    char *vfile;
 649    char *vtag;
 650    int targc;
 651    char *targv[];
 652    int inattic;
 653{
 654    Vers_TS *vers;
 655    int letter;
 656    char *tocvsPath;
 657    char *expand;
 658    struct file_info finfo;
 659
 660    memset (&finfo, 0, sizeof finfo);
 661    finfo.file = vfile;
 662    /* Not used, so don't worry about it.  */
 663    finfo.update_dir = NULL;
 664    finfo.fullname = finfo.file;
 665    finfo.repository = repository;
 666    finfo.entries = NULL;
 667    finfo.rcs = NULL;
 668    vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL,
 669		       1, 0);
 670    if (vers->vn_rcs != NULL
 671	&& !RCS_isdead(vers->srcfile, vers->vn_rcs))
 672    {
 673	int different;
 674
 675	/*
 676	 * The rcs file does have a revision on the vendor branch. Compare
 677	 * this revision with the import file; if they match exactly, there
 678	 * is no need to install the new import file as a new revision to the
 679	 * branch.  Just tag the revision with the new import tags.
 680	 * 
 681	 * This is to try to cut down the number of "C" conflict messages for
 682	 * locally modified import source files.
 683	 */
 684	tocvsPath = wrap_tocvs_process_file (vfile);
 685	/* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is
 686           not NULL?  */
 687	expand = vers->srcfile->expand != NULL &&
 688			vers->srcfile->expand[0] == 'b' ? "-kb" : "-ko";
 689	different = RCS_cmp_file( vers->srcfile, vers->vn_rcs, (char **)NULL,
 690	                          (char *)NULL, expand, vfile );
 691	if (tocvsPath)
 692	    if (unlink_file_dir (tocvsPath) < 0)
 693		error (0, errno, "cannot remove %s", tocvsPath);
 694
 695	if (!different)
 696	{
 697	    int retval = 0;
 698
 699	    /*
 700	     * The two files are identical.  Just update the tags, print the
 701	     * "U", signifying that the file has changed, but needs no
 702	     * attention, and we're done.
 703	     */
 704	    if (add_tags (vers->srcfile, vfile, vtag, targc, targv))
 705		retval = 1;
 706	    add_log ('U', vfile);
 707	    freevers_ts (&vers);
 708	    return (retval);
 709	}
 710    }
 711
 712    /* We may have failed to parse the RCS file; check just in case */
 713    if (vers->srcfile == NULL ||
 714	add_rev (message, vers->srcfile, vfile, vers->vn_rcs) ||
 715	add_tags (vers->srcfile, vfile, vtag, targc, targv))
 716    {
 717	freevers_ts (&vers);
 718	return (1);
 719    }
 720
 721    if (vers->srcfile->branch == NULL || inattic ||
 722	strcmp (vers->srcfile->branch, vbranch) != 0)
 723    {
 724	conflicts++;
 725	letter = 'C';
 726    }
 727    else
 728	letter = 'U';
 729    add_log (letter, vfile);
 730
 731    freevers_ts (&vers);
 732    return (0);
 733}
 734
 735/*
 736 * Add the revision to the vendor branch
 737 */
 738static int
 739add_rev (message, rcs, vfile, vers)
 740    char *message;
 741    RCSNode *rcs;
 742    char *vfile;
 743    char *vers;
 744{
 745    int locked, status, ierrno;
 746    char *tocvsPath;
 747
 748    if (noexec)
 749	return (0);
 750
 751    locked = 0;
 752    if (vers != NULL)
 753    {
 754	/* Before RCS_lock existed, we were directing stdout, as well as
 755	   stderr, from the RCS command, to DEVNULL.  I wouldn't guess that
 756	   was necessary, but I don't know for sure.  */
 757	/* Earlier versions of this function printed a `fork failed' error
 758	   when RCS_lock returned an error code.  That's not appropriate
 759	   now that RCS_lock is librarified, but should the error text be
 760	   preserved? */
 761	if (RCS_lock (rcs, vbranch, 1) != 0)
 762	    return 1;
 763	locked = 1;
 764	RCS_rewrite (rcs, NULL, NULL);
 765    }
 766    tocvsPath = wrap_tocvs_process_file (vfile);
 767
 768    status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath,
 769			  message, vbranch, 0,
 770			  (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE
 771			   | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)));
 772    ierrno = errno;
 773
 774    if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0))
 775	error (0, errno, "cannot remove %s", tocvsPath);
 776
 777    if (status)
 778    {
 779	if (!noexec)
 780	{
 781	    fperrmsg (logfp, 0, status == -1 ? ierrno : 0,
 782		      "ERROR: Check-in of %s failed", rcs->path);
 783	    error (0, status == -1 ? ierrno : 0,
 784		   "ERROR: Check-in of %s failed", rcs->path);
 785	}
 786	if (locked)
 787	{
 788	    (void) RCS_unlock(rcs, vbranch, 0);
 789	    RCS_rewrite (rcs, NULL, NULL);
 790	}
 791	return (1);
 792    }
 793    return (0);
 794}
 795
 796/*
 797 * Add the vendor branch tag and all the specified import release tags to the
 798 * RCS file.  The vendor branch tag goes on the branch root (1.1.1) while the
 799 * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
 800 * 1.1.1.2, ...).
 801 */
 802static int
 803add_tags (rcs, vfile, vtag, targc, targv)
 804    RCSNode *rcs;
 805    char *vfile;
 806    char *vtag;
 807    int targc;
 808    char *targv[];
 809{
 810    int i, ierrno;
 811    Vers_TS *vers;
 812    int retcode = 0;
 813    struct file_info finfo;
 814
 815    if (noexec)
 816	return (0);
 817
 818    if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0)
 819    {
 820	ierrno = errno;
 821	fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
 822		  "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
 823	error (0, retcode == -1 ? ierrno : 0,
 824	       "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
 825	return (1);
 826    }
 827    RCS_rewrite (rcs, NULL, NULL);
 828
 829    memset (&finfo, 0, sizeof finfo);
 830    finfo.file = vfile;
 831    /* Not used, so don't worry about it.  */
 832    finfo.update_dir = NULL;
 833    finfo.fullname = finfo.file;
 834    finfo.repository = repository;
 835    finfo.entries = NULL;
 836    finfo.rcs = NULL;
 837    vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0);
 838    for (i = 0; i < targc; i++)
 839    {
 840	if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) == 0)
 841	    RCS_rewrite (rcs, NULL, NULL);
 842	else
 843	{
 844	    ierrno = errno;
 845	    fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
 846		      "WARNING: Couldn't add tag %s to %s", targv[i],
 847		      rcs->path);
 848	    error (0, retcode == -1 ? ierrno : 0,
 849		   "WARNING: Couldn't add tag %s to %s", targv[i],
 850		   rcs->path);
 851	}
 852    }
 853    freevers_ts (&vers);
 854    return (0);
 855}
 856
 857/*
 858 * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
 859 */
 860struct compair
 861{
 862    char *suffix, *comlead;
 863};
 864
 865static const struct compair comtable[] =
 866{
 867
 868/*
 869 * comtable pairs each filename suffix with a comment leader. The comment
 870 * leader is placed before each line generated by the $Log keyword. This
 871 * table is used to guess the proper comment leader from the working file's
 872 * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
 873 * languages without multiline comments; for others they are optional.
 874 *
 875 * I believe that the comment leader is unused if you are using RCS 5.7, which
 876 * decides what leader to use based on the text surrounding the $Log keyword
 877 * rather than a specified comment leader.
 878 */
 879    {"a", "-- "},			/* Ada		 */
 880    {"ada", "-- "},
 881    {"adb", "-- "},
 882    {"asm", ";; "},			/* assembler (MS-DOS) */
 883    {"ads", "-- "},			/* Ada		 */
 884    {"bas", "' "},    			/* Visual Basic code */
 885    {"bat", ":: "},			/* batch (MS-DOS) */
 886    {"body", "-- "},			/* Ada		 */
 887    {"c", " * "},			/* C		 */
 888    {"c++", "// "},			/* C++ in all its infinite guises */
 889    {"cc", "// "},
 890    {"cpp", "// "},
 891    {"cxx", "// "},
 892    {"m", "// "},			/* Objective-C */
 893    {"cl", ";;; "},			/* Common Lisp	 */
 894    {"cmd", ":: "},			/* command (OS/2) */
 895    {"cmf", "c "},			/* CM Fortran	 */
 896    {"cs", " * "},			/* C*		 */
 897    {"csh", "# "},			/* shell	 */
 898    {"dlg", " * "},   			/* MS Windows dialog file */
 899    {"e", "# "},			/* efl		 */
 900    {"epsf", "% "},			/* encapsulated postscript */
 901    {"epsi", "% "},			/* encapsulated postscript */
 902    {"el", "; "},			/* Emacs Lisp	 */
 903    {"f", "c "},			/* Fortran	 */
 904    {"for", "c "},
 905    {"frm", "' "},    			/* Visual Basic form */
 906    {"h", " * "},			/* C-header	 */
 907    {"hh", "// "},			/* C++ header	 */
 908    {"hpp", "// "},
 909    {"hxx", "// "},
 910    {"in", "# "},			/* for Makefile.in */
 911    {"l", " * "},			/* lex (conflict between lex and
 912					 * franzlisp) */
 913    {"mac", ";; "},			/* macro (DEC-10, MS-DOS, PDP-11,
 914					 * VMS, etc) */
 915    {"mak", "# "},    			/* makefile, e.g. Visual C++ */
 916    {"me", ".\\\" "},			/* me-macros	t/nroff	 */
 917    {"ml", "; "},			/* mocklisp	 */
 918    {"mm", ".\\\" "},			/* mm-macros	t/nroff	 */
 919    {"ms", ".\\\" "},			/* ms-macros	t/nroff	 */
 920    {"man", ".\\\" "},			/* man-macros	t/nroff	 */
 921    {"1", ".\\\" "},			/* feeble attempt at man pages... */
 922    {"2", ".\\\" "},
 923    {"3", ".\\\" "},
 924    {"4", ".\\\" "},
 925    {"5", ".\\\" "},
 926    {"6", ".\\\" "},
 927    {"7", ".\\\" "},
 928    {"8", ".\\\" "},
 929    {"9", ".\\\" "},
 930    {"p", " * "},			/* pascal	 */
 931    {"pas", " * "},
 932    {"pl", "# "},			/* perl	(conflict with Prolog) */
 933    {"ps", "% "},			/* postscript	 */
 934    {"psw", "% "},			/* postscript wrap */
 935    {"pswm", "% "},			/* postscript wrap */
 936    {"r", "# "},			/* ratfor	 */
 937    {"rc", " * "},			/* Microsoft Windows resource file */
 938    {"red", "% "},			/* psl/rlisp	 */
 939#ifdef sparc
 940    {"s", "! "},			/* assembler	 */
 941#endif
 942#ifdef mc68000
 943    {"s", "| "},			/* assembler	 */
 944#endif
 945#ifdef pdp11
 946    {"s", "/ "},			/* assembler	 */
 947#endif
 948#ifdef vax
 949    {"s", "# "},			/* assembler	 */
 950#endif
 951#ifdef __ksr__
 952    {"s", "# "},			/* assembler	 */
 953    {"S", "# "},			/* Macro assembler */
 954#endif
 955    {"sh", "# "},			/* shell	 */
 956    {"sl", "% "},			/* psl		 */
 957    {"spec", "-- "},			/* Ada		 */
 958    {"tex", "% "},			/* tex		 */
 959    {"y", " * "},			/* yacc		 */
 960    {"ye", " * "},			/* yacc-efl	 */
 961    {"yr", " * "},			/* yacc-ratfor	 */
 962    {"", "# "},				/* default for empty suffix	 */
 963    {NULL, "# "}			/* default for unknown suffix;	 */
 964/* must always be last		 */
 965};
 966
 967static char *
 968get_comment (user)
 969    const char *user;
 970{
 971    char *cp, *suffix;
 972    char *suffix_path;
 973    int i;
 974    char *retval;
 975
 976    suffix_path = xmalloc (strlen (user) + 5);
 977    cp = strrchr (user, '.');
 978    if (cp != NULL)
 979    {
 980	cp++;
 981
 982	/*
 983	 * Convert to lower-case, since we are not concerned about the
 984	 * case-ness of the suffix.
 985	 */
 986	(void) strcpy (suffix_path, cp);
 987	for (cp = suffix_path; *cp; cp++)
 988	    if (isupper ((unsigned char) *cp))
 989		*cp = tolower (*cp);
 990	suffix = suffix_path;
 991    }
 992    else
 993	suffix = "";			/* will use the default */
 994    for (i = 0;; i++)
 995    {
 996	if (comtable[i].suffix == NULL)
 997	{
 998	    /* Default.  Note we'll always hit this case before we
 999	       ever return NULL.  */
1000	    retval = comtable[i].comlead;
1001	    break;
1002	}
1003	if (strcmp (suffix, comtable[i].suffix) == 0)
1004	{
1005	    retval = comtable[i].comlead;
1006	    break;
1007	}
1008    }
1009    free (suffix_path);
1010    return retval;
1011}
1012
1013/* Create a new RCS file from scratch.
1014
1015   This probably should be moved to rcs.c now that it is called from
1016   places outside import.c.
1017
1018   Return value is 0 for success, or nonzero for failure (in which
1019   case an error message will have already been printed).  */
1020int
1021add_rcs_file (message, rcs, user, add_vhead, key_opt,
1022	      add_vbranch, vtag, targc, targv,
1023	      desctext, desclen, add_logfp)
1024    /* Log message for the addition.  Not used if add_vhead == NULL.  */
1025    const char *message;
1026    /* Filename of the RCS file to create.  */
1027    const char *rcs;
1028    /* Filename of the file to serve as the contents of the initial
1029       revision.  Even if add_vhead is NULL, we use this to determine
1030       the modes to give the new RCS file.  */
1031    const char *user;
1032
1033    /* Revision number of head that we are adding.  Normally 1.1 but
1034       could be another revision as long as ADD_VBRANCH is a branch
1035       from it.  If NULL, then just add an empty file without any
1036       revisions (similar to the one created by "rcs -i").  */
1037    const char *add_vhead;
1038
1039    /* Keyword expansion mode, e.g., "b" for binary.  NULL means the
1040       default behavior.  */
1041    const char *key_opt;
1042
1043    /* Vendor branch to import to, or NULL if none.  If non-NULL, then
1044       vtag should also be non-NULL.  */
1045    const char *add_vbranch;
1046    const char *vtag;
1047    int targc;
1048    char *targv[];
1049
1050    /* If non-NULL, description for the file.  If NULL, the description
1051       will be empty.  */
1052    const char *desctext;
1053    size_t desclen;
1054
1055    /* Write errors to here as well as via error (), or NULL if we should
1056       use only error ().  */
1057    FILE *add_logfp;
1058{
1059    FILE *fprcs, *fpuser;
1060    struct stat sb;
1061    struct tm *ftm;
1062    time_t now;
1063    char altdate1[MAXDATELEN];
1064    char *author;
1065    int i, ierrno, err = 0;
1066    mode_t mode;
1067    char *tocvsPath;
1068    const char *userfile;
1069    char *free_opt = NULL;
1070    mode_t file_type;
1071
1072    if (noexec)
1073	return (0);
1074
1075    /* Note that as the code stands now, the -k option overrides any
1076       settings in wrappers (whether CVSROOT/cvswrappers, -W, or
1077       whatever).  Some have suggested this should be the other way
1078       around.  As far as I know the documentation doesn't say one way
1079       or the other.  Before making a change of this sort, should think
1080       about what is best, document it (in cvs.texinfo and NEWS), &c.  */
1081
1082    if (key_opt == NULL)
1083    {
1084	if (wrap_name_has (user, WRAP_RCSOPTION))
1085	{
1086	    key_opt = free_opt = wrap_rcsoption (user, 0);
1087	}
1088    }
1089
1090    tocvsPath = wrap_tocvs_process_file (user);
1091    userfile = (tocvsPath == NULL ? user : tocvsPath);
1092
1093    /* Opening in text mode is probably never the right thing for the
1094       server (because the protocol encodes text files in a fashion
1095       which does not depend on what the client or server OS is, as
1096       documented in cvsclient.texi), but as long as the server just
1097       runs on unix it is a moot point.  */
1098
1099    /* If PreservePermissions is set, then make sure that the file
1100       is a plain file before trying to open it.  Longstanding (although
1101       often unpopular) CVS behavior has been to follow symlinks, so we
1102       maintain that behavior if PreservePermissions is not on.
1103
1104       NOTE: this error message used to be `cannot fstat', but is now
1105       `cannot lstat'.  I don't see a way around this, since we must
1106       stat the file before opening it. -twp */
1107
1108    if (CVS_LSTAT (userfile, &sb) < 0)
1109    {
1110	/* not fatal, continue import */
1111	if (add_logfp != NULL)
1112	    fperrmsg (add_logfp, 0, errno,
1113			  "ERROR: cannot lstat file %s", userfile);
1114	error (0, errno, "cannot lstat file %s", userfile);
1115	goto read_error;
1116    }
1117    file_type = sb.st_mode & S_IFMT;
1118
1119    fpuser = NULL;
1120    if (!preserve_perms || file_type == S_IFREG)
1121    {
1122	fpuser = CVS_FOPEN (userfile,
1123			    ((key_opt != NULL && strcmp (key_opt, "b") == 0)
1124			     ? "rb"
1125			     : "r")
1126	    );
1127	if (fpuser == NULL)
1128	{
1129	    /* not fatal, continue import */
1130	    if (add_logfp != NULL)
1131		fperrmsg (add_logfp, 0, errno,
1132			  "ERROR: cannot read file %s", userfile);
1133	    error (0, errno, "ERROR: cannot read file %s", userfile);
1134	    goto read_error;
1135	}
1136    }
1137
1138    fprcs = CVS_FOPEN (rcs, "w+b");
1139    if (fprcs == NULL)
1140    {
1141	ierrno = errno;
1142	goto write_error_noclose;
1143    }
1144
1145    /*
1146     * putadmin()
1147     */
1148    if (add_vhead != NULL)
1149    {
1150	if (fprintf (fprcs, "head     %s;\012", add_vhead) < 0)
1151	    goto write_error;
1152    }
1153    else
1154    {
1155	if (fprintf (fprcs, "head     ;\012") < 0)
1156	    goto write_error;
1157    }
1158
1159    if (add_vbranch != NULL)
1160    {
1161	if (fprintf (fprcs, "branch   %s;\012", add_vbranch) < 0)
1162	    goto write_error;
1163    }
1164    if (fprintf (fprcs, "access   ;\012") < 0 ||
1165	fprintf (fprcs, "symbols  ") < 0)
1166    {
1167	goto write_error;
1168    }
1169
1170    for (i = targc - 1; i >= 0; i--)
1171    {
1172	/* RCS writes the symbols backwards */
1173	assert (add_vbranch != NULL);
1174	if (fprintf (fprcs, "%s:%s.1 ", targv[i], add_vbranch) < 0)
1175	    goto write_error;
1176    }
1177
1178    if (add_vbranch != NULL)
1179    {
1180	if (fprintf (fprcs, "%s:%s", vtag, add_vbranch) < 0)
1181	    goto write_error;
1182    }
1183    if (fprintf (fprcs, ";\012") < 0)
1184	goto write_error;
1185
1186    if (fprintf (fprcs, "locks    ; strict;\012") < 0 ||
1187	/* XXX - make sure @@ processing works in the RCS file */
1188	fprintf (fprcs, "comment  @%s@;\012", get_comment (user)) < 0)
1189    {
1190	goto write_error;
1191    }
1192
1193    if (key_opt != NULL && strcmp (key_opt, "kv") != 0)
1194    {
1195	if (fprintf (fprcs, "expand   @%s@;\012", key_opt) < 0)
1196	{
1197	    goto write_error;
1198	}
1199    }
1200
1201    if (fprintf (fprcs, "\012") < 0)
1202      goto write_error;
1203
1204    /* Write the revision(s), with the date and author and so on
1205       (that is "delta" rather than "deltatext" from rcsfile(5)).  */
1206    if (add_vhead != NULL)
1207    {
1208	if (use_file_modtime)
1209	    now = sb.st_mtime;
1210	else
1211	    (void) time (&now);
1212	ftm = gmtime (&now);
1213	(void) sprintf (altdate1, DATEFORM,
1214			ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
1215			ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1216			ftm->tm_min, ftm->tm_sec);
1217	author = getcaller ();
1218
1219	if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
1220	fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
1221		 altdate1, author) < 0)
1222	goto write_error;
1223
1224	if (fprintf (fprcs, "branches") < 0)
1225	    goto write_error;
1226	if (add_vbranch != NULL)
1227	{
1228	    if (fprintf (fprcs, " %s.1", add_vbranch) < 0)
1229		goto write_error;
1230	}
1231	if (fprintf (fprcs, ";\012") < 0)
1232	    goto write_error;
1233
1234	if (fprintf (fprcs, "next     ;\012") < 0)
1235	    goto write_error;
1236
1237#ifdef PRESERVE_PERMISSIONS_SUPPORT
1238	/* Store initial permissions if necessary. */
1239	if (preserve_perms)
1240	{
1241	    if (file_type == S_IFLNK)
1242	    {
1243		char *link = xreadlink (userfile);
1244		if (fprintf (fprcs, "symlink\t@") < 0 ||
1245		    expand_at_signs (link, strlen (link), fprcs) < 0 ||
1246		    fprintf (fprcs, "@;\012") < 0)
1247		    goto write_error;
1248		free (link);
1249	    }
1250	    else
1251	    {
1252		if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0)
1253		    goto write_error;
1254		if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0)
1255		    goto write_error;
1256		if (fprintf (fprcs, "permissions\t%o;\012",
1257			     sb.st_mode & 07777) < 0)
1258		    goto write_error;
1259		switch (file_type)
1260		{
1261		    case S_IFREG: break;
1262		    case S_IFCHR:
1263		    case S_IFBLK:
1264#ifdef HAVE_STRUCT_STAT_ST_RDEV
1265			if (fprintf (fprcs, "special\t%s %lu;\012",
1266				     (file_type == S_IFCHR
1267				      ? "character"
1268				      : "block"),
1269				     (unsigned long) sb.st_rdev) < 0)
1270			    goto write_error;
1271#else
1272			error (0, 0,
1273"can't import %s: unable to import device files on this system",
1274userfile);
1275#endif
1276			break;
1277		    default:
1278			error (0, 0,
1279			       "can't import %s: unknown kind of special file",
1280			       userfile);
1281		}
1282	    }
1283	}
1284#endif
1285
1286	if (add_vbranch != NULL)
1287	{
1288	    if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
1289		fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
1290			 altdate1, author) < 0 ||
1291		fprintf (fprcs, "branches ;\012") < 0 ||
1292		fprintf (fprcs, "next     ;\012") < 0)
1293		goto write_error;
1294
1295#ifdef PRESERVE_PERMISSIONS_SUPPORT
1296	    /* Store initial permissions if necessary. */
1297	    if (preserve_perms)
1298	    {
1299		if (file_type == S_IFLNK)
1300		{
1301		    char *link = xreadlink (userfile);
1302		    if (fprintf (fprcs, "symlink\t@") < 0 ||
1303			expand_at_signs (link, strlen (link), fprcs) < 0 ||
1304			fprintf (fprcs, "@;\012") < 0)
1305			goto write_error;
1306		    free (link);
1307		}
1308		else
1309		{
1310		    if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 ||
1311			fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 ||
1312			fprintf (fprcs, "permissions\t%o;\012",
1313				 sb.st_mode & 07777) < 0)
1314			goto write_error;
1315	    
1316		    switch (file_type)
1317		    {
1318			case S_IFREG: break;
1319			case S_IFCHR:
1320			case S_IFBLK:
1321#ifdef HAVE_STRUCT_STAT_ST_RDEV
1322			    if (fprintf (fprcs, "special\t%s %lu;\012",
1323					 (file_type == S_IFCHR
1324					  ? "character"
1325					  : "block"),
1326					 (unsigned long) sb.st_rdev) < 0)
1327				goto write_error;
1328#else
1329			    error (0, 0,
1330"can't import %s: unable to import device files on this system",
1331userfile);
1332#endif
1333			    break;
1334			default:
1335			    error (0, 0,
1336			      "cannot import %s: special file of unknown type",
1337			       userfile);
1338		    }
1339		}
1340	    }
1341#endif
1342
1343	    if (fprintf (fprcs, "\012") < 0)
1344		goto write_error;
1345	}
1346    }
1347
1348    /* Now write the description (possibly empty).  */
1349    if (fprintf (fprcs, "\012desc\012") < 0 ||
1350	fprintf (fprcs, "@") < 0)
1351	goto write_error;
1352    if (desctext != NULL)
1353    {
1354	/* The use of off_t not size_t for the second argument is very
1355	   strange, since we are dealing with something which definitely
1356	   fits in memory.  */
1357	if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0)
1358	    goto write_error;
1359    }
1360    if (fprintf (fprcs, "@\012\012\012") < 0)
1361	goto write_error;
1362
1363    /* Now write the log messages and contents for the revision(s) (that
1364       is, "deltatext" rather than "delta" from rcsfile(5)).  */
1365    if (add_vhead != NULL)
1366    {
1367	if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
1368	    fprintf (fprcs, "log\012@") < 0)
1369	    goto write_error;
1370	if (add_vbranch != NULL)
1371	{
1372	    /* We are going to put the log message in the revision on the
1373	       branch.  So putting it here too seems kind of redundant, I
1374	       guess (and that is what CVS has always done, anyway).  */
1375	    if (fprintf (fprcs, "Initial revision\012") < 0)
1376		goto write_error;
1377	}
1378	else
1379	{
1380	    if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0)
1381		goto write_error;
1382	}
1383	if (fprintf (fprcs, "@\012") < 0 ||
1384	    fprintf (fprcs, "text\012@") < 0)
1385	{
1386	    goto write_error;
1387	}
1388
1389	/* Now copy over the contents of the file, expanding at signs.
1390	   If preserve_perms is set, do this only for regular files. */
1391	if (!preserve_perms || file_type == S_IFREG)
1392	{
1393	    char buf[8192];
1394	    unsigned int len;
1395
1396	    while (1)
1397	    {
1398		len = fread (buf, 1, sizeof buf, fpuser);
1399		if (len == 0)
1400		{
1401		    if (ferror (fpuser))
1402			error (1, errno, "cannot read file %s for copying",
1403			       user);
1404		    break;
1405		}
1406		if (expand_at_signs (buf, len, fprcs) < 0)
1407		    goto write_error;
1408	    }
1409	}
1410	if (fprintf (fprcs, "@\012\012") < 0)
1411	    goto write_error;
1412	if (add_vbranch != NULL)
1413	{
1414	    if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
1415		fprintf (fprcs, "log\012@") < 0 ||
1416		expand_at_signs (message,
1417				 (off_t) strlen (message), fprcs) < 0 ||
1418		fprintf (fprcs, "@\012text\012") < 0 ||
1419		fprintf (fprcs, "@@\012") < 0)
1420		goto write_error;
1421	}
1422    }
1423
1424    if (fclose (fprcs) == EOF)
1425    {
1426	ierrno = errno;
1427	goto write_error_noclose;
1428    }
1429    /* Close fpuser only if we opened it to begin with. */
1430    if (fpuser != NULL)
1431    {
1432	if (fclose (fpuser) < 0)
1433	    error (0, errno, "cannot close %s", user);
1434    }
1435
1436    /*
1437     * Fix the modes on the RCS files.  The user modes of the original
1438     * user file are propagated to the group and other modes as allowed
1439     * by the repository umask, except that all write permissions are
1440     * turned off.
1441     */
1442    mode = (sb.st_mode |
1443	    (sb.st_mode & S_IRWXU) >> 3 |
1444	    (sb.st_mode & S_IRWXU) >> 6) &
1445	   ~cvsumask &
1446	   ~(S_IWRITE | S_IWGRP | S_IWOTH);
1447    if (chmod (rcs, mode) < 0)
1448    {
1449	ierrno = errno;
1450	if (add_logfp != NULL)
1451	    fperrmsg (add_logfp, 0, ierrno,
1452		      "WARNING: cannot change mode of file %s", rcs);
1453	error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
1454	err++;
1455    }
1456    if (tocvsPath)
1457	if (unlink_file_dir (tocvsPath) < 0)
1458		error (0, errno, "cannot remove %s", tocvsPath);
1459    if (free_opt != NULL)
1460	free (free_opt);
1461    return (err);
1462
1463write_error:
1464    ierrno = errno;
1465    if (fclose (fprcs) < 0)
1466	error (0, errno, "cannot close %s", rcs);
1467write_error_noclose:
1468    if (fclose (fpuser) < 0)
1469	error (0, errno, "cannot close %s", user);
1470    if (add_logfp != NULL)
1471	fperrmsg (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
1472    error (0, ierrno, "ERROR: cannot write file %s", rcs);
1473    if (ierrno == ENOSPC)
1474    {
1475	if (CVS_UNLINK (rcs) < 0)
1476	    error (0, errno, "cannot remove %s", rcs);
1477	if (add_logfp != NULL)
1478	    fperrmsg (add_logfp, 0, 0, "ERROR: out of space - aborting");
1479	error (1, 0, "ERROR: out of space - aborting");
1480    }
1481read_error:
1482    if (tocvsPath)
1483	if (unlink_file_dir (tocvsPath) < 0)
1484	    error (0, errno, "cannot remove %s", tocvsPath);
1485
1486    if (free_opt != NULL)
1487	free (free_opt);
1488
1489    return (err + 1);
1490}
1491
1492/*
1493 * Write SIZE bytes at BUF to FP, expanding @ signs into double @
1494 * signs.  If an error occurs, return a negative value and set errno
1495 * to indicate the error.  If not, return a nonnegative value.
1496 */
1497int
1498expand_at_signs (buf, size, fp)
1499    const char *buf;
1500    off_t size;
1501    FILE *fp;
1502{
1503    register const char *cp, *next;
1504
1505    cp = buf;
1506    while ((next = memchr (cp, '@', size)) != NULL)
1507    {
1508	size_t len = ++next - cp;
1509	if (fwrite (cp, 1, len, fp) != len)
1510	    return EOF;
1511	if (putc ('@', fp) == EOF)
1512	    return EOF;
1513	cp = next;
1514	size -= len;
1515    }
1516
1517    if (fwrite (cp, 1, size, fp) != size)
1518	return EOF;
1519
1520    return 1;
1521}
1522
1523/*
1524 * Write an update message to (potentially) the screen and the log file.
1525 */
1526static void
1527add_log (ch, fname)
1528    int ch;
1529    char *fname;
1530{
1531    if (!really_quiet)			/* write to terminal */
1532    {
1533	char buf[2];
1534	buf[0] = ch;
1535	buf[1] = ' ';
1536	cvs_output (buf, 2);
1537	if (repos_len)
1538	{
1539	    cvs_output (repository + repos_len + 1, 0);
1540	    cvs_output ("/", 1);
1541	}
1542	else if (repository[0] != '\0')
1543	{
1544	    cvs_output (repository, 0);
1545	    cvs_output ("/", 1);
1546	}
1547	cvs_output (fname, 0);
1548	cvs_output ("\n", 1);
1549    }
1550
1551    if (repos_len)			/* write to logfile */
1552	(void) fprintf (logfp, "%c %s/%s\n", ch,
1553			repository + repos_len + 1, fname);
1554    else if (repository[0])
1555	(void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
1556    else
1557	(void) fprintf (logfp, "%c %s\n", ch, fname);
1558}
1559
1560/*
1561 * This is the recursive function that walks the argument directory looking
1562 * for sub-directories that have CVS administration files in them and updates
1563 * them recursively.
1564 * 
1565 * Note that we do not follow symbolic links here, which is a feature!
1566 */
1567static int
1568import_descend_dir (message, dir, vtag, targc, targv)
1569    char *message;
1570    char *dir;
1571    char *vtag;
1572    int targc;
1573    char *targv[];
1574{
1575    struct saved_cwd cwd;
1576    char *cp;
1577    int ierrno, err;
1578    char *rcs = NULL;
1579
1580    if (islink (dir))
1581	return (0);
1582    if (save_cwd (&cwd))
1583    {
1584	fperrmsg (logfp, 0, 0, "ERROR: cannot get working directory");
1585	return (1);
1586    }
1587
1588    /* Concatenate DIR to the end of REPOSITORY.  */
1589    if (repository[0] == '\0')
1590    {
1591	char *new = xstrdup (dir);
1592	free (repository);
1593	repository = new;
1594    }
1595    else
1596    {
1597	char *new = xmalloc (strlen (repository) + strlen (dir) + 10);
1598	strcpy (new, repository);
1599	(void) strcat (new, "/");
1600	(void) strcat (new, dir);
1601	free (repository);
1602	repository = new;
1603    }
1604
1605    if (!quiet && !current_parsed_root->isremote)
1606	error (0, 0, "Importing %s", repository);
1607
1608    if ( CVS_CHDIR (dir) < 0)
1609    {
1610	ierrno = errno;
1611	fperrmsg (logfp, 0, ierrno, "ERROR: cannot chdir to %s", dir);
1612	error (0, ierrno, "ERROR: cannot chdir to %s", dir);
1613	err = 1;
1614	goto out;
1615    }
1616    if (!current_parsed_root->isremote && !isdir (repository))
1617    {
1618	rcs = xmalloc (strlen (repository) + sizeof (RCSEXT) + 5);
1619	(void) sprintf (rcs, "%s%s", repository, RCSEXT);
1620	if (isfile (repository) || isfile(rcs))
1621	{
1622	    fperrmsg (logfp, 0, 0,
1623		      "ERROR: %s is a file, should be a directory!",
1624		      repository);
1625	    error (0, 0, "ERROR: %s is a file, should be a directory!",
1626		   repository);
1627	    err = 1;
1628	    goto out;
1629	}
1630	if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0)
1631	{
1632	    ierrno = errno;
1633	    fperrmsg (logfp, 0, ierrno,
1634		      "ERROR: cannot mkdir %s -- not added", repository);
1635	    error (0, ierrno,
1636		   "ERROR: cannot mkdir %s -- not added", repository);
1637	    err = 1;
1638	    goto out;
1639	}
1640    }
1641    err = import_descend (message, vtag, targc, targv);
1642  out:
1643    if (rcs != NULL)
1644	free (rcs);
1645    if ((cp = strrchr (repository, '/')) != NULL)
1646	*cp = '\0';
1647    else
1648	repository[0] = '\0';
1649    if (restore_cwd (&cwd, NULL))
1650	error_exit ();
1651    free_cwd (&cwd);
1652    return (err);
1653}