"Fossies" - the Fresh Open Source Software Archive

Member "cvs-1.11.23/src/commit.c" (28 Jun 2006, 65697 Bytes) of package /linux/misc/old/cvs-1.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "commit.c" see the Fossies "Dox" file reference documentation.

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