home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / commit.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  50KB  |  1,988 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.4 kit.
  7.  * 
  8.  * Commit Files
  9.  * 
  10.  * "commit" commits the present version to the RCS repository, AFTER
  11.  * having done a test on conflicts.
  12.  *
  13.  * The call is: cvs commit [options] files...
  14.  * 
  15.  */
  16.  
  17. #include "cvs.h"
  18. #include "getline.h"
  19. #include "edit.h"
  20. #include "fileattr.h"
  21.  
  22. static Dtype check_direntproc PROTO ((void *callerdat, char *dir,
  23.                       char *repos, char *update_dir,
  24.                       List *entries));
  25. static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  26. static int check_filesdoneproc PROTO ((void *callerdat, int err,
  27.                        char *repos, char *update_dir,
  28.                        List *entries));
  29. static int checkaddfile PROTO((char *file, char *repository, char *tag,
  30.                    char *options, RCSNode **rcsnode)); 
  31. static Dtype commit_direntproc PROTO ((void *callerdat, char *dir,
  32.                        char *repos, char *update_dir,
  33.                        List *entries));
  34. static int commit_dirleaveproc PROTO ((void *callerdat, char *dir,
  35.                        int err, char *update_dir,
  36.                        List *entries));
  37. static int commit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  38. static int commit_filesdoneproc PROTO ((void *callerdat, int err,
  39.                     char *repository, char *update_dir,
  40.                     List *entries));
  41. static int finaladd PROTO((struct file_info *finfo, char *revision, char *tag,
  42.                char *options));
  43. static int findmaxrev PROTO((Node * p, void *closure));
  44. static int lock_RCS PROTO((char *user, char *rcs, char *rev, char *repository));
  45. static int precommit_list_proc PROTO((Node * p, void *closure));
  46. static int precommit_proc PROTO((char *repository, char *filter));
  47. static int remove_file PROTO ((struct file_info *finfo, char *tag,
  48.                    char *message));
  49. static void fix_rcs_modes PROTO((char *rcs, char *user));
  50. static void fixaddfile PROTO((char *file, char *repository));
  51. static void fixbranch PROTO((char *rcs, char *branch));
  52. static void unlockrcs PROTO((char *rcs));
  53. static void ci_delproc PROTO((Node *p));
  54. static void masterlist_delproc PROTO((Node *p));
  55. static void locate_rcs PROTO((char *file, char *repository, char *rcs));
  56.  
  57. struct commit_info
  58. {
  59.     Ctype status;            /* as returned from Classify_File() */
  60.     char *rev;                /* a numeric rev, if we know it */
  61.     char *tag;                /* any sticky tag, or -r option */
  62.     char *options;            /* Any sticky -k option */
  63. };
  64. struct master_lists
  65. {
  66.     List *ulist;            /* list for Update_Logfile */
  67.     List *cilist;            /* list with commit_info structs */
  68. };
  69.  
  70. static int force_ci = 0;
  71. static int got_message;
  72. static int run_module_prog = 1;
  73. static int aflag;
  74. static char *tag;
  75. static char *write_dirtag;
  76. static char *logfile;
  77. static List *mulist;
  78. static char *message;
  79. static time_t last_register_time;
  80.  
  81.  
  82. static const char *const commit_usage[] =
  83. {
  84.     "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n",
  85.     "\t-n\tDo not run the module program (if any).\n",
  86.     "\t-R\tProcess directories recursively.\n",
  87.     "\t-l\tLocal directory only (not recursive).\n",
  88.     "\t-f\tForce the file to be committed; disables recursion.\n",
  89.     "\t-F file\tRead the log message from file.\n",
  90.     "\t-m msg\tLog message.\n",
  91.     "\t-r rev\tCommit to this branch or trunk revision.\n",
  92.     NULL
  93. };
  94.  
  95. #ifdef CLIENT_SUPPORT
  96. /* Identify a file which needs "? foo" or a Questionable request.  */
  97. struct question {
  98.     /* The two fields for the Directory request.  */
  99.     char *dir;
  100.     char *repos;
  101.  
  102.     /* The file name.  */
  103.     char *file;
  104.  
  105.     struct question *next;
  106. };
  107.  
  108. struct find_data {
  109.     List *ulist;
  110.     int argc;
  111.     char **argv;
  112.  
  113.     /* This is used from dirent to filesdone time, for each directory,
  114.        to make a list of files we have already seen.  */
  115.     List *ignlist;
  116.  
  117.     /* Linked list of files which need "? foo" or a Questionable request.  */
  118.     struct question *questionables;
  119.  
  120.     /* Only good within functions called from the filesdoneproc.  Stores
  121.        the repository (pointer into storage managed by the recursion
  122.        processor.  */
  123.     char *repository;
  124. };
  125.  
  126. static Dtype find_dirent_proc PROTO ((void *callerdat, char *dir,
  127.                       char *repository, char *update_dir,
  128.                       List *entries));
  129.  
  130. static Dtype
  131. find_dirent_proc (callerdat, dir, repository, update_dir, entries)
  132.     void *callerdat;
  133.     char *dir;
  134.     char *repository;
  135.     char *update_dir;
  136.     List *entries;
  137. {
  138.     struct find_data *find_data = (struct find_data *)callerdat;
  139.  
  140.     /* initialize the ignore list for this directory */
  141.     find_data->ignlist = getlist ();
  142.     return R_PROCESS;
  143. }
  144.  
  145. /* Here as a static until we get around to fixing ignore_files to pass
  146.    it along as an argument.  */
  147. static struct find_data *find_data_static;
  148.  
  149. static void find_ignproc PROTO ((char *, char *));
  150.  
  151. static void
  152. find_ignproc (file, dir)
  153.     char *file;
  154.     char *dir;
  155. {
  156.     struct question *p;
  157.  
  158.     p = (struct question *) xmalloc (sizeof (struct question));
  159.     p->dir = xstrdup (dir);
  160.     p->repos = xstrdup (find_data_static->repository);
  161.     p->file = xstrdup (file);
  162.     p->next = find_data_static->questionables;
  163.     find_data_static->questionables = p;
  164. }
  165.  
  166. static int find_filesdoneproc PROTO ((void *callerdat, int err,
  167.                       char *repository, char *update_dir,
  168.                       List *entries));
  169.  
  170. static int
  171. find_filesdoneproc (callerdat, err, repository, update_dir, entries)
  172.     void *callerdat;
  173.     int err;
  174.     char *repository;
  175.     char *update_dir;
  176.     List *entries;
  177. {
  178.     struct find_data *find_data = (struct find_data *)callerdat;
  179.     find_data->repository = repository;
  180.  
  181.     /* if this directory has an ignore list, process it then free it */
  182.     if (find_data->ignlist)
  183.     {
  184.     find_data_static = find_data;
  185.     ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
  186.     dellist (&find_data->ignlist);
  187.     }
  188.  
  189.     find_data->repository = NULL;
  190.  
  191.     return err;
  192. }
  193.  
  194. static int find_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  195.  
  196. /* Machinery to find out what is modified, added, and removed.  It is
  197.    possible this should be broken out into a new client_classify function;
  198.    merging it with classify_file is almost sure to be a mess, though,
  199.    because classify_file has all kinds of repository processing.  */
  200. static int
  201. find_fileproc (callerdat, finfo)
  202.     void *callerdat;
  203.     struct file_info *finfo;
  204. {
  205.     Vers_TS *vers;
  206.     enum classify_type status;
  207.     Node *node;
  208.     struct find_data *args = (struct find_data *)callerdat;
  209.     struct logfile_info *data;
  210.     struct file_info xfinfo;
  211.  
  212.     /* if this directory has an ignore list, add this file to it */
  213.     if (args->ignlist)
  214.     {
  215.     Node *p;
  216.  
  217.     p = getnode ();
  218.     p->type = FILES;
  219.     p->key = xstrdup (finfo->file);
  220.     if (addnode (args->ignlist, p) != 0)
  221.         freenode (p);
  222.     }
  223.  
  224.     xfinfo = *finfo;
  225.     xfinfo.repository = NULL;
  226.     xfinfo.rcs = NULL;
  227.  
  228.     vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
  229.     if (vers->ts_user == NULL
  230.     && vers->vn_user != NULL
  231.     && vers->vn_user[0] == '-')
  232.     /* FIXME: If vn_user is starts with "-" but ts_user is
  233.        non-NULL, what classify_file does is print "%s should be
  234.        removed and is still there".  I'm not sure what it does
  235.        then.  We probably should do the same.  */
  236.     status = T_REMOVED;
  237.     else if (vers->vn_user == NULL)
  238.     {
  239.     if (vers->ts_user == NULL)
  240.         error (0, 0, "nothing known about `%s'", finfo->fullname);
  241.     else
  242.         error (0, 0, "use `cvs add' to create an entry for %s",
  243.            finfo->fullname);
  244.     return 1;
  245.     }
  246.     else if (vers->ts_user != NULL
  247.          && vers->vn_user != NULL
  248.          && vers->vn_user[0] == '0')
  249.     /* FIXME: If vn_user is "0" but ts_user is NULL, what classify_file
  250.        does is print "new-born %s has disappeared" and removes the entry.
  251.        We probably should do the same.  */
  252.     status = T_ADDED;
  253.     else if (vers->ts_user != NULL
  254.          && vers->ts_rcs != NULL
  255.          && strcmp (vers->ts_user, vers->ts_rcs) != 0)
  256.     status = T_MODIFIED;
  257.     else
  258.     {
  259.     /* This covers unmodified files, as well as a variety of other
  260.        cases.  FIXME: we probably should be printing a message and
  261.        returning 1 for many of those cases (but I'm not sure
  262.        exactly which ones).  */
  263.     return 0;
  264.     }
  265.  
  266.     node = getnode ();
  267.     node->key = xstrdup (finfo->fullname);
  268.  
  269.     data = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
  270.     data->type = status;
  271.     data->tag = xstrdup (vers->tag);
  272.  
  273.     node->type = UPDATE;
  274.     node->delproc = update_delproc;
  275.     node->data = (char *) data;
  276.     (void)addnode (args->ulist, node);
  277.  
  278.     ++args->argc;
  279.  
  280.     freevers_ts (&vers);
  281.     return 0;
  282. }
  283.  
  284. static int copy_ulist PROTO ((Node *, void *));
  285.  
  286. static int
  287. copy_ulist (node, data)
  288.     Node *node;
  289.     void *data;
  290. {
  291.     struct find_data *args = (struct find_data *)data;
  292.     args->argv[args->argc++] = node->key;
  293.     return 0;
  294. }
  295. #endif /* CLIENT_SUPPORT */
  296.  
  297. int
  298. commit (argc, argv)
  299.     int argc;
  300.     char **argv;
  301. {
  302.     int c;
  303.     int err = 0;
  304.     int local = 0;
  305.  
  306.     if (argc == -1)
  307.     usage (commit_usage);
  308.  
  309. #ifdef CVS_BADROOT
  310.     /*
  311.      * For log purposes, do not allow "root" to commit files.  If you look
  312.      * like root, but are really logged in as a non-root user, it's OK.
  313.      */
  314.     if (geteuid () == (uid_t) 0)
  315.     {
  316.     struct passwd *pw;
  317.  
  318.     if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL)
  319.         error (1, 0, "you are unknown to this system");
  320.     if (pw->pw_uid == (uid_t) 0)
  321.         error (1, 0, "cannot commit files as 'root'");
  322.     }
  323. #endif /* CVS_BADROOT */
  324.  
  325.     optind = 1;
  326.     while ((c = getopt (argc, argv, "nlRm:fF:r:")) != -1)
  327.     {
  328.     switch (c)
  329.     {
  330.         case 'n':
  331.         run_module_prog = 0;
  332.         break;
  333.         case 'm':
  334. #ifdef FORCE_USE_EDITOR
  335.         use_editor = TRUE;
  336. #else
  337.         use_editor = FALSE;
  338. #endif
  339.         if (message)
  340.         {
  341.             free (message);
  342.             message = NULL;
  343.         }
  344.  
  345.         message = xstrdup(optarg);
  346.         break;
  347.         case 'r':
  348.         if (tag)
  349.             free (tag);
  350.         tag = xstrdup (optarg);
  351.         break;
  352.         case 'l':
  353.         local = 1;
  354.         break;
  355.         case 'R':
  356.         local = 0;
  357.         break;
  358.         case 'f':
  359.         force_ci = 1;
  360.         local = 1;        /* also disable recursion */
  361.         break;
  362.         case 'F':
  363. #ifdef FORCE_USE_EDITOR
  364.         use_editor = TRUE;
  365. #else
  366.         use_editor = FALSE;
  367. #endif
  368.         logfile = optarg;
  369.         break;
  370.         case '?':
  371.         default:
  372.         usage (commit_usage);
  373.         break;
  374.     }
  375.     }
  376.     argc -= optind;
  377.     argv += optind;
  378.  
  379.     /* numeric specified revision means we ignore sticky tags... */
  380.     if (tag && isdigit (*tag))
  381.     {
  382.     aflag = 1;
  383.     /* strip trailing dots */
  384.     while (tag[strlen (tag) - 1] == '.')
  385.         tag[strlen (tag) - 1] = '\0';
  386.     }
  387.  
  388.     /* some checks related to the "-F logfile" option */
  389.     if (logfile)
  390.     {
  391.     int n, logfd;
  392.     struct stat statbuf;
  393.  
  394.     if (message)
  395.         error (1, 0, "cannot specify both a message and a log file");
  396.  
  397.     /* FIXME: Why is this binary?  Needs more investigation.  */
  398.     if ((logfd = CVS_OPEN (logfile, O_RDONLY | OPEN_BINARY)) < 0)
  399.         error (1, errno, "cannot open log file %s", logfile);
  400.  
  401.     if (fstat(logfd, &statbuf) < 0)
  402.         error (1, errno, "cannot find size of log file %s", logfile);
  403.  
  404.     message = xmalloc (statbuf.st_size + 1);
  405.  
  406.     /* FIXME: Should keep reading until EOF, rather than assuming the
  407.        first read gets the whole thing.  */
  408.     if ((n = read (logfd, message, statbuf.st_size + 1)) < 0)
  409.         error (1, errno, "cannot read log message from %s", logfile);
  410.  
  411.     (void) close (logfd);
  412.     message[n] = '\0';
  413.     }
  414.  
  415. #ifdef CLIENT_SUPPORT
  416.     if (client_active) 
  417.     {
  418.     struct find_data find_args;
  419.  
  420.     ign_setup ();
  421.  
  422.     find_args.ulist = getlist ();
  423.     find_args.argc = 0;
  424.     find_args.questionables = NULL;
  425.     find_args.ignlist = NULL;
  426.     find_args.repository = NULL;
  427.  
  428.     err = start_recursion (find_fileproc, find_filesdoneproc,
  429.                    find_dirent_proc, (DIRLEAVEPROC) NULL,
  430.                    (void *)&find_args,
  431.                    argc, argv, local, W_LOCAL, 0, 0,
  432.                    (char *)NULL, 0);
  433.     if (err)
  434.         error (1, 0, "correct above errors first!");
  435.  
  436.     if (find_args.argc == 0)
  437.         /* Nothing to commit.  Exit now without contacting the
  438.            server (note that this means that we won't print "?
  439.            foo" for files which merit it, because we don't know
  440.            what is in the CVSROOT/cvsignore file).  */
  441.         return 0;
  442.  
  443.     /* Now we keep track of which files we actually are going to
  444.        operate on, and only work with those files in the future.
  445.        This saves time--we don't want to search the file system
  446.        of the working directory twice.  */
  447.     find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **));
  448.     find_args.argc = 0;
  449.     walklist (find_args.ulist, copy_ulist, &find_args);
  450.  
  451.     /* Do this before calling do_editor; don't ask for a log
  452.        message if we can't talk to the server.  But do it after we
  453.        have made the checks that we can locally (to more quickly
  454.        catch syntax errors, the case where no files are modified,
  455.        added or removed, etc.).
  456.  
  457.        On the other hand, calling start_server before do_editor
  458.        means that we chew up server resources the whole time that
  459.        the user has the editor open (hours or days if the user
  460.        forgets about it), which seems dubious.  */
  461.     start_server ();
  462.  
  463.     /*
  464.      * We do this once, not once for each directory as in normal CVS.
  465.      * The protocol is designed this way.  This is a feature.
  466.      */
  467.     if (use_editor)
  468.         do_editor (".", &message, (char *)NULL, find_args.ulist);
  469.  
  470.     /* We always send some sort of message, even if empty.  */
  471.     option_with_arg ("-m", message);
  472.  
  473.     /* OK, now process all the questionable files we have been saving
  474.        up.  */
  475.     {
  476.         struct question *p;
  477.         struct question *q;
  478.  
  479.         p = find_args.questionables;
  480.         while (p != NULL)
  481.         {
  482.         if (ign_inhibit_server || !supported_request ("Questionable"))
  483.         {
  484.             cvs_output ("? ", 2);
  485.             if (p->dir[0] != '\0')
  486.             {
  487.             cvs_output (p->dir, 0);
  488.             cvs_output ("/", 1);
  489.             }
  490.             cvs_output (p->file, 0);
  491.             cvs_output ("\n", 1);
  492.         }
  493.         else
  494.         {
  495.             send_to_server ("Directory ", 0);
  496.             send_to_server (p->dir[0] == '\0' ? "." : p->dir, 0);
  497.             send_to_server ("\012", 1);
  498.             send_to_server (p->repos, 0);
  499.             send_to_server ("\012", 1);
  500.  
  501.             send_to_server ("Questionable ", 0);
  502.             send_to_server (p->file, 0);
  503.             send_to_server ("\012", 1);
  504.         }
  505.         free (p->dir);
  506.         free (p->repos);
  507.         free (p->file);
  508.         q = p->next;
  509.         free (p);
  510.         p = q;
  511.         }
  512.     }
  513.  
  514.     if (local)
  515.         send_arg("-l");
  516.     if (force_ci)
  517.         send_arg("-f");
  518.     if (!run_module_prog)
  519.         send_arg("-n");
  520.     option_with_arg ("-r", tag);
  521.  
  522.     /* Sending only the names of the files which were modified, added,
  523.        or removed means that the server will only do an up-to-date
  524.        check on those files.  This is different from local CVS and
  525.        previous versions of client/server CVS, but it probably is a Good
  526.        Thing, or at least Not Such A Bad Thing.  */
  527.     send_file_names (find_args.argc, find_args.argv, 0);
  528.     send_files (find_args.argc, find_args.argv, local, 0);
  529.  
  530.     send_to_server ("ci\012", 0);
  531.     return get_responses_and_close ();
  532.     }
  533. #endif
  534.  
  535.     if (tag != NULL)
  536.     tag_check_valid (tag, argc, argv, local, aflag, "");
  537.  
  538.     /* XXX - this is not the perfect check for this */
  539.     if (argc <= 0)
  540.     write_dirtag = tag;
  541.  
  542.     wrap_setup ();
  543.  
  544.     lock_tree_for_write (argc, argv, local, aflag);
  545.  
  546.     /*
  547.      * Set up the master update list
  548.      */
  549.     mulist = getlist ();
  550.  
  551.     /*
  552.      * Run the recursion processor to verify the files are all up-to-date
  553.      */
  554.     err = start_recursion (check_fileproc, check_filesdoneproc,
  555.                check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc,
  556.                argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1);
  557.     if (err)
  558.     {
  559.     lock_tree_cleanup ();
  560.     error (1, 0, "correct above errors first!");
  561.     }
  562.  
  563.     /*
  564.      * Run the recursion processor to commit the files
  565.      */
  566.     if (noexec == 0)
  567.     err = start_recursion (commit_fileproc, commit_filesdoneproc,
  568.                    commit_direntproc, commit_dirleaveproc, NULL,
  569.                    argc, argv, local, W_LOCAL, aflag, 0,
  570.                    (char *) NULL, 1);
  571.  
  572.     /*
  573.      * Unlock all the dirs and clean up
  574.      */
  575.     lock_tree_cleanup ();
  576.     dellist (&mulist);
  577.  
  578.     if (last_register_time)
  579.     {
  580.     time_t now;
  581.  
  582.     (void) time (&now);
  583.     if (now == last_register_time) 
  584.     {
  585.         sleep (1);            /* to avoid time-stamp races */
  586.     }
  587.     }
  588.  
  589.     return (err);
  590. }
  591.  
  592. /*
  593.  * Check to see if a file is ok to commit and make sure all files are
  594.  * up-to-date
  595.  */
  596. /* ARGSUSED */
  597. static int
  598. check_fileproc (callerdat, finfo)
  599.     void *callerdat;
  600.     struct file_info *finfo;
  601. {
  602.     Ctype status;
  603.     char *xdir;
  604.     Node *p;
  605.     List *ulist, *cilist;
  606.     Vers_TS *vers;
  607.     struct commit_info *ci;
  608.     struct logfile_info *li;
  609.     int save_noexec, save_quiet, save_really_quiet;
  610.  
  611.     save_noexec = noexec;
  612.     save_quiet = quiet;
  613.     save_really_quiet = really_quiet;
  614.     noexec = quiet = really_quiet = 1;
  615.  
  616.     /* handle specified numeric revision specially */
  617.     if (tag && isdigit (*tag))
  618.     {
  619.     /* If the tag is for the trunk, make sure we're at the head */
  620.     if (numdots (tag) < 2)
  621.     {
  622.         status = Classify_File (finfo, (char *) NULL, (char *) NULL,
  623.                     (char *) NULL, 1, aflag, &vers, 0);
  624.         if (status == T_UPTODATE || status == T_MODIFIED ||
  625.         status == T_ADDED)
  626.         {
  627.         Ctype xstatus;
  628.  
  629.         freevers_ts (&vers);
  630.         xstatus = Classify_File (finfo, tag, (char *) NULL,
  631.                      (char *) NULL, 1, aflag, &vers, 0);
  632.         if (xstatus == T_REMOVE_ENTRY)
  633.             status = T_MODIFIED;
  634.         else if (status == T_MODIFIED && xstatus == T_CONFLICT)
  635.             status = T_MODIFIED;
  636.         else
  637.             status = xstatus;
  638.         }
  639.     }
  640.     else
  641.     {
  642.         char *xtag, *cp;
  643.  
  644.         /*
  645.          * The revision is off the main trunk; make sure we're
  646.          * up-to-date with the head of the specified branch.
  647.          */
  648.         xtag = xstrdup (tag);
  649.         if ((numdots (xtag) & 1) != 0)
  650.         {
  651.         cp = strrchr (xtag, '.');
  652.         *cp = '\0';
  653.         }
  654.         status = Classify_File (finfo, xtag, (char *) NULL,
  655.                     (char *) NULL, 1, aflag, &vers, 0);
  656.         if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
  657.         && (cp = strrchr (xtag, '.')) != NULL)
  658.         {
  659.         /* pluck one more dot off the revision */
  660.         *cp = '\0';
  661.         freevers_ts (&vers);
  662.         status = Classify_File (finfo, xtag, (char *) NULL,
  663.                     (char *) NULL, 1, aflag, &vers, 0);
  664.         if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
  665.             status = T_MODIFIED;
  666.         }
  667.         /* now, muck with vers to make the tag correct */
  668.         free (vers->tag);
  669.         vers->tag = xstrdup (tag);
  670.         free (xtag);
  671.     }
  672.     }
  673.     else
  674.     status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL,
  675.                 1, 0, &vers, 0);
  676.     noexec = save_noexec;
  677.     quiet = save_quiet;
  678.     really_quiet = save_really_quiet;
  679.  
  680.     /*
  681.      * If the force-commit option is enabled, and the file in question
  682.      * appears to be up-to-date, just make it look modified so that
  683.      * it will be committed.
  684.      */
  685.     if (force_ci && status == T_UPTODATE)
  686.     status = T_MODIFIED;
  687.  
  688.     switch (status)
  689.     {
  690.     case T_CHECKOUT:
  691. #ifdef SERVER_SUPPORT
  692.     case T_PATCH:
  693. #endif
  694.     case T_NEEDS_MERGE:
  695.     case T_CONFLICT:
  696.     case T_REMOVE_ENTRY:
  697.         error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
  698.         freevers_ts (&vers);
  699.         return (1);
  700.     case T_MODIFIED:
  701.     case T_ADDED:
  702.     case T_REMOVED:
  703.         /*
  704.          * some quick sanity checks; if no numeric -r option specified:
  705.          *    - can't have a sticky date
  706.          *    - can't have a sticky tag that is not a branch
  707.          * Also,
  708.          *    - if status is T_REMOVED, can't have a numeric tag
  709.          *    - if status is T_ADDED, rcs file must not exist unless on
  710.          *    a branch
  711.          *    - if status is T_ADDED, can't have a non-trunk numeric rev
  712.          *    - if status is T_MODIFIED and a Conflict marker exists, don't
  713.          *    allow the commit if timestamp is identical or if we find
  714.          *    an RCS_MERGE_PAT in the file.
  715.          */
  716.         if (!tag || !isdigit (*tag))
  717.         {
  718.         if (vers->date)
  719.         {
  720.             error (0, 0,
  721.                "cannot commit with sticky date for file `%s'",
  722.                finfo->fullname);
  723.             freevers_ts (&vers);
  724.             return (1);
  725.         }
  726.         if (status == T_MODIFIED && vers->tag &&
  727.             !RCS_isbranch (finfo->rcs, vers->tag))
  728.         {
  729.             error (0, 0,
  730.                "sticky tag `%s' for file `%s' is not a branch",
  731.                vers->tag, finfo->fullname);
  732.             freevers_ts (&vers);
  733.             return (1);
  734.         }
  735.         }
  736.         if (status == T_MODIFIED && !force_ci && vers->ts_conflict)
  737.         {
  738.         char *filestamp;
  739.         int retcode;
  740.  
  741.         /*
  742.          * We found a "conflict" marker.
  743.          *
  744.          * If the timestamp on the file is the same as the
  745.          * timestamp stored in the Entries file, we block the commit.
  746.          */
  747. #ifdef SERVER_SUPPORT
  748.         if (server_active)
  749.             retcode = vers->ts_conflict[0] != '=';
  750.         else {
  751.             filestamp = time_stamp (finfo->file);
  752.             retcode = strcmp (vers->ts_conflict, filestamp);
  753.             free (filestamp);
  754.         }
  755. #else
  756.         filestamp = time_stamp (finfo->file);
  757.         retcode = strcmp (vers->ts_conflict, filestamp);
  758.         free (filestamp);
  759. #endif
  760.         if (retcode == 0)
  761.         {
  762.             error (0, 0,
  763.               "file `%s' had a conflict and has not been modified",
  764.                finfo->fullname);
  765.             freevers_ts (&vers);
  766.             return (1);
  767.         }
  768.  
  769.         /*
  770.          * If the timestamps differ, look for Conflict indicators
  771.          * in the file to see if we should block the commit anyway
  772.          */
  773.         run_setup ("%s", GREP);
  774.         run_arg (RCS_MERGE_PAT);
  775.         run_arg (finfo->file);
  776.         retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY);
  777.             
  778.         if (retcode == -1)
  779.         {
  780.             error (1, errno,
  781.                "fork failed while examining conflict in `%s'",
  782.                finfo->fullname);
  783.         }
  784.         else if (retcode == 0)
  785.         {
  786.             error (0, 0,
  787.                "file `%s' still contains conflict indicators",
  788.                finfo->fullname);
  789.             freevers_ts (&vers);
  790.             return (1);
  791.         }
  792.         }
  793.  
  794.         if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
  795.         {
  796.         error (0, 0,
  797.     "cannot remove file `%s' which has a numeric sticky tag of `%s'",
  798.                finfo->fullname, vers->tag);
  799.         freevers_ts (&vers);
  800.         return (1);
  801.         }
  802.         if (status == T_ADDED)
  803.         {
  804.             if (vers->tag == NULL)
  805.         {
  806.             char rcs[PATH_MAX];
  807.  
  808.             /* Don't look in the attic; if it exists there we
  809.                will move it back out in checkaddfile.  */
  810.             sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file,
  811.                 RCSEXT);
  812.             if (isreadable (rcs))
  813.             {
  814.             error (0, 0,
  815.             "cannot add file `%s' when RCS file `%s' already exists",
  816.                    finfo->fullname, rcs);
  817.             freevers_ts (&vers);
  818.             return (1);
  819.             }
  820.         }
  821.         if (vers->tag && isdigit (*vers->tag) &&
  822.             numdots (vers->tag) > 1)
  823.         {
  824.             error (0, 0,
  825.         "cannot add file `%s' with revision `%s'; must be on trunk",
  826.                    finfo->fullname, vers->tag);
  827.             freevers_ts (&vers);
  828.             return (1);
  829.         }
  830.         }
  831.  
  832.         /* done with consistency checks; now, to get on with the commit */
  833.         if (finfo->update_dir[0] == '\0')
  834.         xdir = ".";
  835.         else
  836.         xdir = finfo->update_dir;
  837.         if ((p = findnode (mulist, xdir)) != NULL)
  838.         {
  839.         ulist = ((struct master_lists *) p->data)->ulist;
  840.         cilist = ((struct master_lists *) p->data)->cilist;
  841.         }
  842.         else
  843.         {
  844.         struct master_lists *ml;
  845.  
  846.         ulist = getlist ();
  847.         cilist = getlist ();
  848.         p = getnode ();
  849.         p->key = xstrdup (xdir);
  850.         p->type = UPDATE;
  851.         ml = (struct master_lists *)
  852.             xmalloc (sizeof (struct master_lists));
  853.         ml->ulist = ulist;
  854.         ml->cilist = cilist;
  855.         p->data = (char *) ml;
  856.         p->delproc = masterlist_delproc;
  857.         (void) addnode (mulist, p);
  858.         }
  859.  
  860.         /* first do ulist, then cilist */
  861.         p = getnode ();
  862.         p->key = xstrdup (finfo->file);
  863.         p->type = UPDATE;
  864.         p->delproc = update_delproc;
  865.         li = ((struct logfile_info *)
  866.           xmalloc (sizeof (struct logfile_info)));
  867.         li->type = status;
  868.         li->tag = xstrdup (vers->tag);
  869.         p->data = (char *) li;
  870.         (void) addnode (ulist, p);
  871.  
  872.         p = getnode ();
  873.         p->key = xstrdup (finfo->file);
  874.         p->type = UPDATE;
  875.         p->delproc = ci_delproc;
  876.         ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
  877.         ci->status = status;
  878.         if (vers->tag)
  879.         if (isdigit (*vers->tag))
  880.             ci->rev = xstrdup (vers->tag);
  881.         else
  882.             ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
  883.         else
  884.         ci->rev = (char *) NULL;
  885.         ci->tag = xstrdup (vers->tag);
  886.         ci->options = xstrdup(vers->options);
  887.         p->data = (char *) ci;
  888.         (void) addnode (cilist, p);
  889.         break;
  890.     case T_UNKNOWN:
  891.         error (0, 0, "nothing known about `%s'", finfo->fullname);
  892.         freevers_ts (&vers);
  893.         return (1);
  894.     case T_UPTODATE:
  895.         break;
  896.     default:
  897.         error (0, 0, "CVS internal error: unknown status %d", status);
  898.         break;
  899.     }
  900.  
  901.     freevers_ts (&vers);
  902.     return (0);
  903. }
  904.  
  905. /*
  906.  * Print warm fuzzies while examining the dirs
  907.  */
  908. /* ARGSUSED */
  909. static Dtype
  910. check_direntproc (callerdat, dir, repos, update_dir, entries)
  911.     void *callerdat;
  912.     char *dir;
  913.     char *repos;
  914.     char *update_dir;
  915.     List *entries;
  916. {
  917.     if (!quiet)
  918.     error (0, 0, "Examining %s", update_dir);
  919.  
  920.     return (R_PROCESS);
  921. }
  922.  
  923. /*
  924.  * Walklist proc to run pre-commit checks
  925.  */
  926. static int
  927. precommit_list_proc (p, closure)
  928.     Node *p;
  929.     void *closure;
  930. {
  931.     if (p->data == (char *) T_ADDED || p->data == (char *) T_MODIFIED ||
  932.     p->data == (char *) T_REMOVED)
  933.     {
  934.     run_arg (p->key);
  935.     }
  936.     return (0);
  937. }
  938.  
  939. /*
  940.  * Callback proc for pre-commit checking
  941.  */
  942. static List *ulist;
  943. static int
  944. precommit_proc (repository, filter)
  945.     char *repository;
  946.     char *filter;
  947. {
  948.     /* see if the filter is there, only if it's a full path */
  949.     if (isabsolute (filter))
  950.     {
  951.         char *s, *cp;
  952.     
  953.     s = xstrdup (filter);
  954.     for (cp = s; *cp; cp++)
  955.         if (isspace (*cp))
  956.         {
  957.         *cp = '\0';
  958.         break;
  959.         }
  960.     if (!isfile (s))
  961.     {
  962.         error (0, errno, "cannot find pre-commit filter `%s'", s);
  963.         free (s);
  964.         return (1);            /* so it fails! */
  965.     }
  966.     free (s);
  967.     }
  968.  
  969.     run_setup ("%s %s", filter, repository);
  970.     (void) walklist (ulist, precommit_list_proc, NULL);
  971.     return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
  972. }
  973.  
  974. /*
  975.  * Run the pre-commit checks for the dir
  976.  */
  977. /* ARGSUSED */
  978. static int
  979. check_filesdoneproc (callerdat, err, repos, update_dir, entries)
  980.     void *callerdat;
  981.     int err;
  982.     char *repos;
  983.     char *update_dir;
  984.     List *entries;
  985. {
  986.     int n;
  987.     Node *p;
  988.  
  989.     /* find the update list for this dir */
  990.     p = findnode (mulist, update_dir);
  991.     if (p != NULL)
  992.     ulist = ((struct master_lists *) p->data)->ulist;
  993.     else
  994.     ulist = (List *) NULL;
  995.  
  996.     /* skip the checks if there's nothing to do */
  997.     if (ulist == NULL || ulist->list->next == ulist->list)
  998.     return (err);
  999.  
  1000.     /* run any pre-commit checks */
  1001.     if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0)
  1002.     {
  1003.     error (0, 0, "Pre-commit check failed");
  1004.     err += n;
  1005.     }
  1006.  
  1007.     return (err);
  1008. }
  1009.  
  1010. /*
  1011.  * Do the work of committing a file
  1012.  */
  1013. static int maxrev;
  1014. static char sbranch[PATH_MAX];
  1015.  
  1016. /* ARGSUSED */
  1017. static int
  1018. commit_fileproc (callerdat, finfo)
  1019.     void *callerdat;
  1020.     struct file_info *finfo;
  1021. {
  1022.     Node *p;
  1023.     int err = 0;
  1024.     List *ulist, *cilist;
  1025.     struct commit_info *ci;
  1026.  
  1027.     if (finfo->update_dir[0] == '\0')
  1028.     p = findnode (mulist, ".");
  1029.     else
  1030.     p = findnode (mulist, finfo->update_dir);
  1031.  
  1032.     /*
  1033.      * if p is null, there were file type command line args which were
  1034.      * all up-to-date so nothing really needs to be done
  1035.      */
  1036.     if (p == NULL)
  1037.     return (0);
  1038.     ulist = ((struct master_lists *) p->data)->ulist;
  1039.     cilist = ((struct master_lists *) p->data)->cilist;
  1040.  
  1041.     /*
  1042.      * At this point, we should have the commit message unless we were called
  1043.      * with files as args from the command line.  In that latter case, we
  1044.      * need to get the commit message ourselves
  1045.      */
  1046.     if (use_editor && !got_message)
  1047.       {
  1048.     got_message = 1;
  1049.     do_editor (finfo->update_dir, &message, finfo->repository, ulist);
  1050.       }
  1051.  
  1052.     p = findnode (cilist, finfo->file);
  1053.     if (p == NULL)
  1054.     return (0);
  1055.  
  1056.     ci = (struct commit_info *) p->data;
  1057.     if (ci->status == T_MODIFIED)
  1058.     {
  1059.     if (finfo->rcs == NULL)
  1060.         error (1, 0, "internal error: no parsed RCS file");
  1061.     if (lock_RCS (finfo->file, finfo->rcs->path, ci->rev,
  1062.               finfo->repository) != 0)
  1063.     {
  1064.         unlockrcs (finfo->rcs->path);
  1065.         err = 1;
  1066.         goto out;
  1067.     }
  1068.     }
  1069.     else if (ci->status == T_ADDED)
  1070.     {
  1071.     if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
  1072.               &finfo->rcs) != 0)
  1073.     {
  1074.         fixaddfile (finfo->file, finfo->repository);
  1075.         err = 1;
  1076.         goto out;
  1077.     }
  1078.  
  1079.     /* adding files with a tag, now means adding them on a branch.
  1080.        Since the branch test was done in check_fileproc for
  1081.        modified files, we need to stub it in again here. */
  1082.  
  1083.     if (ci->tag)
  1084.     {
  1085.         if (finfo->rcs == NULL)
  1086.         error (1, 0, "internal error: no parsed RCS file");
  1087.         ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
  1088.         err = Checkin ('A', finfo, finfo->rcs->path, ci->rev,
  1089.                ci->tag, ci->options, message);
  1090.         if (err != 0)
  1091.         {
  1092.         unlockrcs (finfo->rcs->path);
  1093.         fixbranch (finfo->rcs->path, sbranch);
  1094.         }
  1095.  
  1096.         (void) time (&last_register_time);
  1097.  
  1098.         ci->status = T_UPTODATE;
  1099.     }
  1100.     }
  1101.  
  1102.     /*
  1103.      * Add the file for real
  1104.      */
  1105.     if (ci->status == T_ADDED)
  1106.     {
  1107.     char *xrev = (char *) NULL;
  1108.  
  1109.     if (ci->rev == NULL)
  1110.     {
  1111.         /* find the max major rev number in this directory */
  1112.         maxrev = 0;
  1113.         (void) walklist (finfo->entries, findmaxrev, NULL);
  1114.         if (maxrev == 0)
  1115.         maxrev = 1;
  1116.         xrev = xmalloc (20);
  1117.         (void) sprintf (xrev, "%d", maxrev);
  1118.     }
  1119.  
  1120.     /* XXX - an added file with symbolic -r should add tag as well */
  1121.     err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
  1122.     if (xrev)
  1123.         free (xrev);
  1124.     }
  1125.     else if (ci->status == T_MODIFIED)
  1126.     {
  1127.     err = Checkin ('M', finfo,
  1128.                finfo->rcs->path, ci->rev, ci->tag,
  1129.                ci->options, message);
  1130.  
  1131.     (void) time (&last_register_time);
  1132.  
  1133.     if (err != 0)
  1134.     {
  1135.         unlockrcs (finfo->rcs->path);
  1136.         fixbranch (finfo->rcs->path, sbranch);
  1137.     }
  1138.     }
  1139.     else if (ci->status == T_REMOVED)
  1140.     {
  1141.     err = remove_file (finfo, ci->tag, message);
  1142. #ifdef SERVER_SUPPORT
  1143.     if (server_active) {
  1144.         server_scratch_entry_only ();
  1145.         server_updated (finfo,
  1146.                 NULL,
  1147.  
  1148.                 /* Doesn't matter, it won't get checked.  */
  1149.                 SERVER_UPDATED,
  1150.  
  1151.                 (struct stat *) NULL,
  1152.                 (unsigned char *) NULL);
  1153.     }
  1154. #endif
  1155.     }
  1156.  
  1157.     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
  1158.        about T_ADDED or T_REMOVED.  */
  1159.     notify_do ('C', finfo->file, getcaller (), NULL, NULL, finfo->repository);
  1160.  
  1161. out:
  1162.     if (err != 0)
  1163.     {
  1164.     /* on failure, remove the file from ulist */
  1165.     p = findnode (ulist, finfo->file);
  1166.     if (p)
  1167.         delnode (p);
  1168.     }
  1169.  
  1170.     return (err);
  1171. }
  1172.  
  1173. /*
  1174.  * Log the commit and clean up the update list
  1175.  */
  1176. /* ARGSUSED */
  1177. static int
  1178. commit_filesdoneproc (callerdat, err, repository, update_dir, entries)
  1179.     void *callerdat;
  1180.     int err;
  1181.     char *repository;
  1182.     char *update_dir;
  1183.     List *entries;
  1184. {
  1185.     Node *p;
  1186.     List *ulist;
  1187.  
  1188.     p = findnode (mulist, update_dir);
  1189.     if (p == NULL)
  1190.     return (err);
  1191.  
  1192.     ulist = ((struct master_lists *) p->data)->ulist;
  1193.  
  1194.     got_message = 0;
  1195.  
  1196.  
  1197.     Update_Logfile (repository, message, (FILE *) 0, ulist);
  1198.  
  1199.     /* Build the administrative files if necessary.  */
  1200.     {
  1201.     char *p;
  1202.  
  1203.     if (strncmp (CVSroot_directory, repository,
  1204.              strlen (CVSroot_directory)) != 0)
  1205.         error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", repository, CVSroot_directory);
  1206.     p = repository + strlen (CVSroot_directory);
  1207.     if (*p == '/')
  1208.         ++p;
  1209.     if (strcmp ("CVSROOT", p) == 0)
  1210.     {
  1211.         /* "Database" might a little bit grandiose and/or vague,
  1212.            but "checked-out copies of administrative files, unless
  1213.            in the case of modules and you are using ndbm in which
  1214.            case modules.{pag,dir,db}" is verbose and excessively
  1215.            focused on how the database is implemented.  */
  1216.  
  1217.         cvs_output (program_name, 0);
  1218.         cvs_output (" ", 1);
  1219.         cvs_output (command_name, 0);
  1220.         cvs_output (": Rebuilding administrative file database\n", 0);
  1221.         mkmodules (repository);
  1222.     }
  1223.     }
  1224.  
  1225.     if (err == 0 && run_module_prog)
  1226.     {
  1227.     FILE *fp;
  1228.  
  1229.     if ((fp = CVS_FOPEN (CVSADM_CIPROG, "r")) != NULL)
  1230.     {
  1231.         char *line;
  1232.         int line_length;
  1233.         size_t line_chars_allocated;
  1234.         char *repository;
  1235.  
  1236.         line = NULL;
  1237.         line_chars_allocated = 0;
  1238.         line_length = getline (&line, &line_chars_allocated, fp);
  1239.         if (line_length > 0)
  1240.         {
  1241.         /* Remove any trailing newline.  */
  1242.         if (line[line_length - 1] == '\n')
  1243.             line[--line_length] = '\0';
  1244.         repository = Name_Repository ((char *) NULL, update_dir);
  1245.         run_setup ("%s %s", line, repository);
  1246.         (void) printf ("%s %s: Executing '", program_name,
  1247.                    command_name);
  1248.         run_print (stdout);
  1249.         (void) printf ("'\n");
  1250.         (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  1251.         free (repository);
  1252.         }
  1253.         else
  1254.         {
  1255.         if (ferror (fp))
  1256.             error (0, errno, "warning: error reading %s",
  1257.                CVSADM_CIPROG);
  1258.         }
  1259.         if (line != NULL)
  1260.         free (line);
  1261.         if (fclose (fp) < 0)
  1262.         error (0, errno, "warning: cannot close %s", CVSADM_CIPROG);
  1263.     }
  1264.     else
  1265.     {
  1266.         if (! existence_error (errno))
  1267.         error (0, errno, "warning: cannot open %s", CVSADM_CIPROG);
  1268.     }
  1269.     }
  1270.  
  1271.     return (err);
  1272. }
  1273.  
  1274. /*
  1275.  * Get the log message for a dir and print a warm fuzzy
  1276.  */
  1277. /* ARGSUSED */
  1278. static Dtype
  1279. commit_direntproc (callerdat, dir, repos, update_dir, entries)
  1280.     void *callerdat;
  1281.     char *dir;
  1282.     char *repos;
  1283.     char *update_dir;
  1284.     List *entries;
  1285. {
  1286.     Node *p;
  1287.     List *ulist;
  1288.     char *real_repos;
  1289.  
  1290.     /* find the update list for this dir */
  1291.     p = findnode (mulist, update_dir);
  1292.     if (p != NULL)
  1293.     ulist = ((struct master_lists *) p->data)->ulist;
  1294.     else
  1295.     ulist = (List *) NULL;
  1296.  
  1297.     /* skip the files as an optimization */
  1298.     if (ulist == NULL || ulist->list->next == ulist->list)
  1299.     return (R_SKIP_FILES);
  1300.  
  1301.     /* print the warm fuzzy */
  1302.     if (!quiet)
  1303.     error (0, 0, "Committing %s", update_dir);
  1304.  
  1305.     /* get commit message */
  1306.     if (use_editor)
  1307.     {
  1308.     got_message = 1;
  1309.     real_repos = Name_Repository (dir, update_dir);
  1310.     do_editor (update_dir, &message, real_repos, ulist);
  1311.     free (real_repos);
  1312.     }
  1313.     return (R_PROCESS);
  1314. }
  1315.  
  1316. /*
  1317.  * Process the post-commit proc if necessary
  1318.  */
  1319. /* ARGSUSED */
  1320. static int
  1321. commit_dirleaveproc (callerdat, dir, err, update_dir, entries)
  1322.     void *callerdat;
  1323.     char *dir;
  1324.     int err;
  1325.     char *update_dir;
  1326.     List *entries;
  1327. {
  1328.     /* update the per-directory tag info */
  1329.     if (err == 0 && write_dirtag != NULL)
  1330.     {
  1331.     WriteTag ((char *) NULL, write_dirtag, (char *) NULL);
  1332. #ifdef SERVER_SUPPORT
  1333.     if (server_active)
  1334.         server_set_sticky (update_dir, Name_Repository (dir, update_dir),
  1335.                    write_dirtag, (char *) NULL);
  1336. #endif
  1337.     }
  1338.  
  1339.     return (err);
  1340. }
  1341.  
  1342. /*
  1343.  * find the maximum major rev number in an entries file
  1344.  */
  1345. static int
  1346. findmaxrev (p, closure)
  1347.     Node *p;
  1348.     void *closure;
  1349. {
  1350.     char *cp;
  1351.     int thisrev;
  1352.     Entnode *entdata;
  1353.  
  1354.     entdata = (Entnode *) p->data;
  1355.     if (entdata->type != ENT_FILE)
  1356.     return (0);
  1357.     cp = strchr (entdata->version, '.');
  1358.     if (cp != NULL)
  1359.     *cp = '\0';
  1360.     thisrev = atoi (entdata->version);
  1361.     if (cp != NULL)
  1362.     *cp = '.';
  1363.     if (thisrev > maxrev)
  1364.     maxrev = thisrev;
  1365.     return (0);
  1366. }
  1367.  
  1368. /*
  1369.  * Actually remove a file by moving it to the attic
  1370.  * XXX - if removing a ,v file that is a relative symbolic link to
  1371.  * another ,v file, we probably should add a ".." component to the
  1372.  * link to keep it relative after we move it into the attic.
  1373.  */
  1374. static int
  1375. remove_file (finfo, tag, message)
  1376.     struct file_info *finfo;
  1377.     char *tag;
  1378.     char *message;
  1379. {
  1380.     mode_t omask;
  1381.     int retcode;
  1382.     char *tmp;
  1383.  
  1384.     int branch;
  1385.     int lockflag;
  1386.     char *corev;
  1387.     char *rev;
  1388.     char *prev_rev;
  1389.  
  1390.     corev = NULL;
  1391.     rev = NULL;
  1392.     prev_rev = NULL;
  1393.  
  1394.     retcode = 0;
  1395.  
  1396.     if (finfo->rcs == NULL)
  1397.     error (1, 0, "internal error: no parsed RCS file");
  1398.  
  1399.     branch = 0;
  1400.     if (tag && !(branch = RCS_isbranch (finfo->rcs, tag)))
  1401.     {
  1402.     /* a symbolic tag is specified; just remove the tag from the file */
  1403.     if ((retcode = RCS_deltag (finfo->rcs->path, tag, 1)) != 0) 
  1404.     {
  1405.         if (!quiet)
  1406.         error (0, retcode == -1 ? errno : 0,
  1407.                "failed to remove tag `%s' from `%s'", tag,
  1408.                finfo->fullname);
  1409.         return (1);
  1410.     }
  1411.     Scratch_Entry (finfo->entries, finfo->file);
  1412.     return (0);
  1413.     }
  1414.  
  1415.     /* we are removing the file from either the head or a branch */
  1416.     /* commit a new, dead revision. */
  1417.  
  1418.     /* Print message indicating that file is going to be removed. */
  1419.     (void) printf ("Removing %s;\n", finfo->fullname);
  1420.  
  1421.     rev = NULL;
  1422.     lockflag = 1;
  1423.     if (branch)
  1424.     {
  1425.     char *branchname;
  1426.  
  1427.     rev = RCS_whatbranch (finfo->rcs, tag);
  1428.     if (rev == NULL)
  1429.     {
  1430.         error (0, 0, "cannot find branch \"%s\".", tag);
  1431.         return (1);
  1432.     }
  1433.     
  1434.     branchname = RCS_getbranch (finfo->rcs, rev, 1);
  1435.     if (branchname == NULL)
  1436.     {
  1437.         /* no revision exists on this branch.  use the previous
  1438.            revision but do not lock. */
  1439.         corev = RCS_gettag (finfo->rcs, tag, 1, 0);
  1440.         prev_rev = xstrdup(rev);
  1441.         lockflag = 0;
  1442.     } else
  1443.     {
  1444.         corev = xstrdup (rev);
  1445.         prev_rev = xstrdup(branchname);
  1446.         free (branchname);
  1447.     }
  1448.  
  1449.     } else  /* Not a branch */
  1450.     {
  1451.         /* Get current head revision of file. */
  1452.     prev_rev = RCS_head (finfo->rcs);
  1453.     }
  1454.     
  1455.     /* if removing without a tag or a branch, then make sure the default
  1456.        branch is the trunk. */
  1457.     if (!tag && !branch)
  1458.     {
  1459.         if (RCS_setbranch (finfo->rcs->path, NULL) != 0) 
  1460.     {
  1461.         error (0, 0, "cannot change branch to default for %s",
  1462.            finfo->fullname);
  1463.         return (1);
  1464.     }
  1465.     }
  1466.  
  1467. #ifdef SERVER_SUPPORT
  1468.     if (server_active) {
  1469.     /* If this is the server, there will be a file sitting in the
  1470.        temp directory which is the kludgy way in which server.c
  1471.        tells time_stamp that the file is no longer around.  Remove
  1472.        it so we can create temp files with that name (ignore errors).  */
  1473.     unlink_file (finfo->file);
  1474.     }
  1475. #endif
  1476.  
  1477.     /* check something out.  Generally this is the head.  If we have a
  1478.        particular rev, then name it.  */
  1479.     retcode = RCS_fast_checkout (finfo->rcs, "", rev ? corev : NULL, NULL,
  1480.                  RUN_TTY, 0);
  1481.     if (retcode != 0)
  1482.     {
  1483.     if (!quiet)
  1484.         error (0, retcode == -1 ? errno : 0,
  1485.            "failed to check out `%s'", finfo->fullname);
  1486.     return (1);
  1487.     }
  1488.  
  1489.     /* Except when we are creating a branch, lock the revision so that
  1490.        we can check in the new revision.  */
  1491.     if (lockflag)
  1492.     RCS_lock (finfo->rcs->path, rev ? corev : NULL, 0);
  1493.  
  1494.     if (corev != NULL)
  1495.     free (corev);
  1496.  
  1497.     retcode = RCS_checkin (finfo->rcs->path, NULL, message, rev,
  1498.                RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
  1499.     if (retcode    != 0)
  1500.     {
  1501.     if (!quiet)
  1502.         error (0, retcode == -1 ? errno : 0,
  1503.            "failed to commit dead revision for `%s'", finfo->fullname);
  1504.     return (1);
  1505.     }
  1506.  
  1507.     if (rev != NULL)
  1508.     free (rev);
  1509.  
  1510.     if (!branch)
  1511.     {
  1512.     /* this was the head; really move it into the Attic */
  1513.     tmp = xmalloc(strlen(finfo->repository) + 
  1514.               sizeof('/') +
  1515.               sizeof(CVSATTIC) +
  1516.               sizeof('/') +
  1517.               strlen(finfo->file) +
  1518.               sizeof(RCSEXT) + 1);
  1519.     (void) sprintf (tmp, "%s/%s", finfo->repository, CVSATTIC);
  1520.     omask = umask (cvsumask);
  1521.     (void) CVS_MKDIR (tmp, 0777);
  1522.     (void) umask (omask);
  1523.     (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, finfo->file, RCSEXT);
  1524.     
  1525.     if (strcmp (finfo->rcs->path, tmp) != 0
  1526.         && CVS_RENAME (finfo->rcs->path, tmp) == -1
  1527.         && (isreadable (finfo->rcs->path) || !isreadable (tmp)))
  1528.     {
  1529.         free(tmp);
  1530.         return (1);
  1531.     }
  1532.     free(tmp);
  1533.     }
  1534.  
  1535.     /* Print message that file was removed. */
  1536.     (void) printf ("%s  <--  %s\n", finfo->rcs->path, finfo->file);
  1537.     (void) printf ("new revision: delete; ");
  1538.     (void) printf ("previous revision: %s\n", prev_rev);
  1539.     (void) printf ("done\n");
  1540.     free(prev_rev);
  1541.  
  1542.     Scratch_Entry (finfo->entries, finfo->file);
  1543.     return (0);
  1544. }
  1545.  
  1546. /*
  1547.  * Do the actual checkin for added files
  1548.  */
  1549. static int
  1550. finaladd (finfo, rev, tag, options)
  1551.     struct file_info *finfo;
  1552.     char *rev;
  1553.     char *tag;
  1554.     char *options;
  1555. {
  1556.     int ret;
  1557.     char tmp[PATH_MAX];
  1558.     char rcs[PATH_MAX];
  1559.  
  1560.     locate_rcs (finfo->file, finfo->repository, rcs);
  1561.     ret = Checkin ('A', finfo, rcs, rev, tag, options, message);
  1562.     if (ret == 0)
  1563.     {
  1564.     (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
  1565.     (void) unlink_file (tmp);
  1566.     }
  1567.     else
  1568.     fixaddfile (finfo->file, finfo->repository);
  1569.  
  1570.     (void) time (&last_register_time);
  1571.  
  1572.     return (ret);
  1573. }
  1574.  
  1575. /*
  1576.  * Unlock an rcs file
  1577.  */
  1578. static void
  1579. unlockrcs (rcs)
  1580.     char *rcs;
  1581. {
  1582.     int retcode;
  1583.  
  1584.     if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0)
  1585.     error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1586.            "could not unlock %s", rcs);
  1587. }
  1588.  
  1589. /*
  1590.  * remove a partially added file.  if we can parse it, leave it alone.
  1591.  */
  1592. static void
  1593. fixaddfile (file, repository)
  1594.     char *file;
  1595.     char *repository;
  1596. {
  1597.     RCSNode *rcsfile;
  1598.     char rcs[PATH_MAX];
  1599.     int save_really_quiet;
  1600.  
  1601.     locate_rcs (file, repository, rcs);
  1602.     save_really_quiet = really_quiet;
  1603.     really_quiet = 1;
  1604.     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
  1605.     (void) unlink_file (rcs);
  1606.     else
  1607.     freercsnode (&rcsfile);
  1608.     really_quiet = save_really_quiet;
  1609. }
  1610.  
  1611. /*
  1612.  * put the branch back on an rcs file
  1613.  */
  1614. static void
  1615. fixbranch (rcs, branch)
  1616.     char *rcs;
  1617.     char *branch;
  1618. {
  1619.     int retcode;
  1620.  
  1621.     if (branch != NULL && branch[0] != '\0')
  1622.     {
  1623.     if ((retcode = RCS_setbranch (rcs, branch)) != 0)
  1624.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1625.            "cannot restore branch to %s for %s", branch, rcs);
  1626.     }
  1627. }
  1628.  
  1629. /*
  1630.  * do the initial part of a file add for the named file.  if adding
  1631.  * with a tag, put the file in the Attic and point the symbolic tag
  1632.  * at the committed revision.
  1633.  */
  1634.  
  1635. static int
  1636. checkaddfile (file, repository, tag, options, rcsnode)
  1637.     char *file;
  1638.     char *repository;
  1639.     char *tag;
  1640.     char *options;
  1641.     RCSNode **rcsnode;
  1642. {
  1643.     char rcs[PATH_MAX];
  1644.     char fname[PATH_MAX];
  1645.     mode_t omask;
  1646.     int retcode = 0;
  1647.     int newfile = 0;
  1648.  
  1649.     if (tag)
  1650.     {
  1651.         (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1652.     if (! isreadable (rcs))
  1653.     {
  1654.         (void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
  1655.         omask = umask (cvsumask);
  1656.         if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST)
  1657.         error (1, errno, "cannot make directory `%s'", rcs);;
  1658.         (void) umask (omask);
  1659.         (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file,
  1660.                 RCSEXT);
  1661.     }
  1662.     }
  1663.     else
  1664.     locate_rcs (file, repository, rcs);
  1665.  
  1666.     if (isreadable(rcs))
  1667.     {
  1668.     /* file has existed in the past.  Prepare to resurrect. */
  1669.     char oldfile[PATH_MAX];
  1670.     char *rev;
  1671.     RCSNode *rcsfile;
  1672.  
  1673.     if (tag == NULL)
  1674.     {
  1675.         /* we are adding on the trunk, so move the file out of the
  1676.            Attic. */
  1677.         strcpy (oldfile, rcs);
  1678.         sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1679.         
  1680.         if (strcmp (oldfile, rcs) == 0
  1681.         || CVS_RENAME (oldfile, rcs) != 0
  1682.         || isreadable (oldfile)
  1683.         || !isreadable (rcs))
  1684.         {
  1685.         error (0, 0, "failed to move `%s' out of the attic.",
  1686.                file);
  1687.         return (1);
  1688.         }
  1689.     }
  1690.  
  1691.     if ((rcsfile = *rcsnode) == NULL)
  1692.     {
  1693.         error (0, 0, "could not find parsed rcsfile %s", file);
  1694.         return (1);
  1695.     }
  1696.  
  1697.     rev = RCS_getversion (rcsfile, tag, NULL, 1, 0);
  1698.     /* and lock it */
  1699.     if (lock_RCS (file, rcs, rev, repository)) {
  1700.         error (0, 0, "cannot lock `%s'.", rcs);
  1701.         free (rev);
  1702.         return (1);
  1703.     }
  1704.  
  1705.     free (rev);
  1706.     } else {
  1707.     /* this is the first time we have ever seen this file; create
  1708.        an rcs file.  */
  1709.     run_setup ("%s%s -x,v/ -i", Rcsbin, RCS);
  1710.  
  1711.     (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
  1712.     /* If the file does not exist, no big deal.  In particular, the
  1713.        server does not (yet at least) create CVSEXT_LOG files.  */
  1714.     if (isfile (fname))
  1715.         run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
  1716.  
  1717.     /* Set RCS keyword expansion options.  */
  1718.     if (options && options[0] == '-' && options[1] == 'k')
  1719.         run_arg (options);
  1720.     run_arg (rcs);
  1721.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
  1722.     {
  1723.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1724.            "could not create %s", rcs);
  1725.         return (1);
  1726.     }
  1727.     newfile = 1;
  1728.     }
  1729.  
  1730.     /* when adding a file for the first time, and using a tag, we need
  1731.        to create a dead revision on the trunk.  */
  1732.     if (tag && newfile)
  1733.     {
  1734.     char *tmp;
  1735.  
  1736.     /* move the new file out of the way. */
  1737.     (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
  1738.     rename_file (file, fname);
  1739.     copy_file (DEVNULL, file);
  1740.  
  1741.     tmp = xmalloc (strlen (file) + strlen (tag) + 80);
  1742.     /* commit a dead revision. */
  1743.     (void) sprintf (tmp, "file %s was initially added on branch %s.",
  1744.             file, tag);
  1745.     retcode = RCS_checkin (rcs, NULL, tmp, NULL,
  1746.                    RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
  1747.     free (tmp);
  1748.     if (retcode != 0)
  1749.     {
  1750.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1751.            "could not create initial dead revision %s", rcs);
  1752.         return (1);
  1753.     }
  1754.  
  1755.     /* put the new file back where it was */
  1756.     rename_file (fname, file);
  1757.     
  1758.     /* and lock it once again. */
  1759.     if (lock_RCS (file, rcs, NULL, repository)) {
  1760.         error (0, 0, "cannot lock `%s'.", rcs);
  1761.         return (1);
  1762.     }
  1763.     }
  1764.  
  1765.     if (tag != NULL)
  1766.     {
  1767.     /* when adding with a tag, we need to stub a branch, if it
  1768.        doesn't already exist.  */
  1769.     RCSNode *rcsfile;
  1770.  
  1771.     rcsfile = RCS_parse (file, repository);
  1772.     if (rcsfile == NULL)
  1773.     {
  1774.         error (0, 0, "could not read %s", rcs);
  1775.         return (1);
  1776.     }
  1777.     
  1778.     if (!RCS_nodeisbranch (rcsfile, tag)) {
  1779.         /* branch does not exist.  Stub it.  */
  1780.         char *head;
  1781.         char *magicrev;
  1782.         
  1783.         head = RCS_getversion (rcsfile, NULL, NULL, 0, 0);
  1784.         magicrev = RCS_magicrev (rcsfile, head);
  1785.         if ((retcode = RCS_settag(rcs, tag, magicrev)) != 0)
  1786.         {
  1787.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1788.                "could not stub branch %s for %s", tag, rcs);
  1789.         return (1);
  1790.         }
  1791.         
  1792.         freercsnode (&rcsfile);
  1793.         
  1794.         /* reparse the file, then add it to our list. */
  1795.         rcsfile = RCS_parse (file, repository);
  1796.         if (rcsfile == NULL)
  1797.         {
  1798.         error (0, 0, "could not reparse %s", rcs);
  1799.         return (1);
  1800.         }
  1801.  
  1802.         free (head);
  1803.         free (magicrev);
  1804.     }
  1805.     else
  1806.     {
  1807.         /* lock the branch. (stubbed branches need not be locked.)  */
  1808.         if (lock_RCS (file, rcs, NULL, repository)) {
  1809.         error (0, 0, "cannot lock `%s'.", rcs);
  1810.         return (1);
  1811.         }
  1812.     } 
  1813.  
  1814.     if (rcsnode)
  1815.         freercsnode(rcsnode);
  1816.     *rcsnode = rcsfile;
  1817.     }
  1818.  
  1819.     fileattr_newfile (file);
  1820.  
  1821.     fix_rcs_modes (rcs, file);
  1822.     return (0);
  1823. }
  1824.  
  1825. /*
  1826.  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
  1827.  * couldn't.  If the RCS file currently has a branch as the head, we must
  1828.  * move the head back to the trunk before locking the file, and be sure to
  1829.  * put the branch back as the head if there are any errors.
  1830.  */
  1831. static int
  1832. lock_RCS (user, rcs, rev, repository)
  1833.     char *user;
  1834.     char *rcs;
  1835.     char *rev;
  1836.     char *repository;
  1837. {
  1838.     RCSNode *rcsfile;
  1839.     char *branch = NULL;
  1840.     int err = 0;
  1841.  
  1842.     /*
  1843.      * For a specified, numeric revision of the form "1" or "1.1", (or when
  1844.      * no revision is specified ""), definitely move the branch to the trunk
  1845.      * before locking the RCS file.
  1846.      * 
  1847.      * The assumption is that if there is more than one revision on the trunk,
  1848.      * the head points to the trunk, not a branch... and as such, it's not
  1849.      * necessary to move the head in this case.
  1850.      */
  1851.     if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
  1852.     {
  1853.     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
  1854.     {
  1855.         /* invalid rcs file? */
  1856.         err = 1;
  1857.     }
  1858.     else
  1859.     {
  1860.         /* rcsfile is valid */
  1861.         branch = xstrdup (rcsfile->branch);
  1862.         freercsnode (&rcsfile);
  1863.         if (branch != NULL)
  1864.         {
  1865.         if (RCS_setbranch (rcs, NULL) != 0)
  1866.         {
  1867.             error (0, 0, "cannot change branch to default for %s",
  1868.                rcs);
  1869.             if (branch)
  1870.             free (branch);
  1871.             return (1);
  1872.         }
  1873.         }
  1874.         err = RCS_lock(rcs, NULL, 0);
  1875.     }
  1876.     }
  1877.     else
  1878.     {
  1879.     (void) RCS_lock(rcs, rev, 1);
  1880.     }
  1881.  
  1882.     if (err == 0)
  1883.     {
  1884.     if (branch)
  1885.     {
  1886.         (void) strcpy (sbranch, branch);
  1887.         free (branch);
  1888.     }
  1889.     else
  1890.         sbranch[0] = '\0';
  1891.     return (0);
  1892.     }
  1893.  
  1894.     /* try to restore the branch if we can on error */
  1895.     if (branch != NULL)
  1896.     fixbranch (rcs, branch);
  1897.  
  1898.     if (branch)
  1899.     free (branch);
  1900.     return (1);
  1901. }
  1902.  
  1903. /*
  1904.  * Called when "add"ing files to the RCS respository, as it is necessary to
  1905.  * preserve the file modes in the same fashion that RCS does.  This would be
  1906.  * automatic except that we are placing the RCS ,v file very far away from
  1907.  * the user file, and I can't seem to convince RCS of the location of the
  1908.  * user file.  So we munge it here, after the ,v file has been successfully
  1909.  * initialized with "rcs -i".
  1910.  */
  1911. static void
  1912. fix_rcs_modes (rcs, user)
  1913.     char *rcs;
  1914.     char *user;
  1915. {
  1916.     struct stat sb;
  1917.  
  1918.     if ( CVS_STAT (user, &sb) != -1)
  1919.     (void) chmod (rcs, (int) sb.st_mode & ~0222);
  1920. }
  1921.  
  1922. /*
  1923.  * free an UPDATE node's data
  1924.  */
  1925. void
  1926. update_delproc (p)
  1927.     Node *p;
  1928. {
  1929.     struct logfile_info *li;
  1930.  
  1931.     li = (struct logfile_info *) p->data;
  1932.     if (li->tag)
  1933.     free (li->tag);
  1934.     free (li);
  1935. }
  1936.  
  1937. /*
  1938.  * Free the commit_info structure in p.
  1939.  */
  1940. static void
  1941. ci_delproc (p)
  1942.     Node *p;
  1943. {
  1944.     struct commit_info *ci;
  1945.  
  1946.     ci = (struct commit_info *) p->data;
  1947.     if (ci->rev)
  1948.     free (ci->rev);
  1949.     if (ci->tag)
  1950.     free (ci->tag);
  1951.     if (ci->options)
  1952.     free (ci->options);
  1953.     free (ci);
  1954. }
  1955.  
  1956. /*
  1957.  * Free the commit_info structure in p.
  1958.  */
  1959. static void
  1960. masterlist_delproc (p)
  1961.     Node *p;
  1962. {
  1963.     struct master_lists *ml;
  1964.  
  1965.     ml = (struct master_lists *) p->data;
  1966.     dellist (&ml->ulist);
  1967.     dellist (&ml->cilist);
  1968.     free (ml);
  1969. }
  1970.  
  1971. /*
  1972.  * Find an RCS file in the repository.
  1973.  */
  1974. static void
  1975. locate_rcs (file, repository, rcs)
  1976.     char *file;
  1977.     char *repository;
  1978.     char *rcs;
  1979. {
  1980.     (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1981.     if (!isreadable (rcs))
  1982.     {
  1983.     (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
  1984.     if (!isreadable (rcs))
  1985.         (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1986.     }
  1987. }
  1988.