Home | History | Annotate | Line # | Download | only in src
      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  * Commit Files
     14  *
     15  * "commit" commits the present version to the RCS repository, AFTER
     16  * having done a test on conflicts.
     17  *
     18  * The call is: cvs commit [options] files...
     19  *
     20  */
     21 #include <sys/cdefs.h>
     22 __RCSID("$NetBSD: commit.c,v 1.5 2016/05/17 14:00:09 christos Exp $");
     23 
     24 #include "cvs.h"
     25 #include "getline.h"
     26 #include "edit.h"
     27 #include "fileattr.h"
     28 #include "hardlink.h"
     29 
     30 static Dtype check_direntproc (void *callerdat, const char *dir,
     31                                const char *repos, const char *update_dir,
     32                                List *entries);
     33 static int check_fileproc (void *callerdat, struct file_info *finfo);
     34 static int check_filesdoneproc (void *callerdat, int err, const char *repos,
     35 				const char *update_dir, List *entries);
     36 static int checkaddfile (const char *file, const char *repository,
     37                          const char *tag, const char *options,
     38                          RCSNode **rcsnode);
     39 static Dtype commit_direntproc (void *callerdat, const char *dir,
     40                                 const char *repos, const char *update_dir,
     41                                 List *entries);
     42 static int commit_dirleaveproc (void *callerdat, const char *dir, int err,
     43 				const char *update_dir, List *entries);
     44 static int commit_fileproc (void *callerdat, struct file_info *finfo);
     45 static int commit_filesdoneproc (void *callerdat, int err,
     46                                  const char *repository,
     47 				 const char *update_dir, List *entries);
     48 static int finaladd (struct file_info *finfo, char *revision, char *tag,
     49 		     char *options);
     50 static int findmaxrev (Node * p, void *closure);
     51 static int lock_RCS (const char *user, RCSNode *rcs, const char *rev,
     52                      const char *repository);
     53 static int precommit_list_to_args_proc (Node * p, void *closure);
     54 static int precommit_proc (const char *repository, const char *filter,
     55                            void *closure);
     56 static int remove_file (struct file_info *finfo, char *tag,
     57 			char *message);
     58 static void fixaddfile (const char *rcs);
     59 static void fixbranch (RCSNode *, char *branch);
     60 static void unlockrcs (RCSNode *rcs);
     61 static void ci_delproc (Node *p);
     62 static void masterlist_delproc (Node *p);
     63 
     64 struct commit_info
     65 {
     66     Ctype status;			/* as returned from Classify_File() */
     67     char *rev;				/* a numeric rev, if we know it */
     68     char *tag;				/* any sticky tag, or -r option */
     69     char *options;			/* Any sticky -k option */
     70 };
     71 struct master_lists
     72 {
     73     List *ulist;			/* list for Update_Logfile */
     74     List *cilist;			/* list with commit_info structs */
     75 };
     76 
     77 static int check_valid_edit = 0;
     78 static int force_ci = 0;
     79 static int got_message;
     80 static int aflag;
     81 static char *saved_tag;
     82 static char *write_dirtag;
     83 static int write_dirnonbranch;
     84 static char *logfile;
     85 static List *mulist;
     86 static char *saved_message;
     87 static time_t last_register_time;
     88 
     89 static const char *const commit_usage[] =
     90 {
     91     "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n",
     92     "    -c          Check for valid edits before committing.\n",
     93     "    -R          Process directories recursively.\n",
     94     "    -l          Local directory only (not recursive).\n",
     95     "    -f          Force the file to be committed; disables recursion.\n",
     96     "    -F logfile  Read the log message from file.\n",
     97     "    -m msg      Log message.\n",
     98     "    -r rev      Commit to this branch or trunk revision.\n",
     99     "(Specify the --help global option for a list of other help options)\n",
    100     NULL
    101 };
    102 
    103 #ifdef CLIENT_SUPPORT
    104 /* Identify a file which needs "? foo" or a Questionable request.  */
    105 struct question
    106 {
    107     /* The two fields for the Directory request.  */
    108     char *dir;
    109     char *repos;
    110 
    111     /* The file name.  */
    112     char *file;
    113 
    114     struct question *next;
    115 };
    116 
    117 struct find_data
    118 {
    119     List *ulist;
    120     int argc;
    121     char **argv;
    122 
    123     /* This is used from dirent to filesdone time, for each directory,
    124        to make a list of files we have already seen.  */
    125     List *ignlist;
    126 
    127     /* Linked list of files which need "? foo" or a Questionable request.  */
    128     struct question *questionables;
    129 
    130     /* Only good within functions called from the filesdoneproc.  Stores
    131        the repository (pointer into storage managed by the recursion
    132        processor.  */
    133     const char *repository;
    134 
    135     /* Non-zero if we should force the commit.  This is enabled by
    136        either -f or -r options, unlike force_ci which is just -f.  */
    137     int force;
    138 };
    139 
    140 
    141 
    142 static Dtype
    143 find_dirent_proc (void *callerdat, const char *dir, const char *repository,
    144                   const char *update_dir, List *entries)
    145 {
    146     struct find_data *find_data = callerdat;
    147 
    148     /* This check seems to slowly be creeping throughout CVS (update
    149        and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995.  My guess
    150        is that it (or some variant thereof) should go in all the
    151        dirent procs.  Unless someone has some better idea...  */
    152     if (!isdir (dir))
    153 	return R_SKIP_ALL;
    154 
    155     /* initialize the ignore list for this directory */
    156     find_data->ignlist = getlist ();
    157 
    158     /* Print the same warm fuzzy as in check_direntproc, since that
    159        code will never be run during client/server operation and we
    160        want the messages to match. */
    161     if (!quiet)
    162 	error (0, 0, "Examining %s", update_dir);
    163 
    164     return R_PROCESS;
    165 }
    166 
    167 
    168 
    169 /* Here as a static until we get around to fixing ignore_files to pass
    170    it along as an argument.  */
    171 static struct find_data *find_data_static;
    172 
    173 
    174 
    175 static void
    176 find_ignproc (const char *file, const char *dir)
    177 {
    178     struct question *p;
    179 
    180     p = xmalloc (sizeof (struct question));
    181     p->dir = xstrdup (dir);
    182     p->repos = xstrdup (find_data_static->repository);
    183     p->file = xstrdup (file);
    184     p->next = find_data_static->questionables;
    185     find_data_static->questionables = p;
    186 }
    187 
    188 
    189 
    190 static int
    191 find_filesdoneproc (void *callerdat, int err, const char *repository,
    192                     const char *update_dir, List *entries)
    193 {
    194     struct find_data *find_data = callerdat;
    195     find_data->repository = repository;
    196 
    197     /* if this directory has an ignore list, process it then free it */
    198     if (find_data->ignlist)
    199     {
    200 	find_data_static = find_data;
    201 	ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
    202 	dellist (&find_data->ignlist);
    203     }
    204 
    205     find_data->repository = NULL;
    206 
    207     return err;
    208 }
    209 
    210 
    211 
    212 /* Machinery to find out what is modified, added, and removed.  It is
    213    possible this should be broken out into a new client_classify function;
    214    merging it with classify_file is almost sure to be a mess, though,
    215    because classify_file has all kinds of repository processing.  */
    216 static int
    217 find_fileproc (void *callerdat, struct file_info *finfo)
    218 {
    219     Vers_TS *vers;
    220     enum classify_type status;
    221     Node *node;
    222     struct find_data *args = callerdat;
    223     struct logfile_info *data;
    224     struct file_info xfinfo;
    225 
    226     /* if this directory has an ignore list, add this file to it */
    227     if (args->ignlist)
    228     {
    229 	Node *p;
    230 
    231 	p = getnode ();
    232 	p->type = FILES;
    233 	p->key = xstrdup (finfo->file);
    234 	if (addnode (args->ignlist, p) != 0)
    235 	    freenode (p);
    236     }
    237 
    238     xfinfo = *finfo;
    239     xfinfo.repository = NULL;
    240     xfinfo.rcs = NULL;
    241 
    242     vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0);
    243     if (vers->vn_user == NULL)
    244     {
    245 	if (vers->ts_user == NULL)
    246 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
    247 	else
    248 	    error (0, 0, "use `%s add' to create an entry for `%s'",
    249 		   program_name, finfo->fullname);
    250 	freevers_ts (&vers);
    251 	return 1;
    252     }
    253     if (vers->vn_user[0] == '-')
    254     {
    255 	if (vers->ts_user != NULL)
    256 	{
    257 	    error (0, 0,
    258 		   "`%s' should be removed and is still there (or is back"
    259 		   " again)", finfo->fullname);
    260 	    freevers_ts (&vers);
    261 	    return 1;
    262 	}
    263 	/* else */
    264 	status = T_REMOVED;
    265     }
    266     else if (strcmp (vers->vn_user, "0") == 0)
    267     {
    268 	if (vers->ts_user == NULL)
    269 	{
    270 	    /* This happens when one has `cvs add'ed a file, but it no
    271 	       longer exists in the working directory at commit time.
    272 	       FIXME: What classify_file does in this case is print
    273 	       "new-born %s has disappeared" and removes the entry.
    274 	       We probably should do the same.  */
    275 	    if (!really_quiet)
    276 		error (0, 0, "warning: new-born %s has disappeared",
    277 		       finfo->fullname);
    278 	    status = T_REMOVE_ENTRY;
    279 	}
    280 	else
    281 	    status = T_ADDED;
    282     }
    283     else if (vers->ts_user == NULL)
    284     {
    285 	/* FIXME: What classify_file does in this case is print
    286 	   "%s was lost".  We probably should do the same.  */
    287 	freevers_ts (&vers);
    288 	return 0;
    289     }
    290     else if (vers->ts_rcs != NULL
    291 	     && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0))
    292 	/* If we are forcing commits, pretend that the file is
    293            modified.  */
    294 	status = T_MODIFIED;
    295     else
    296     {
    297 	/* This covers unmodified files, as well as a variety of other
    298 	   cases.  FIXME: we probably should be printing a message and
    299 	   returning 1 for many of those cases (but I'm not sure
    300 	   exactly which ones).  */
    301 	freevers_ts (&vers);
    302 	return 0;
    303     }
    304 
    305     node = getnode ();
    306     node->key = xstrdup (finfo->fullname);
    307 
    308     data = xmalloc (sizeof (struct logfile_info));
    309     data->type = status;
    310     data->tag = xstrdup (vers->tag);
    311     data->rev_old = data->rev_new = NULL;
    312 
    313     node->type = UPDATE;
    314     node->delproc = update_delproc;
    315     node->data = data;
    316     (void)addnode (args->ulist, node);
    317 
    318     ++args->argc;
    319 
    320     freevers_ts (&vers);
    321     return 0;
    322 }
    323 
    324 
    325 
    326 static int
    327 copy_ulist (Node *node, void *data)
    328 {
    329     struct find_data *args = data;
    330     args->argv[args->argc++] = node->key;
    331     return 0;
    332 }
    333 #endif /* CLIENT_SUPPORT */
    334 
    335 
    336 
    337 #ifdef SERVER_SUPPORT
    338 # define COMMIT_OPTIONS "+cnlRm:fF:r:"
    339 #else /* !SERVER_SUPPORT */
    340 # define COMMIT_OPTIONS "+clRm:fF:r:"
    341 #endif /* SERVER_SUPPORT */
    342 int
    343 commit (int argc, char **argv)
    344 {
    345     int c;
    346     int err = 0;
    347     int local = 0;
    348 
    349     if (argc == -1)
    350 	usage (commit_usage);
    351 
    352 #ifdef CVS_BADROOT
    353     /*
    354      * For log purposes, do not allow "root" to commit files.  If you look
    355      * like root, but are really logged in as a non-root user, it's OK.
    356      */
    357     /* FIXME: Shouldn't this check be much more closely related to the
    358        readonly user stuff (CVSROOT/readers, &c).  That is, why should
    359        root be able to "cvs init", "cvs import", &c, but not "cvs ci"?  */
    360     /* Who we are on the client side doesn't affect logging.  */
    361     if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote)
    362     {
    363 	struct passwd *pw;
    364 
    365 	if ((pw = getpwnam (getcaller ())) == NULL)
    366 	    error (1, 0,
    367                    "your apparent username (%s) is unknown to this system",
    368                    getcaller ());
    369 	if (pw->pw_uid == (uid_t) 0)
    370 	    error (1, 0, "'root' is not allowed to commit files");
    371     }
    372 #endif /* CVS_BADROOT */
    373 
    374     getoptreset ();
    375     while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1)
    376     {
    377 	switch (c)
    378 	{
    379             case 'c':
    380                 check_valid_edit = 1;
    381                 break;
    382 #ifdef SERVER_SUPPORT
    383 	    case 'n':
    384 		/* Silently ignore -n for compatibility with old
    385 		 * clients.
    386 		 */
    387 		break;
    388 #endif /* SERVER_SUPPORT */
    389 	    case 'm':
    390 #ifdef FORCE_USE_EDITOR
    391 		use_editor = 1;
    392 #else
    393 		use_editor = 0;
    394 #endif
    395 		if (saved_message)
    396 		{
    397 		    free (saved_message);
    398 		    saved_message = NULL;
    399 		}
    400 
    401 		saved_message = xstrdup (optarg);
    402 		break;
    403 	    case 'r':
    404 		if (saved_tag)
    405 		    free (saved_tag);
    406 		saved_tag = xstrdup (optarg);
    407 		break;
    408 	    case 'l':
    409 		local = 1;
    410 		break;
    411 	    case 'R':
    412 		local = 0;
    413 		break;
    414 	    case 'f':
    415 		force_ci = 1;
    416                 check_valid_edit = 0;
    417 		local = 1;		/* also disable recursion */
    418 		break;
    419 	    case 'F':
    420 #ifdef FORCE_USE_EDITOR
    421 		use_editor = 1;
    422 #else
    423 		use_editor = 0;
    424 #endif
    425 		logfile = optarg;
    426 		break;
    427 	    case '?':
    428 	    default:
    429 		usage (commit_usage);
    430 		break;
    431 	}
    432     }
    433     argc -= optind;
    434     argv += optind;
    435 
    436     /* numeric specified revision means we ignore sticky tags... */
    437     if (saved_tag && isdigit ((unsigned char) *saved_tag))
    438     {
    439 	char *p = saved_tag + strlen (saved_tag);
    440 	aflag = 1;
    441 	/* strip trailing dots and leading zeros */
    442 	while (*--p == '.') ;
    443 	p[1] = '\0';
    444 	while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1]))
    445 	    ++saved_tag;
    446     }
    447 
    448     /* some checks related to the "-F logfile" option */
    449     if (logfile)
    450     {
    451 	size_t size = 0, len;
    452 
    453 	if (saved_message)
    454 	    error (1, 0, "cannot specify both a message and a log file");
    455 
    456 	get_file (logfile, logfile, "r", &saved_message, &size, &len);
    457     }
    458 
    459 #ifdef CLIENT_SUPPORT
    460     if (current_parsed_root->isremote)
    461     {
    462 	struct find_data find_args;
    463 
    464 	ign_setup ();
    465 
    466 	find_args.ulist = getlist ();
    467 	find_args.argc = 0;
    468 	find_args.questionables = NULL;
    469 	find_args.ignlist = NULL;
    470 	find_args.repository = NULL;
    471 
    472 	/* It is possible that only a numeric tag should set this.
    473 	   I haven't really thought about it much.
    474 	   Anyway, I suspect that setting it unnecessarily only causes
    475 	   a little unneeded network traffic.  */
    476 	find_args.force = force_ci || saved_tag != NULL;
    477 
    478 	err = start_recursion
    479 	    (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL,
    480 	     &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
    481 	     NULL, 0, NULL );
    482 	if (err)
    483 	    error (1, 0, "correct above errors first!");
    484 
    485 	if (find_args.argc == 0)
    486 	{
    487 	    /* Nothing to commit.  Exit now without contacting the
    488 	       server (note that this means that we won't print "?
    489 	       foo" for files which merit it, because we don't know
    490 	       what is in the CVSROOT/cvsignore file).  */
    491 	    dellist (&find_args.ulist);
    492 	    return 0;
    493 	}
    494 
    495 	/* Now we keep track of which files we actually are going to
    496 	   operate on, and only work with those files in the future.
    497 	   This saves time--we don't want to search the file system
    498 	   of the working directory twice.  */
    499 	if (size_overflow_p (xtimes (find_args.argc, sizeof (char **))))
    500 	{
    501 	    find_args.argc = 0;
    502 	    return 0;
    503 	}
    504 	find_args.argv = xnmalloc (find_args.argc, sizeof (char **));
    505 	find_args.argc = 0;
    506 	walklist (find_args.ulist, copy_ulist, &find_args);
    507 
    508 	/* Do this before calling do_editor; don't ask for a log
    509 	   message if we can't talk to the server.  But do it after we
    510 	   have made the checks that we can locally (to more quickly
    511 	   catch syntax errors, the case where no files are modified,
    512 	   added or removed, etc.).
    513 
    514 	   On the other hand, calling start_server before do_editor
    515 	   means that we chew up server resources the whole time that
    516 	   the user has the editor open (hours or days if the user
    517 	   forgets about it), which seems dubious.  */
    518 	start_server ();
    519 
    520 	/*
    521 	 * We do this once, not once for each directory as in normal CVS.
    522 	 * The protocol is designed this way.  This is a feature.
    523 	 */
    524 	if (use_editor)
    525 	    do_editor (".", &saved_message, NULL, find_args.ulist);
    526 
    527 	/* We always send some sort of message, even if empty.  */
    528 	option_with_arg ("-m", saved_message ? saved_message : "");
    529 
    530 	/* OK, now process all the questionable files we have been saving
    531 	   up.  */
    532 	{
    533 	    struct question *p;
    534 	    struct question *q;
    535 
    536 	    p = find_args.questionables;
    537 	    while (p != NULL)
    538 	    {
    539 		if (ign_inhibit_server || !supported_request ("Questionable"))
    540 		{
    541 		    cvs_output ("? ", 2);
    542 		    if (p->dir[0] != '\0')
    543 		    {
    544 			cvs_output (p->dir, 0);
    545 			cvs_output ("/", 1);
    546 		    }
    547 		    cvs_output (p->file, 0);
    548 		    cvs_output ("\n", 1);
    549 		}
    550 		else
    551 		{
    552 		    /* This used to send the Directory line of its own accord,
    553 		     * but skipped some of the other processing like checking
    554 		     * for whether the server would accept "Relative-directory"
    555 		     * requests.  Relying on send_a_repository() to do this
    556 		     * picks up these checks but also:
    557 		     *
    558 		     *   1. Causes the "Directory" request to be sent only once
    559 		     *      per directory.
    560 		     *   2. Causes the global TOPLEVEL_REPOS to be set.
    561 		     *   3. Causes "Static-directory" and "Sticky" requests
    562 		     *      to sometimes be sent.
    563 		     *
    564 		     * (1) is almost certainly a plus.  (2) & (3) may or may
    565 		     * not be useful sometimes, and will ocassionally cause a
    566 		     * little extra network traffic.  The additional network
    567 		     * traffic is probably already saved several times over and
    568 		     * certainly cancelled out via the multiple "Directory"
    569 		     * request suppression of (1).
    570 		     */
    571 		    send_a_repository (p->dir, p->repos, p->dir);
    572 
    573 		    send_to_server ("Questionable ", 0);
    574 		    send_to_server (p->file, 0);
    575 		    send_to_server ("\012", 1);
    576 		}
    577 		free (p->dir);
    578 		free (p->repos);
    579 		free (p->file);
    580 		q = p->next;
    581 		free (p);
    582 		p = q;
    583 	    }
    584 	}
    585 
    586 	if (local)
    587 	    send_arg ("-l");
    588         if (check_valid_edit)
    589             send_arg ("-c");
    590 	if (force_ci)
    591 	    send_arg ("-f");
    592 	option_with_arg ("-r", saved_tag);
    593 	send_arg ("--");
    594 
    595 	/* FIXME: This whole find_args.force/SEND_FORCE business is a
    596 	   kludge.  It would seem to be a server bug that we have to
    597 	   say that files are modified when they are not.  This makes
    598 	   "cvs commit -r 2" across a whole bunch of files a very slow
    599 	   operation (and it isn't documented in cvsclient.texi).  I
    600 	   haven't looked at the server code carefully enough to be
    601 	   _sure_ why this is needed, but if it is because the "ci"
    602 	   program, which we used to call, wanted the file to exist,
    603 	   then it would be relatively simple to fix in the server.  */
    604 	send_files (find_args.argc, find_args.argv, local, 0,
    605 		    find_args.force ? SEND_FORCE : 0);
    606 
    607 	/* Sending only the names of the files which were modified, added,
    608 	   or removed means that the server will only do an up-to-date
    609 	   check on those files.  This is different from local CVS and
    610 	   previous versions of client/server CVS, but it probably is a Good
    611 	   Thing, or at least Not Such A Bad Thing.  */
    612 	send_file_names (find_args.argc, find_args.argv, 0);
    613 	free (find_args.argv);
    614 	dellist (&find_args.ulist);
    615 
    616 	send_to_server ("ci\012", 0);
    617 	err = get_responses_and_close ();
    618 	if (err != 0 && use_editor && saved_message != NULL)
    619 	{
    620 	    /* If there was an error, don't nuke the user's carefully
    621 	       constructed prose.  This is something of a kludge; a better
    622 	       solution is probably more along the lines of #150 in TODO
    623 	       (doing a second up-to-date check before accepting the
    624 	       log message has also been suggested, but that seems kind of
    625 	       iffy because the real up-to-date check could still fail,
    626 	       another error could occur, &c.  Also, a second check would
    627 	       slow things down).  */
    628 
    629 	    char *fname;
    630 	    FILE *fp;
    631 
    632 	    fp = cvs_temp_file (&fname);
    633 	    if (fp == NULL)
    634 		error (1, 0, "cannot create temporary file %s", fname);
    635 	    if (fwrite (saved_message, 1, strlen (saved_message), fp)
    636 		!= strlen (saved_message))
    637 		error (1, errno, "cannot write temporary file %s", fname);
    638 	    if (fclose (fp) < 0)
    639 		error (0, errno, "cannot close temporary file %s", fname);
    640 	    error (0, 0, "saving log message in %s", fname);
    641 	    free (fname);
    642 	}
    643 	return err;
    644     }
    645 #endif
    646 
    647     if (saved_tag != NULL)
    648 	tag_check_valid (saved_tag, argc, argv, local, aflag, "", false);
    649 
    650     /* XXX - this is not the perfect check for this */
    651     if (argc <= 0)
    652 	write_dirtag = saved_tag;
    653 
    654     wrap_setup ();
    655 
    656     lock_tree_promotably (argc, argv, local, W_LOCAL, aflag);
    657 
    658     /*
    659      * Set up the master update list and hard link list
    660      */
    661     mulist = getlist ();
    662 
    663 #ifdef PRESERVE_PERMISSIONS_SUPPORT
    664     if (preserve_perms)
    665     {
    666 	hardlist = getlist ();
    667 
    668 	/*
    669 	 * We need to save the working directory so that
    670 	 * check_fileproc can construct a full pathname for each file.
    671 	 */
    672 	working_dir = xgetcwd ();
    673     }
    674 #endif
    675 
    676     /*
    677      * Run the recursion processor to verify the files are all up-to-date
    678      */
    679     err = start_recursion (check_fileproc, check_filesdoneproc,
    680                            check_direntproc, NULL, NULL, argc, argv, local,
    681                            W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL);
    682     if (err)
    683 	error (1, 0, "correct above errors first!");
    684 
    685     /*
    686      * Run the recursion processor to commit the files
    687      */
    688     write_dirnonbranch = 0;
    689     if (noexec == 0)
    690 	err = start_recursion (commit_fileproc, commit_filesdoneproc,
    691                                commit_direntproc, commit_dirleaveproc, NULL,
    692                                argc, argv, local, W_LOCAL, aflag,
    693                                CVS_LOCK_WRITE, NULL, 1, NULL);
    694 
    695     /*
    696      * Unlock all the dirs and clean up
    697      */
    698     Lock_Cleanup ();
    699     dellist (&mulist);
    700 
    701     /* see if we need to sleep before returning to avoid time-stamp races */
    702     if (!server_active && last_register_time)
    703     {
    704 	sleep_past (last_register_time);
    705     }
    706 
    707     return err;
    708 }
    709 
    710 
    711 
    712 /* This routine determines the status of a given file and retrieves
    713    the version information that is associated with that file. */
    714 
    715 static
    716 Ctype
    717 classify_file_internal (struct file_info *finfo, Vers_TS **vers)
    718 {
    719     int save_noexec, save_quiet, save_really_quiet;
    720     Ctype status;
    721 
    722     /* FIXME: Do we need to save quiet as well as really_quiet?  Last
    723        time I glanced at Classify_File I only saw it looking at really_quiet
    724        not quiet.  */
    725     save_noexec = noexec;
    726     save_quiet = quiet;
    727     save_really_quiet = really_quiet;
    728     noexec = quiet = really_quiet = 1;
    729 
    730     /* handle specified numeric revision specially */
    731     if (saved_tag && isdigit ((unsigned char) *saved_tag))
    732     {
    733 	/* If the tag is for the trunk, make sure we're at the head */
    734 	if (numdots (saved_tag) < 2)
    735 	{
    736 	    status = Classify_File (finfo, NULL, NULL,
    737 				    NULL, 1, aflag, vers, 0);
    738 	    if (status == T_UPTODATE || status == T_MODIFIED ||
    739 		status == T_ADDED)
    740 	    {
    741 		Ctype xstatus;
    742 
    743 		freevers_ts (vers);
    744 		xstatus = Classify_File (finfo, saved_tag, NULL,
    745 					 NULL, 1, aflag, vers, 0);
    746 		if (xstatus == T_REMOVE_ENTRY)
    747 		    status = T_MODIFIED;
    748 		else if (status == T_MODIFIED && xstatus == T_CONFLICT)
    749 		    status = T_MODIFIED;
    750 		else
    751 		    status = xstatus;
    752 	    }
    753 	}
    754 	else
    755 	{
    756 	    char *xtag, *cp;
    757 
    758 	    /*
    759 	     * The revision is off the main trunk; make sure we're
    760 	     * up-to-date with the head of the specified branch.
    761 	     */
    762 	    xtag = xstrdup (saved_tag);
    763 	    if ((numdots (xtag) & 1) != 0)
    764 	    {
    765 		cp = strrchr (xtag, '.');
    766 		*cp = '\0';
    767 	    }
    768 	    status = Classify_File (finfo, xtag, NULL,
    769 				    NULL, 1, aflag, vers, 0);
    770 	    if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
    771 		&& (cp = strrchr (xtag, '.')) != NULL)
    772 	    {
    773 		/* pluck one more dot off the revision */
    774 		*cp = '\0';
    775 		freevers_ts (vers);
    776 		status = Classify_File (finfo, xtag, NULL,
    777 					NULL, 1, aflag, vers, 0);
    778 		if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
    779 		    status = T_MODIFIED;
    780 	    }
    781 	    /* now, muck with vers to make the tag correct */
    782 	    free ((*vers)->tag);
    783 	    (*vers)->tag = xstrdup (saved_tag);
    784 	    free (xtag);
    785 	}
    786     }
    787     else
    788 	status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0);
    789     noexec = save_noexec;
    790     quiet = save_quiet;
    791     really_quiet = save_really_quiet;
    792 
    793     return status;
    794 }
    795 
    796 
    797 
    798 /*
    799  * Check to see if a file is ok to commit and make sure all files are
    800  * up-to-date
    801  */
    802 /* ARGSUSED */
    803 static int
    804 check_fileproc (void *callerdat, struct file_info *finfo)
    805 {
    806     Ctype status;
    807     const char *xdir;
    808     Node *p;
    809     List *ulist, *cilist;
    810     Vers_TS *vers;
    811     struct commit_info *ci;
    812     struct logfile_info *li;
    813     int retval = 1;
    814 
    815     size_t cvsroot_len = strlen (current_parsed_root->directory);
    816 
    817     if (!finfo->repository)
    818     {
    819 	error (0, 0, "nothing known about `%s'", finfo->fullname);
    820 	return 1;
    821     }
    822 
    823     if (strncmp (finfo->repository, current_parsed_root->directory,
    824                  cvsroot_len) == 0
    825 	&& ISSLASH (finfo->repository[cvsroot_len])
    826 	&& strncmp (finfo->repository + cvsroot_len + 1,
    827 		    CVSROOTADM,
    828 		    sizeof (CVSROOTADM) - 1) == 0
    829 	&& ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
    830 	&& strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
    831 		   CVSNULLREPOS) == 0
    832 	)
    833 	error (1, 0, "cannot check in to %s", finfo->repository);
    834 
    835     status = classify_file_internal (finfo, &vers);
    836 
    837     /*
    838      * If the force-commit option is enabled, and the file in question
    839      * appears to be up-to-date, just make it look modified so that
    840      * it will be committed.
    841      */
    842     if (force_ci && status == T_UPTODATE)
    843 	status = T_MODIFIED;
    844 
    845     switch (status)
    846     {
    847 	case T_CHECKOUT:
    848 	case T_PATCH:
    849 	case T_NEEDS_MERGE:
    850 	case T_REMOVE_ENTRY:
    851 	    error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
    852 	    goto out;
    853 	case T_CONFLICT:
    854 	case T_MODIFIED:
    855 	case T_ADDED:
    856 	case T_REMOVED:
    857         {
    858             char *editor;
    859 
    860 	    /*
    861 	     * some quick sanity checks; if no numeric -r option specified:
    862 	     *	- can't have a sticky date
    863 	     *	- can't have a sticky tag that is not a branch
    864 	     * Also,
    865 	     *	- if status is T_REMOVED, file must not exist and its entry
    866 	     *	  can't have a numeric sticky tag.
    867 	     *	- if status is T_ADDED, rcs file must not exist unless on
    868 	     *    a branch or head is dead
    869 	     *	- if status is T_ADDED, can't have a non-trunk numeric rev
    870 	     *	- if status is T_MODIFIED and a Conflict marker exists, don't
    871 	     *    allow the commit if timestamp is identical or if we find
    872 	     *    an RCS_MERGE_PAT in the file.
    873 	     */
    874 	    if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
    875 	    {
    876 		if (vers->date)
    877 		{
    878 		    error (0, 0,
    879 			   "cannot commit with sticky date for file `%s'",
    880 			   finfo->fullname);
    881 		    goto out;
    882 		}
    883 		if (status == T_MODIFIED && vers->tag &&
    884 		    !RCS_isbranch (finfo->rcs, vers->tag))
    885 		{
    886 		    error (0, 0,
    887 			   "sticky tag `%s' for file `%s' is not a branch",
    888 			   vers->tag, finfo->fullname);
    889 		    goto out;
    890 		}
    891 	    }
    892 	    if (status == T_CONFLICT && !force_ci)
    893 	    {
    894 		error (0, 0,
    895 		      "file `%s' had a conflict and has not been modified",
    896 		       finfo->fullname);
    897 		goto out;
    898 	    }
    899 	    if (status == T_MODIFIED && !force_ci && file_has_markers (finfo))
    900 	    {
    901 		/* Make this a warning, not an error, because we have
    902 		   no way of knowing whether the "conflict indicators"
    903 		   are really from a conflict or whether they are part
    904 		   of the document itself (cvs.texinfo and sanity.sh in
    905 		   CVS itself, for example, tend to want to have strings
    906 		   like ">>>>>>>" at the start of a line).  Making people
    907 		   kludge this the way they need to kludge keyword
    908 		   expansion seems undesirable.  And it is worse than
    909 		   keyword expansion, because there is no -ko
    910 		   analogue.  */
    911 		error (0, 0,
    912 		       "\
    913 warning: file `%s' seems to still contain conflict indicators",
    914 		       finfo->fullname);
    915 	    }
    916 
    917 	    if (status == T_REMOVED)
    918 	    {
    919 		if (vers->ts_user != NULL)
    920 		{
    921 		    error (0, 0,
    922 			   "`%s' should be removed and is still there (or is"
    923 			   " back again)", finfo->fullname);
    924 		    goto out;
    925 		}
    926 
    927 		if (vers->tag && isdigit ((unsigned char) *vers->tag))
    928 		{
    929 		    /* Remove also tries to forbid this, but we should check
    930 		       here.  I'm only _sure_ about somewhat obscure cases
    931 		       (hacking the Entries file, using an old version of
    932 		       CVS for the remove and a new one for the commit), but
    933 		       there might be other cases.  */
    934 		    error (0, 0,
    935 			   "cannot remove file `%s' which has a numeric sticky"
    936 			   " tag of `%s'", finfo->fullname, vers->tag);
    937 		    freevers_ts (&vers);
    938 		    goto out;
    939 		}
    940 	    }
    941 	    if (status == T_ADDED)
    942 	    {
    943 	        if (vers->tag == NULL)
    944 		{
    945 		    if (finfo->rcs != NULL &&
    946 			!RCS_isdead (finfo->rcs, finfo->rcs->head))
    947 		    {
    948 			error (0, 0,
    949 		    "cannot add file `%s' when RCS file `%s' already exists",
    950 			       finfo->fullname, finfo->rcs->path);
    951 			goto out;
    952 		    }
    953 		}
    954 		else if (isdigit ((unsigned char) *vers->tag) &&
    955 		    numdots (vers->tag) > 1)
    956 		{
    957 		    error (0, 0,
    958 		"cannot add file `%s' with revision `%s'; must be on trunk",
    959 			       finfo->fullname, vers->tag);
    960 		    goto out;
    961 		}
    962 	    }
    963 
    964 	    /* done with consistency checks; now, to get on with the commit */
    965 	    if (finfo->update_dir[0] == '\0')
    966 		xdir = ".";
    967 	    else
    968 		xdir = finfo->update_dir;
    969 	    if ((p = findnode (mulist, xdir)) != NULL)
    970 	    {
    971 		ulist = ((struct master_lists *) p->data)->ulist;
    972 		cilist = ((struct master_lists *) p->data)->cilist;
    973 	    }
    974 	    else
    975 	    {
    976 		struct master_lists *ml;
    977 
    978 		ml = xmalloc (sizeof (struct master_lists));
    979 		ulist = ml->ulist = getlist ();
    980 		cilist = ml->cilist = getlist ();
    981 
    982 		p = getnode ();
    983 		p->key = xstrdup (xdir);
    984 		p->type = UPDATE;
    985 		p->data = ml;
    986 		p->delproc = masterlist_delproc;
    987 		(void) addnode (mulist, p);
    988 	    }
    989 
    990 	    /* first do ulist, then cilist */
    991 	    p = getnode ();
    992 	    p->key = xstrdup (finfo->file);
    993 	    p->type = UPDATE;
    994 	    p->delproc = update_delproc;
    995 	    li = xmalloc (sizeof (struct logfile_info));
    996 	    li->type = status;
    997 
    998 	    if (check_valid_edit)
    999             {
   1000                 char *editors = NULL;
   1001 
   1002 		editor = NULL;
   1003                 editors = fileattr_get0 (finfo->file, "_editors");
   1004                 if (editors != NULL)
   1005                 {
   1006                     char *caller = getcaller ();
   1007                     char *p = NULL;
   1008                     char *p0 = NULL;
   1009 
   1010                     p = editors;
   1011                     p0 = p;
   1012                     while (*p != '\0')
   1013                     {
   1014                         p = strchr (p, '>');
   1015                         if (p == NULL)
   1016                         {
   1017                             break;
   1018                         }
   1019                         *p = '\0';
   1020                         if (strcmp (caller, p0) == 0)
   1021                         {
   1022                             break;
   1023                         }
   1024                         p = strchr (p + 1, ',');
   1025                         if (p == NULL)
   1026                         {
   1027                             break;
   1028                         }
   1029                         ++p;
   1030                         p0 = p;
   1031                     }
   1032 
   1033                     if (strcmp (caller, p0) == 0)
   1034                     {
   1035                         editor = caller;
   1036                     }
   1037 
   1038                     free (editors);
   1039                 }
   1040             }
   1041 
   1042             if (check_valid_edit && editor == NULL)
   1043             {
   1044                 error (0, 0, "Valid edit does not exist for %s",
   1045                        finfo->fullname);
   1046                 freevers_ts (&vers);
   1047                 return 1;
   1048             }
   1049 
   1050 	    li->tag = xstrdup (vers->tag);
   1051 	    /* If the file was re-added, we want the revision in the commitlog
   1052 	       to be NONE, not the previous dead revision. */
   1053 	    li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs);
   1054 	    li->rev_new = NULL;
   1055 	    p->data = li;
   1056 	    (void) addnode (ulist, p);
   1057 
   1058 	    p = getnode ();
   1059 	    p->key = xstrdup (finfo->file);
   1060 	    p->type = UPDATE;
   1061 	    p->delproc = ci_delproc;
   1062 	    ci = xmalloc (sizeof (struct commit_info));
   1063 	    ci->status = status;
   1064 	    if (vers->tag)
   1065 		if (isdigit ((unsigned char) *vers->tag))
   1066 		    ci->rev = xstrdup (vers->tag);
   1067 		else
   1068 		    ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
   1069 	    else
   1070 		ci->rev = NULL;
   1071 	    ci->tag = xstrdup (vers->tag);
   1072 	    ci->options = xstrdup (vers->options);
   1073 	    p->data = ci;
   1074 	    (void) addnode (cilist, p);
   1075 
   1076 #ifdef PRESERVE_PERMISSIONS_SUPPORT
   1077 	    if (preserve_perms)
   1078 	    {
   1079 		/* Add this file to hardlist, indexed on its inode.  When
   1080 		   we are done, we can find out what files are hardlinked
   1081 		   to a given file by looking up its inode in hardlist. */
   1082 		char *fullpath;
   1083 		Node *linkp;
   1084 		struct hardlink_info *hlinfo;
   1085 
   1086 		/* Get the full pathname of the current file. */
   1087 		fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
   1088 
   1089 		/* To permit following links in subdirectories, files
   1090                    are keyed on finfo->fullname, not on finfo->name. */
   1091 		linkp = lookup_file_by_inode (fullpath);
   1092 
   1093 		/* If linkp is NULL, the file doesn't exist... maybe
   1094 		   we're doing a remove operation? */
   1095 		if (linkp != NULL)
   1096 		{
   1097 		    /* Create a new hardlink_info node, which will record
   1098 		       the current file's status and the links listed in its
   1099 		       `hardlinks' delta field.  We will append this
   1100 		       hardlink_info node to the appropriate hardlist entry. */
   1101 		    hlinfo = xmalloc (sizeof (struct hardlink_info));
   1102 		    hlinfo->status = status;
   1103 		    linkp->data = hlinfo;
   1104 		}
   1105 	    }
   1106 #endif
   1107 
   1108 	    break;
   1109         }
   1110 
   1111 	case T_UNKNOWN:
   1112 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
   1113 	    goto out;
   1114 	case T_UPTODATE:
   1115 	    break;
   1116 	default:
   1117 	    error (0, 0, "CVS internal error: unknown status %d", status);
   1118 	    break;
   1119     }
   1120 
   1121     retval = 0;
   1122 
   1123  out:
   1124 
   1125     freevers_ts (&vers);
   1126     return retval;
   1127 }
   1128 
   1129 
   1130 
   1131 /*
   1132  * By default, return the code that tells do_recursion to examine all
   1133  * directories
   1134  */
   1135 /* ARGSUSED */
   1136 static Dtype
   1137 check_direntproc (void *callerdat, const char *dir, const char *repos,
   1138                   const char *update_dir, List *entries)
   1139 {
   1140     if (!isdir (dir))
   1141 	return R_SKIP_ALL;
   1142 
   1143     if (!quiet)
   1144 	error (0, 0, "Examining %s", update_dir);
   1145 
   1146     return R_PROCESS;
   1147 }
   1148 
   1149 
   1150 
   1151 /*
   1152  * Walklist proc to generate an arg list from the line in commitinfo
   1153  */
   1154 static int
   1155 precommit_list_to_args_proc (p, closure)
   1156     Node *p;
   1157     void *closure;
   1158 {
   1159     struct format_cmdline_walklist_closure *c = closure;
   1160     struct logfile_info *li;
   1161     char *arg = NULL;
   1162     const char *f;
   1163     char *d;
   1164     size_t doff;
   1165 
   1166     if (p->data == NULL) return 1;
   1167 
   1168     f = c->format;
   1169     d = *c->d;
   1170     /* foreach requested attribute */
   1171     while (*f)
   1172     {
   1173    	switch (*f++)
   1174 	{
   1175 	    case 's':
   1176 		li = p->data;
   1177 		if (li->type == T_ADDED
   1178 			|| li->type == T_MODIFIED
   1179 			|| li->type == T_REMOVED)
   1180 		{
   1181 		    arg = p->key;
   1182 		}
   1183 		break;
   1184 	    default:
   1185 		error (1, 0,
   1186 		       "Unknown format character or not a list attribute: %c",
   1187 		       f[-1]);
   1188 		/* NOTREACHED */
   1189 		break;
   1190 	}
   1191 	/* copy the attribute into an argument */
   1192 	if (c->quotes)
   1193 	{
   1194 	    arg = cmdlineescape (c->quotes, arg);
   1195 	}
   1196 	else
   1197 	{
   1198 	    arg = cmdlinequote ('"', arg);
   1199 	}
   1200 	doff = d - *c->buf;
   1201 	expand_string (c->buf, c->length, doff + strlen (arg));
   1202 	d = *c->buf + doff;
   1203 	strncpy (d, arg, strlen (arg));
   1204 	d += strlen (arg);
   1205 	free (arg);
   1206 
   1207 	/* and always put the extra space on.  we'll have to back up a char
   1208 	 * when we're done, but that seems most efficient
   1209 	 */
   1210 	doff = d - *c->buf;
   1211 	expand_string (c->buf, c->length, doff + 1);
   1212 	d = *c->buf + doff;
   1213 	*d++ = ' ';
   1214     }
   1215     /* correct our original pointer into the buff */
   1216     *c->d = d;
   1217     return 0;
   1218 }
   1219 
   1220 
   1221 
   1222 /*
   1223  * Callback proc for pre-commit checking
   1224  */
   1225 static int
   1226 precommit_proc (const char *repository, const char *filter, void *closure)
   1227 {
   1228     char *newfilter = NULL;
   1229     char *cmdline;
   1230     const char *srepos = Short_Repository (repository);
   1231     List *ulist = closure;
   1232 
   1233 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
   1234     if (!strchr (filter, '%'))
   1235     {
   1236 	error (0, 0,
   1237                "warning: commitinfo line contains no format strings:\n"
   1238                "    \"%s\"\n"
   1239                "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n"
   1240                "deprecated.", filter);
   1241 	newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
   1242 	filter = newfilter;
   1243     }
   1244 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
   1245 
   1246     /*
   1247      * Cast any NULL arguments as appropriate pointers as this is an
   1248      * stdarg function and we need to be certain the caller gets what
   1249      * is expected.
   1250      */
   1251     cmdline = format_cmdline (
   1252 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
   1253 			      false, srepos,
   1254 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
   1255 			      filter,
   1256 			      "c", "s", cvs_cmd_name,
   1257 #ifdef SERVER_SUPPORT
   1258 			      "R", "s", referrer ? referrer->original : "NONE",
   1259 #endif /* SERVER_SUPPORT */
   1260 			      "p", "s", srepos,
   1261 			      "r", "s", current_parsed_root->directory,
   1262 			      "s", ",", ulist, precommit_list_to_args_proc,
   1263 			      (void *) NULL,
   1264 			      (char *) NULL);
   1265 
   1266     if (newfilter) free (newfilter);
   1267 
   1268     if (!cmdline || !strlen (cmdline))
   1269     {
   1270 	if (cmdline) free (cmdline);
   1271 	error (0, 0, "precommit proc resolved to the empty string!");
   1272 	return 1;
   1273     }
   1274 
   1275     run_setup (cmdline);
   1276     free (cmdline);
   1277 
   1278     return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY|
   1279 	(server_active ? 0 : RUN_UNSETXID));
   1280 }
   1281 
   1282 
   1283 
   1284 /*
   1285  * Run the pre-commit checks for the dir
   1286  */
   1287 /* ARGSUSED */
   1288 static int
   1289 check_filesdoneproc (void *callerdat, int err, const char *repos,
   1290                      const char *update_dir, List *entries)
   1291 {
   1292     int n;
   1293     Node *p;
   1294     List *saved_ulist;
   1295 
   1296     /* find the update list for this dir */
   1297     p = findnode (mulist, update_dir);
   1298     if (p != NULL)
   1299 	saved_ulist = ((struct master_lists *) p->data)->ulist;
   1300     else
   1301 	saved_ulist = NULL;
   1302 
   1303     /* skip the checks if there's nothing to do */
   1304     if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
   1305 	return err;
   1306 
   1307     /* run any pre-commit checks */
   1308     n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
   1309                     saved_ulist);
   1310     if (n > 0)
   1311     {
   1312 	error (0, 0, "Pre-commit check failed");
   1313 	err += n;
   1314     }
   1315 
   1316     return err;
   1317 }
   1318 
   1319 
   1320 
   1321 /*
   1322  * Do the work of committing a file
   1323  */
   1324 static int maxrev;
   1325 static char *sbranch;
   1326 
   1327 /* ARGSUSED */
   1328 static int
   1329 commit_fileproc (void *callerdat, struct file_info *finfo)
   1330 {
   1331     Node *p;
   1332     int err = 0;
   1333     List *ulist, *cilist;
   1334     struct commit_info *ci;
   1335 
   1336     /* Keep track of whether write_dirtag is a branch tag.
   1337        Note that if it is a branch tag in some files and a nonbranch tag
   1338        in others, treat it as a nonbranch tag.  It is possible that case
   1339        should elicit a warning or an error.  */
   1340     if (write_dirtag != NULL
   1341 	&& finfo->rcs != NULL)
   1342     {
   1343 	char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
   1344 	if (rev != NULL
   1345 	    && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
   1346 	    write_dirnonbranch = 1;
   1347 	if (rev != NULL)
   1348 	    free (rev);
   1349     }
   1350 
   1351     if (finfo->update_dir[0] == '\0')
   1352 	p = findnode (mulist, ".");
   1353     else
   1354 	p = findnode (mulist, finfo->update_dir);
   1355 
   1356     /*
   1357      * if p is null, there were file type command line args which were
   1358      * all up-to-date so nothing really needs to be done
   1359      */
   1360     if (p == NULL)
   1361 	return 0;
   1362     ulist = ((struct master_lists *) p->data)->ulist;
   1363     cilist = ((struct master_lists *) p->data)->cilist;
   1364 
   1365     /*
   1366      * At this point, we should have the commit message unless we were called
   1367      * with files as args from the command line.  In that latter case, we
   1368      * need to get the commit message ourselves
   1369      */
   1370     if (!got_message)
   1371     {
   1372 	got_message = 1;
   1373 	if (!server_active && use_editor)
   1374 	    do_editor (finfo->update_dir, &saved_message,
   1375 		       finfo->repository, ulist);
   1376 	do_verify (&saved_message, finfo->repository, ulist);
   1377     }
   1378 
   1379     p = findnode (cilist, finfo->file);
   1380     if (p == NULL)
   1381 	return 0;
   1382 
   1383     ci = p->data;
   1384 
   1385 /* cvsacl patch */
   1386 #ifdef SERVER_SUPPORT
   1387     if (use_cvs_acl /* && server_active */)
   1388     {
   1389 	int whichperm;
   1390 	if (ci->status == T_MODIFIED)
   1391 	    whichperm = 3;
   1392 	else if (ci->status == T_ADDED)
   1393 	    whichperm = 6;
   1394 	else if (ci->status == T_REMOVED)
   1395 	    whichperm = 7;
   1396 
   1397 	if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm,
   1398 			     NULL, NULL, 1))
   1399 	{
   1400 	    if (stop_at_first_permission_denied)
   1401 		error (1, 0, "permission denied for %s",
   1402 		       Short_Repository (finfo->repository));
   1403 	    else
   1404 		error (0, 0, "permission denied for %s/%s",
   1405 		       Short_Repository (finfo->repository), finfo->file);
   1406 
   1407 		return (0);
   1408 	}
   1409     }
   1410 #endif
   1411 
   1412     if (ci->status == T_MODIFIED)
   1413     {
   1414 	if (finfo->rcs == NULL)
   1415 	    error (1, 0, "internal error: no parsed RCS file");
   1416 	if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
   1417 		      finfo->repository) != 0)
   1418 	{
   1419 	    unlockrcs (finfo->rcs);
   1420 	    err = 1;
   1421 	    goto out;
   1422 	}
   1423     }
   1424     else if (ci->status == T_ADDED)
   1425     {
   1426 	if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
   1427 			  &finfo->rcs) != 0)
   1428 	{
   1429 	    if (finfo->rcs != NULL)
   1430 		fixaddfile (finfo->rcs->path);
   1431 	    err = 1;
   1432 	    goto out;
   1433 	}
   1434 
   1435 	/* adding files with a tag, now means adding them on a branch.
   1436 	   Since the branch test was done in check_fileproc for
   1437 	   modified files, we need to stub it in again here. */
   1438 
   1439 	if (ci->tag
   1440 
   1441 	    /* If numeric, it is on the trunk; check_fileproc enforced
   1442 	       this.  */
   1443 	    && !isdigit ((unsigned char) ci->tag[0]))
   1444 	{
   1445 	    if (finfo->rcs == NULL)
   1446 		error (1, 0, "internal error: no parsed RCS file");
   1447 	    if (ci->rev)
   1448 		free (ci->rev);
   1449 	    ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
   1450 	    err = Checkin ('A', finfo, ci->rev,
   1451 			   ci->tag, ci->options, saved_message);
   1452 	    if (err != 0)
   1453 	    {
   1454 		unlockrcs (finfo->rcs);
   1455 		fixbranch (finfo->rcs, sbranch);
   1456 	    }
   1457 
   1458 	    (void) time (&last_register_time);
   1459 
   1460 	    ci->status = T_UPTODATE;
   1461 	}
   1462     }
   1463 
   1464     /*
   1465      * Add the file for real
   1466      */
   1467     if (ci->status == T_ADDED)
   1468     {
   1469 	char *xrev = NULL;
   1470 
   1471 	if (ci->rev == NULL)
   1472 	{
   1473 	    /* find the max major rev number in this directory */
   1474 	    maxrev = 0;
   1475 	    (void) walklist (finfo->entries, findmaxrev, NULL);
   1476 	    if (finfo->rcs->head)
   1477 	    {
   1478 		/* resurrecting: include dead revision */
   1479 		int thisrev = atoi (finfo->rcs->head);
   1480 		if (thisrev > maxrev)
   1481 		    maxrev = thisrev;
   1482 	    }
   1483 	    if (maxrev == 0)
   1484 		maxrev = 1;
   1485 	    xrev = Xasprintf ("%d", maxrev);
   1486 	}
   1487 
   1488 	/* XXX - an added file with symbolic -r should add tag as well */
   1489 	err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
   1490 	if (xrev)
   1491 	    free (xrev);
   1492     }
   1493     else if (ci->status == T_MODIFIED)
   1494     {
   1495 	err = Checkin ('M', finfo, ci->rev, ci->tag,
   1496 		       ci->options, saved_message);
   1497 
   1498 	(void) time (&last_register_time);
   1499 
   1500 	if (err != 0)
   1501 	{
   1502 	    unlockrcs (finfo->rcs);
   1503 	    fixbranch (finfo->rcs, sbranch);
   1504 	}
   1505     }
   1506     else if (ci->status == T_REMOVED)
   1507     {
   1508 	err = remove_file (finfo, ci->tag, saved_message);
   1509 #ifdef SERVER_SUPPORT
   1510 	if (server_active)
   1511 	{
   1512 	    server_scratch_entry_only ();
   1513 	    server_updated (finfo,
   1514 			    NULL,
   1515 
   1516 			    /* Doesn't matter, it won't get checked.  */
   1517 			    SERVER_UPDATED,
   1518 
   1519 			    (mode_t) -1,
   1520 			    NULL,
   1521 			    NULL);
   1522 	}
   1523 #endif
   1524     }
   1525 
   1526     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
   1527        about T_ADDED or T_REMOVED.  */
   1528     notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
   1529 	       finfo->repository);
   1530 
   1531 out:
   1532     if (err != 0)
   1533     {
   1534 	/* on failure, remove the file from ulist */
   1535 	p = findnode (ulist, finfo->file);
   1536 	if (p)
   1537 	    delnode (p);
   1538     }
   1539     else
   1540     {
   1541 	/* On success, retrieve the new version number of the file and
   1542            copy it into the log information (see logmsg.c
   1543            (logfile_write) for more details).  We should only update
   1544            the version number for files that have been added or
   1545            modified but not removed since classify_file_internal
   1546            will return the version number of a file even after it has
   1547            been removed from the archive, which is not the behavior we
   1548            want for our commitlog messages; we want the old version
   1549            number and then "NONE." */
   1550 
   1551 	if (ci->status != T_REMOVED)
   1552 	{
   1553 	    p = findnode (ulist, finfo->file);
   1554 	    if (p)
   1555 	    {
   1556 		Vers_TS *vers;
   1557 		struct logfile_info *li;
   1558 
   1559 		(void) classify_file_internal (finfo, &vers);
   1560 		li = p->data;
   1561 		li->rev_new = xstrdup (vers->vn_rcs);
   1562 		freevers_ts (&vers);
   1563 	    }
   1564 	}
   1565     }
   1566     if (SIG_inCrSect ())
   1567 	SIG_endCrSect ();
   1568 
   1569     return err;
   1570 }
   1571 
   1572 
   1573 
   1574 /*
   1575  * Log the commit and clean up the update list
   1576  */
   1577 /* ARGSUSED */
   1578 static int
   1579 commit_filesdoneproc (void *callerdat, int err, const char *repository,
   1580                       const char *update_dir, List *entries)
   1581 {
   1582     Node *p;
   1583     List *ulist;
   1584 
   1585     assert (repository);
   1586 
   1587     p = findnode (mulist, update_dir);
   1588     if (p == NULL)
   1589 	return err;
   1590 
   1591     ulist = ((struct master_lists *) p->data)->ulist;
   1592 
   1593     got_message = 0;
   1594 
   1595     /* Build the administrative files if necessary.  */
   1596     {
   1597 	const char *p;
   1598 
   1599 	if (strncmp (current_parsed_root->directory, repository,
   1600 		     strlen (current_parsed_root->directory)) != 0)
   1601 	    error (0, 0,
   1602 		 "internal error: repository (%s) doesn't begin with root (%s)",
   1603 		   repository, current_parsed_root->directory);
   1604 	p = repository + strlen (current_parsed_root->directory);
   1605 	if (*p == '/')
   1606 	    ++p;
   1607 	if (strcmp ("CVSROOT", p) == 0
   1608 	    /* Check for subdirectories because people may want to create
   1609 	       subdirectories and list files therein in checkoutlist.  */
   1610 	    || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
   1611 	    )
   1612 	{
   1613 	    /* "Database" might a little bit grandiose and/or vague,
   1614 	       but "checked-out copies of administrative files, unless
   1615 	       in the case of modules and you are using ndbm in which
   1616 	       case modules.{pag,dir,db}" is verbose and excessively
   1617 	       focused on how the database is implemented.  */
   1618 
   1619 	    /* mkmodules requires the absolute name of the CVSROOT directory.
   1620 	       Remove anything after the `CVSROOT' component -- this is
   1621 	       necessary when committing in a subdirectory of CVSROOT.  */
   1622 	    char *admin_dir = xstrdup (repository);
   1623 	    int cvsrootlen = strlen ("CVSROOT");
   1624 	    assert (admin_dir[p - repository + cvsrootlen] == '\0'
   1625 		    || admin_dir[p - repository + cvsrootlen] == '/');
   1626 	    admin_dir[p - repository + cvsrootlen] = '\0';
   1627 
   1628 	    if (!really_quiet)
   1629 	    {
   1630 		cvs_output (program_name, 0);
   1631 		cvs_output (" ", 1);
   1632 		cvs_output (cvs_cmd_name, 0);
   1633 		cvs_output (": Rebuilding administrative file database\n", 0);
   1634 	    }
   1635 	    mkmodules (admin_dir);
   1636 	    free (admin_dir);
   1637 	    WriteTemplate (".", 1, repository);
   1638 	}
   1639     }
   1640 
   1641     /* FIXME: This used to be above the block above.  The advantage of being
   1642      * here is that it is not called until after all possible writes from this
   1643      * process are complete.  The disadvantage is that a fatal error during
   1644      * update of CVSROOT can prevent the loginfo script from being called.
   1645      *
   1646      * A more general solution I have been considering is calling a generic
   1647      * "postwrite" hook from the remove write lock routine.
   1648      */
   1649     Update_Logfile (repository, saved_message, NULL, ulist);
   1650 
   1651     return err;
   1652 }
   1653 
   1654 
   1655 
   1656 /*
   1657  * Get the log message for a dir
   1658  */
   1659 /* ARGSUSED */
   1660 static Dtype
   1661 commit_direntproc (void *callerdat, const char *dir, const char *repos,
   1662                    const char *update_dir, List *entries)
   1663 {
   1664     Node *p;
   1665     List *ulist;
   1666     char *real_repos;
   1667 
   1668     if (!isdir (dir))
   1669 	return R_SKIP_ALL;
   1670 
   1671     /* find the update list for this dir */
   1672     p = findnode (mulist, update_dir);
   1673     if (p != NULL)
   1674 	ulist = ((struct master_lists *) p->data)->ulist;
   1675     else
   1676 	ulist = NULL;
   1677 
   1678     /* skip the files as an optimization */
   1679     if (ulist == NULL || ulist->list->next == ulist->list)
   1680 	return R_SKIP_FILES;
   1681 
   1682     /* get commit message */
   1683     got_message = 1;
   1684     real_repos = Name_Repository (dir, update_dir);
   1685     if (!server_active && use_editor)
   1686 	do_editor (update_dir, &saved_message, real_repos, ulist);
   1687     do_verify (&saved_message, real_repos, ulist);
   1688     free (real_repos);
   1689     return R_PROCESS;
   1690 }
   1691 
   1692 
   1693 
   1694 /*
   1695  * Process the post-commit proc if necessary
   1696  */
   1697 /* ARGSUSED */
   1698 static int
   1699 commit_dirleaveproc (void *callerdat, const char *dir, int err,
   1700                      const char *update_dir, List *entries)
   1701 {
   1702     /* update the per-directory tag info */
   1703     /* FIXME?  Why?  The "commit examples" node of cvs.texinfo briefly
   1704        mentions commit -r being sticky, but apparently in the context of
   1705        this being a confusing feature!  */
   1706     if (err == 0 && write_dirtag != NULL)
   1707     {
   1708 	char *repos = Name_Repository (NULL, update_dir);
   1709 	WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
   1710 		  update_dir, repos);
   1711 	free (repos);
   1712     }
   1713 
   1714     return err;
   1715 }
   1716 
   1717 
   1718 
   1719 /*
   1720  * find the maximum major rev number in an entries file
   1721  */
   1722 static int
   1723 findmaxrev (Node *p, void *closure)
   1724 {
   1725     int thisrev;
   1726     Entnode *entdata = p->data;
   1727 
   1728     if (entdata->type != ENT_FILE)
   1729 	return 0;
   1730     thisrev = atoi (entdata->version);
   1731     if (thisrev > maxrev)
   1732 	maxrev = thisrev;
   1733     return 0;
   1734 }
   1735 
   1736 /*
   1737  * Actually remove a file by moving it to the attic
   1738  * XXX - if removing a ,v file that is a relative symbolic link to
   1739  * another ,v file, we probably should add a ".." component to the
   1740  * link to keep it relative after we move it into the attic.
   1741 
   1742    Return value is 0 on success, or >0 on error (in which case we have
   1743    printed an error message).  */
   1744 static int
   1745 remove_file (struct file_info *finfo, char *tag, char *message)
   1746 {
   1747     int retcode;
   1748 
   1749     int branch;
   1750     int lockflag;
   1751     char *corev;
   1752     char *rev;
   1753     char *prev_rev;
   1754     char *old_path;
   1755 
   1756     corev = NULL;
   1757     rev = NULL;
   1758     prev_rev = NULL;
   1759 
   1760     retcode = 0;
   1761 
   1762     if (finfo->rcs == NULL)
   1763 	error (1, 0, "internal error: no parsed RCS file");
   1764 
   1765     branch = 0;
   1766     if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
   1767     {
   1768 	/* a symbolic tag is specified; just remove the tag from the file */
   1769 	if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
   1770 	{
   1771 	    if (!quiet)
   1772 		error (0, retcode == -1 ? errno : 0,
   1773 		       "failed to remove tag `%s' from `%s'", tag,
   1774 		       finfo->fullname);
   1775 	    return 1;
   1776 	}
   1777 	RCS_rewrite (finfo->rcs, NULL, NULL);
   1778 	Scratch_Entry (finfo->entries, finfo->file);
   1779 	return 0;
   1780     }
   1781 
   1782     /* we are removing the file from either the head or a branch */
   1783     /* commit a new, dead revision. */
   1784 
   1785     rev = NULL;
   1786     lockflag = 1;
   1787     if (branch)
   1788     {
   1789 	char *branchname;
   1790 
   1791 	rev = RCS_whatbranch (finfo->rcs, tag);
   1792 	if (rev == NULL)
   1793 	{
   1794 	    error (0, 0, "cannot find branch \"%s\".", tag);
   1795 	    return 1;
   1796 	}
   1797 
   1798 	branchname = RCS_getbranch (finfo->rcs, rev, 1);
   1799 	if (branchname == NULL)
   1800 	{
   1801 	    /* no revision exists on this branch.  use the previous
   1802 	       revision but do not lock. */
   1803 	    corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
   1804 	    prev_rev = xstrdup (corev);
   1805 	    lockflag = 0;
   1806 	} else
   1807 	{
   1808 	    corev = xstrdup (rev);
   1809 	    prev_rev = xstrdup (branchname);
   1810 	    free (branchname);
   1811 	}
   1812 
   1813     } else  /* Not a branch */
   1814     {
   1815         /* Get current head revision of file. */
   1816 	prev_rev = RCS_head (finfo->rcs);
   1817     }
   1818 
   1819     /* if removing without a tag or a branch, then make sure the default
   1820        branch is the trunk. */
   1821     if (!tag && !branch)
   1822     {
   1823         if (RCS_setbranch (finfo->rcs, NULL) != 0)
   1824 	{
   1825 	    error (0, 0, "cannot change branch to default for %s",
   1826 		   finfo->fullname);
   1827 	    return 1;
   1828 	}
   1829 	RCS_rewrite (finfo->rcs, NULL, NULL);
   1830     }
   1831 
   1832     /* check something out.  Generally this is the head.  If we have a
   1833        particular rev, then name it.  */
   1834     retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
   1835 			    NULL, NULL, RUN_TTY, NULL, NULL);
   1836     if (retcode != 0)
   1837     {
   1838 	error (0, 0,
   1839 	       "failed to check out `%s'", finfo->fullname);
   1840 	return 1;
   1841     }
   1842 
   1843     /* Except when we are creating a branch, lock the revision so that
   1844        we can check in the new revision.  */
   1845     if (lockflag)
   1846     {
   1847 	if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
   1848 	    RCS_rewrite (finfo->rcs, NULL, NULL);
   1849     }
   1850 
   1851     if (corev != NULL)
   1852 	free (corev);
   1853 
   1854     retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
   1855 			   rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
   1856     if (retcode	!= 0)
   1857     {
   1858 	if (!quiet)
   1859 	    error (0, retcode == -1 ? errno : 0,
   1860 		   "failed to commit dead revision for `%s'", finfo->fullname);
   1861 	return 1;
   1862     }
   1863     /* At this point, the file has been committed as removed.  We should
   1864        probably tell the history file about it  */
   1865     history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository);
   1866 
   1867     if (rev != NULL)
   1868 	free (rev);
   1869 
   1870     old_path = xstrdup (finfo->rcs->path);
   1871     if (!branch)
   1872 	RCS_setattic (finfo->rcs, 1);
   1873 
   1874     /* Print message that file was removed. */
   1875     if (!really_quiet)
   1876     {
   1877 	cvs_output (old_path, 0);
   1878 	cvs_output ("  <--  ", 0);
   1879 	if (finfo->update_dir && strlen (finfo->update_dir))
   1880 	{
   1881 	    cvs_output (finfo->update_dir, 0);
   1882 	    cvs_output ("/", 1);
   1883 	}
   1884 	cvs_output (finfo->file, 0);
   1885 	cvs_output ("\nnew revision: delete; previous revision: ", 0);
   1886 	cvs_output (prev_rev, 0);
   1887 	cvs_output ("\n", 0);
   1888     }
   1889 
   1890     free (prev_rev);
   1891 
   1892     free (old_path);
   1893 
   1894     Scratch_Entry (finfo->entries, finfo->file);
   1895     return 0;
   1896 }
   1897 
   1898 
   1899 
   1900 /*
   1901  * Do the actual checkin for added files
   1902  */
   1903 static int
   1904 finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
   1905 {
   1906     int ret;
   1907 
   1908     ret = Checkin ('A', finfo, rev, tag, options, saved_message);
   1909     if (ret == 0)
   1910     {
   1911 	char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
   1912 	if (unlink_file (tmp) < 0
   1913 	    && !existence_error (errno))
   1914 	    error (0, errno, "cannot remove %s", tmp);
   1915 	free (tmp);
   1916     }
   1917     else if (finfo->rcs != NULL)
   1918 	fixaddfile (finfo->rcs->path);
   1919 
   1920     (void) time (&last_register_time);
   1921 
   1922     return ret;
   1923 }
   1924 
   1925 
   1926 
   1927 /*
   1928  * Unlock an rcs file
   1929  */
   1930 static void
   1931 unlockrcs (RCSNode *rcs)
   1932 {
   1933     int retcode;
   1934 
   1935     if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
   1936 	error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
   1937 	       "could not unlock %s", rcs->path);
   1938     else
   1939 	RCS_rewrite (rcs, NULL, NULL);
   1940 }
   1941 
   1942 
   1943 
   1944 /*
   1945  * remove a partially added file.  if we can parse it, leave it alone.
   1946  *
   1947  * FIXME: Every caller that calls this function can access finfo->rcs (the
   1948  * parsed RCSNode data), so we should be able to detect that the file needs
   1949  * to be removed without reparsing the file as we do below.
   1950  */
   1951 static void
   1952 fixaddfile (const char *rcs)
   1953 {
   1954     RCSNode *rcsfile;
   1955     int save_really_quiet;
   1956 
   1957     save_really_quiet = really_quiet;
   1958     really_quiet = 1;
   1959     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
   1960     {
   1961 	if (unlink_file (rcs) < 0)
   1962 	    error (0, errno, "cannot remove %s", rcs);
   1963     }
   1964     else
   1965 	freercsnode (&rcsfile);
   1966     really_quiet = save_really_quiet;
   1967 }
   1968 
   1969 
   1970 
   1971 /*
   1972  * put the branch back on an rcs file
   1973  */
   1974 static void
   1975 fixbranch (RCSNode *rcs, char *branch)
   1976 {
   1977     int retcode;
   1978 
   1979     if (branch != NULL)
   1980     {
   1981 	if ((retcode = RCS_setbranch (rcs, branch)) != 0)
   1982 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
   1983 		   "cannot restore branch to %s for %s", branch, rcs->path);
   1984 	RCS_rewrite (rcs, NULL, NULL);
   1985     }
   1986 }
   1987 
   1988 
   1989 
   1990 /*
   1991  * do the initial part of a file add for the named file.  if adding
   1992  * with a tag, put the file in the Attic and point the symbolic tag
   1993  * at the committed revision.
   1994  *
   1995  * INPUTS
   1996  *   file	The name of the file in the workspace.
   1997  *   repository	The repository directory to expect to find FILE,v in.
   1998  *   tag	The name or rev num of the branch being added to, if any.
   1999  *   options	Any RCS keyword expansion options specified by the user.
   2000  *   rcsnode	A pointer to the pre-parsed RCSNode for this file, if the file
   2001  *		exists in the repository.  If this is NULL, assume the file
   2002  *		does not yet exist.
   2003  *
   2004  * RETURNS
   2005  *   0 on success.
   2006  *   1 on errors, after printing any appropriate error messages.
   2007  *
   2008  * ERRORS
   2009  *   This function will return an error when any of the following functions do:
   2010  *     add_rcs_file
   2011  *     RCS_setattic
   2012  *     lock_RCS
   2013  *     RCS_checkin
   2014  *     RCS_parse (called to verify the newly created archive file)
   2015  *     RCS_settag
   2016  */
   2017 
   2018 static int
   2019 checkaddfile (const char *file, const char *repository, const char *tag,
   2020               const char *options, RCSNode **rcsnode)
   2021 {
   2022     RCSNode *rcs;
   2023     char *fname;
   2024     int newfile = 0;		/* Set to 1 if we created a new RCS archive. */
   2025     int retval = 1;
   2026     int adding_on_branch;
   2027 
   2028     assert (rcsnode != NULL);
   2029 
   2030     /* Callers expect to be able to use either "" or NULL to mean the
   2031        default keyword expansion.  */
   2032     if (options != NULL && options[0] == '\0')
   2033 	options = NULL;
   2034     if (options != NULL)
   2035 	assert (options[0] == '-' && options[1] == 'k');
   2036 
   2037     /* If numeric, it is on the trunk; check_fileproc enforced
   2038        this.  */
   2039     adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
   2040 
   2041     if (*rcsnode == NULL)
   2042     {
   2043 	char *rcsname;
   2044 	char *desc = NULL;
   2045 	size_t descalloc = 0;
   2046 	size_t desclen = 0;
   2047 	const char *opt;
   2048 
   2049 	if (adding_on_branch)
   2050 	{
   2051 	    mode_t omask;
   2052 	    rcsname = xmalloc (strlen (repository)
   2053 			       + sizeof (CVSATTIC)
   2054 			       + strlen (file)
   2055 			       + sizeof (RCSEXT)
   2056 			       + 3);
   2057 	    (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
   2058 	    omask = umask (cvsumask);
   2059 	    if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
   2060 		error (1, errno, "cannot make directory `%s'", rcsname);
   2061 	    (void) umask (omask);
   2062 	    (void) sprintf (rcsname,
   2063 			    "%s/%s/%s%s",
   2064 			    repository,
   2065 			    CVSATTIC,
   2066 			    file,
   2067 			    RCSEXT);
   2068 	}
   2069 	else
   2070 	    rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
   2071 
   2072 	/* this is the first time we have ever seen this file; create
   2073 	   an RCS file.  */
   2074 	fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
   2075 	/* If the file does not exist, no big deal.  In particular, the
   2076 	   server does not (yet at least) create CVSEXT_LOG files.  */
   2077 	if (isfile (fname))
   2078 	    /* FIXME: Should be including update_dir in the appropriate
   2079 	       place here.  */
   2080 	    get_file (fname, fname, "r", &desc, &descalloc, &desclen);
   2081 	free (fname);
   2082 
   2083 	/* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
   2084 	   end of the log message if the message is nonempty.
   2085 	   Do it.  RCS also deletes certain whitespace, in cleanlogmsg,
   2086 	   which we don't try to do here.  */
   2087 	if (desclen > 0)
   2088 	{
   2089 	    expand_string (&desc, &descalloc, desclen + 1);
   2090 	    desc[desclen++] = '\012';
   2091 	}
   2092 
   2093 	/* Set RCS keyword expansion options.  */
   2094 	if (options != NULL)
   2095 	    opt = options + 2;
   2096 	else
   2097 	    opt = NULL;
   2098 
   2099 	if (add_rcs_file (NULL, rcsname, file, NULL, opt,
   2100 			  NULL, NULL, 0, NULL,
   2101 			  desc, desclen, NULL, 0) != 0)
   2102 	{
   2103 	    if (rcsname != NULL)
   2104 	        free (rcsname);
   2105 	    goto out;
   2106 	}
   2107 	rcs = RCS_parsercsfile (rcsname);
   2108 	newfile = 1;
   2109 	if (rcsname != NULL)
   2110 	    free (rcsname);
   2111 	if (desc != NULL)
   2112 	    free (desc);
   2113 	*rcsnode = rcs;
   2114     }
   2115     else
   2116     {
   2117 	/* file has existed in the past.  Prepare to resurrect. */
   2118 	char *rev;
   2119 	char *oldexpand;
   2120 
   2121 	rcs = *rcsnode;
   2122 
   2123 	oldexpand = RCS_getexpand (rcs);
   2124 	if ((oldexpand != NULL
   2125 	     && options != NULL
   2126 	     && strcmp (options + 2, oldexpand) != 0)
   2127 	    || (oldexpand == NULL && options != NULL))
   2128 	{
   2129 	    /* We tell the user about this, because it means that the
   2130 	       old revisions will no longer retrieve the way that they
   2131 	       used to.  */
   2132 	    error (0, 0, "changing keyword expansion mode to %s", options);
   2133 	    RCS_setexpand (rcs, options + 2);
   2134 	}
   2135 
   2136 	if (!adding_on_branch)
   2137 	{
   2138 	    /* We are adding on the trunk, so move the file out of the
   2139 	       Attic.  */
   2140 	    if (!(rcs->flags & INATTIC))
   2141 	    {
   2142 		error (0, 0, "warning: expected %s to be in Attic",
   2143 		       rcs->path);
   2144 	    }
   2145 
   2146 	    /* Begin a critical section around the code that spans the
   2147 	       first commit on the trunk of a file that's already been
   2148 	       committed on a branch.  */
   2149 	    SIG_beginCrSect ();
   2150 
   2151 	    if (RCS_setattic (rcs, 0))
   2152 	    {
   2153 		goto out;
   2154 	    }
   2155 	}
   2156 
   2157 	rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
   2158 	/* and lock it */
   2159 	if (lock_RCS (file, rcs, rev, repository))
   2160 	{
   2161 	    error (0, 0, "cannot lock revision %s in `%s'.",
   2162 		   rev ? rev : tag ? tag : "HEAD", rcs->path);
   2163 	    if (rev != NULL)
   2164 		free (rev);
   2165 	    goto out;
   2166 	}
   2167 
   2168 	if (rev != NULL)
   2169 	    free (rev);
   2170     }
   2171 
   2172     /* when adding a file for the first time, and using a tag, we need
   2173        to create a dead revision on the trunk.  */
   2174     if (adding_on_branch)
   2175     {
   2176 	if (newfile)
   2177 	{
   2178 	    char *tmp;
   2179 	    FILE *fp;
   2180 	    int retcode;
   2181 
   2182 	    /* move the new file out of the way. */
   2183 	    fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
   2184 	    rename_file (file, fname);
   2185 
   2186 	    /* Create empty FILE.  Can't use copy_file with a DEVNULL
   2187 	       argument -- copy_file now ignores device files. */
   2188 	    fp = fopen (file, "w");
   2189 	    if (fp == NULL)
   2190 		error (1, errno, "cannot open %s for writing", file);
   2191 	    if (fclose (fp) < 0)
   2192 		error (0, errno, "cannot close %s", file);
   2193 
   2194 	    tmp = Xasprintf ("file %s was initially added on branch %s.",
   2195 			     file, tag);
   2196 	    /* commit a dead revision. */
   2197 	    retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
   2198 				   RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
   2199 	    free (tmp);
   2200 	    if (retcode != 0)
   2201 	    {
   2202 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
   2203 		       "could not create initial dead revision %s", rcs->path);
   2204 		free (fname);
   2205 		goto out;
   2206 	    }
   2207 
   2208 	    /* put the new file back where it was */
   2209 	    rename_file (fname, file);
   2210 	    free (fname);
   2211 
   2212 	    /* double-check that the file was written correctly */
   2213 	    freercsnode (&rcs);
   2214 	    rcs = RCS_parse (file, repository);
   2215 	    if (rcs == NULL)
   2216 	    {
   2217 		error (0, 0, "could not read %s", rcs->path);
   2218 		goto out;
   2219 	    }
   2220 	    *rcsnode = rcs;
   2221 
   2222 	    /* and lock it once again. */
   2223 	    if (lock_RCS (file, rcs, NULL, repository))
   2224 	    {
   2225 		error (0, 0, "cannot lock initial revision in `%s'.",
   2226 		       rcs->path);
   2227 		goto out;
   2228 	    }
   2229 	}
   2230 
   2231 	/* when adding with a tag, we need to stub a branch, if it
   2232 	   doesn't already exist.  */
   2233 	if (!RCS_nodeisbranch (rcs, tag))
   2234 	{
   2235 	    /* branch does not exist.  Stub it.  */
   2236 	    char *head;
   2237 	    char *magicrev;
   2238 	    int retcode;
   2239 	    time_t headtime = -1;
   2240 	    char *revnum, *tmp;
   2241 	    FILE *fp;
   2242 	    time_t t = -1;
   2243 	    struct tm *ct;
   2244 
   2245 	    fixbranch (rcs, sbranch);
   2246 
   2247 	    head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
   2248 	    if (!head)
   2249 		error (1, 0, "No head revision in archive file `%s'.",
   2250 		       rcs->print_path);
   2251 	    magicrev = RCS_magicrev (rcs, head);
   2252 
   2253 	    /* If this is not a new branch, then we will want a dead
   2254 	       version created before this one. */
   2255 	    if (!newfile)
   2256 		headtime = RCS_getrevtime (rcs, head, 0, 0);
   2257 
   2258 	    retcode = RCS_settag (rcs, tag, magicrev);
   2259 	    RCS_rewrite (rcs, NULL, NULL);
   2260 
   2261 	    free (head);
   2262 	    free (magicrev);
   2263 
   2264 	    if (retcode != 0)
   2265 	    {
   2266 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
   2267 		       "could not stub branch %s for %s", tag, rcs->path);
   2268 		goto out;
   2269 	    }
   2270 	    /* We need to add a dead version here to avoid -rtag -Dtime
   2271 	       checkout problems between when the head version was
   2272 	       created and now. */
   2273 	    if (!newfile && headtime != -1)
   2274 	    {
   2275 		/* move the new file out of the way. */
   2276 		fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
   2277 		rename_file (file, fname);
   2278 
   2279 		/* Create empty FILE.  Can't use copy_file with a DEVNULL
   2280 		   argument -- copy_file now ignores device files. */
   2281 		fp = fopen (file, "w");
   2282 		if (fp == NULL)
   2283 		    error (1, errno, "cannot open %s for writing", file);
   2284 		if (fclose (fp) < 0)
   2285 		    error (0, errno, "cannot close %s", file);
   2286 
   2287 		/* As we will be hacking the delta date, put the time
   2288 		   this was added into the log message. */
   2289 		t = time (NULL);
   2290 		ct = gmtime (&t);
   2291 		tmp = Xasprintf ("file %s was added on branch %s on %d-%02d-%02d %02d:%02d:%02d +0000",
   2292 				 file, tag,
   2293 				 ct->tm_year + (ct->tm_year < 100 ? 0 : 1900),
   2294 				 ct->tm_mon + 1, ct->tm_mday,
   2295 				 ct->tm_hour, ct->tm_min, ct->tm_sec);
   2296 
   2297 		/* commit a dead revision. */
   2298 		revnum = RCS_whatbranch (rcs, tag);
   2299 		retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
   2300 				       RCS_FLAGS_DEAD |
   2301 				       RCS_FLAGS_QUIET |
   2302 				       RCS_FLAGS_USETIME);
   2303 		free (revnum);
   2304 		free (tmp);
   2305 
   2306 		if (retcode != 0)
   2307 		{
   2308 		    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
   2309 			   "could not created dead stub %s for %s", tag,
   2310 			   rcs->path);
   2311 		    goto out;
   2312 		}
   2313 
   2314 		/* put the new file back where it was */
   2315 		rename_file (fname, file);
   2316 		free (fname);
   2317 
   2318 		/* double-check that the file was written correctly */
   2319 		freercsnode (&rcs);
   2320 		rcs = RCS_parse (file, repository);
   2321 		if (rcs == NULL)
   2322 		{
   2323 		    error (0, 0, "could not read %s", rcs->path);
   2324 		    goto out;
   2325 		}
   2326 		*rcsnode = rcs;
   2327 	    }
   2328 	}
   2329 	else
   2330 	{
   2331 	    /* lock the branch. (stubbed branches need not be locked.)  */
   2332 	    if (lock_RCS (file, rcs, NULL, repository))
   2333 	    {
   2334 		error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
   2335 		goto out;
   2336 	    }
   2337 	}
   2338 
   2339 	if (*rcsnode != rcs)
   2340 	{
   2341 	    freercsnode (rcsnode);
   2342 	    *rcsnode = rcs;
   2343 	}
   2344     }
   2345 
   2346     fileattr_newfile (file);
   2347 
   2348     /* At this point, we used to set the file mode of the RCS file
   2349        based on the mode of the file in the working directory.  If we
   2350        are creating the RCS file for the first time, add_rcs_file does
   2351        this already.  If we are re-adding the file, then perhaps it is
   2352        consistent to preserve the old file mode, just as we preserve
   2353        the old keyword expansion mode.
   2354 
   2355        If we decide that we should change the modes, then we can't do
   2356        it here anyhow.  At this point, the RCS file may be owned by
   2357        somebody else, so a chmod will fail.  We need to instead do the
   2358        chmod after rewriting it.
   2359 
   2360        FIXME: In general, I think the file mode (and the keyword
   2361        expansion mode) should be associated with a particular revision
   2362        of the file, so that it is possible to have different revisions
   2363        of a file have different modes.  */
   2364 
   2365     retval = 0;
   2366 
   2367  out:
   2368     if (retval != 0 && SIG_inCrSect ())
   2369 	SIG_endCrSect ();
   2370     return retval;
   2371 }
   2372 
   2373 
   2374 
   2375 /*
   2376  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
   2377  * couldn't.  If the RCS file currently has a branch as the head, we must
   2378  * move the head back to the trunk before locking the file, and be sure to
   2379  * put the branch back as the head if there are any errors.
   2380  */
   2381 static int
   2382 lock_RCS (const char *user, RCSNode *rcs, const char *rev,
   2383           const char *repository)
   2384 {
   2385     char *branch = NULL;
   2386     int err = 0;
   2387 
   2388     /*
   2389      * For a specified, numeric revision of the form "1" or "1.1", (or when
   2390      * no revision is specified ""), definitely move the branch to the trunk
   2391      * before locking the RCS file.
   2392      *
   2393      * The assumption is that if there is more than one revision on the trunk,
   2394      * the head points to the trunk, not a branch... and as such, it's not
   2395      * necessary to move the head in this case.
   2396      */
   2397     if (rev == NULL
   2398 	|| (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
   2399     {
   2400 	branch = xstrdup (rcs->branch);
   2401 	if (branch != NULL)
   2402 	{
   2403 	    if (RCS_setbranch (rcs, NULL) != 0)
   2404 	    {
   2405 		error (0, 0, "cannot change branch to default for %s",
   2406 		       rcs->path);
   2407 		if (branch)
   2408 		    free (branch);
   2409 		return 1;
   2410 	    }
   2411 	}
   2412 	err = RCS_lock (rcs, NULL, 1);
   2413     }
   2414     else
   2415     {
   2416 	RCS_lock (rcs, rev, 1);
   2417     }
   2418 
   2419     /* We used to call RCS_rewrite here, and that might seem
   2420        appropriate in order to write out the locked revision
   2421        information.  However, such a call would actually serve no
   2422        purpose.  CVS locks will prevent any interference from other
   2423        CVS processes.  The comment above rcs_internal_lockfile
   2424        explains that it is already unsafe to use RCS and CVS
   2425        simultaneously.  It follows that writing out the locked
   2426        revision information here would add no additional security.
   2427 
   2428        If we ever do care about it, the proper fix is to create the
   2429        RCS lock file before calling this function, and maintain it
   2430        until the checkin is complete.
   2431 
   2432        The call to RCS_lock is still required at present, since in
   2433        some cases RCS_checkin will determine which revision to check
   2434        in by looking for a lock.  FIXME: This is rather roundabout,
   2435        and a more straightforward approach would probably be easier to
   2436        understand.  */
   2437 
   2438     if (err == 0)
   2439     {
   2440 	if (sbranch != NULL)
   2441 	    free (sbranch);
   2442 	sbranch = branch;
   2443 	return 0;
   2444     }
   2445 
   2446     /* try to restore the branch if we can on error */
   2447     if (branch != NULL)
   2448 	fixbranch (rcs, branch);
   2449 
   2450     if (branch)
   2451 	free (branch);
   2452     return 1;
   2453 }
   2454 
   2455 
   2456 
   2457 /*
   2458  * free an UPDATE node's data
   2459  */
   2460 void
   2461 update_delproc (Node *p)
   2462 {
   2463     struct logfile_info *li = p->data;
   2464 
   2465     if (li->tag)
   2466 	free (li->tag);
   2467     if (li->rev_old)
   2468 	free (li->rev_old);
   2469     if (li->rev_new)
   2470 	free (li->rev_new);
   2471     free (li);
   2472 }
   2473 
   2474 /*
   2475  * Free the commit_info structure in p.
   2476  */
   2477 static void
   2478 ci_delproc (Node *p)
   2479 {
   2480     struct commit_info *ci = p->data;
   2481 
   2482     if (ci->rev)
   2483 	free (ci->rev);
   2484     if (ci->tag)
   2485 	free (ci->tag);
   2486     if (ci->options)
   2487 	free (ci->options);
   2488     free (ci);
   2489 }
   2490 
   2491 /*
   2492  * Free the commit_info structure in p.
   2493  */
   2494 static void
   2495 masterlist_delproc (Node *p)
   2496 {
   2497     struct master_lists *ml = p->data;
   2498 
   2499     dellist (&ml->ulist);
   2500     dellist (&ml->cilist);
   2501     free (ml);
   2502 }
   2503