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 / add.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  15KB  |  577 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.  * Add
  9.  * 
  10.  * Adds a file or directory to the RCS source repository.  For a file,
  11.  * the entry is marked as "needing to be added" in the user's own CVS
  12.  * directory, and really added to the repository when it is committed.
  13.  * For a directory, it is added at the appropriate place in the source
  14.  * repository and a CVS directory is generated within the directory.
  15.  * 
  16.  * The -m option is currently the only supported option.  Some may wish to
  17.  * supply standard "rcs" options here, but I've found that this causes more
  18.  * trouble than anything else.
  19.  * 
  20.  * The user files or directories must already exist.  For a directory, it must
  21.  * not already have a CVS file in it.
  22.  * 
  23.  * An "add" on a file that has been "remove"d but not committed will cause the
  24.  * file to be resurrected.
  25.  */
  26.  
  27. #include "cvs.h"
  28. #include "savecwd.h"
  29.  
  30. static int add_directory PROTO((char *repository, List *, char *dir));
  31. static int build_entry PROTO((char *repository, char *user, char *options,
  32.                 char *message, List * entries, char *tag));
  33.  
  34. static const char *const add_usage[] =
  35. {
  36.     "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
  37.     "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
  38.     "\t-m\tUse \"message\" for the creation log.\n",
  39.     NULL
  40. };
  41.  
  42. int
  43. add (argc, argv)
  44.     int argc;
  45.     char **argv;
  46. {
  47.     char *message = NULL;
  48.     char *user;
  49.     int i;
  50.     char *repository;
  51.     int c;
  52.     int err = 0;
  53.     int added_files = 0;
  54.     char *options = NULL;
  55.     List *entries;
  56.     Vers_TS *vers;
  57.  
  58.     if (argc == 1 || argc == -1)
  59.     usage (add_usage);
  60.  
  61.     wrap_setup ();
  62.  
  63.     /* parse args */
  64.     optind = 1;
  65.     while ((c = getopt (argc, argv, "k:m:")) != -1)
  66.     {
  67.     switch (c)
  68.     {
  69.         case 'k':
  70.         if (options)
  71.             free (options);
  72.         options = RCS_check_kflag (optarg);
  73.         break;
  74.  
  75.         case 'm':
  76.         message = xstrdup (optarg);
  77.         break;
  78.         case '?':
  79.         default:
  80.         usage (add_usage);
  81.         break;
  82.     }
  83.     }
  84.     argc -= optind;
  85.     argv += optind;
  86.  
  87.     if (argc <= 0)
  88.     usage (add_usage);
  89.  
  90.     /* find the repository associated with our current dir */
  91.     repository = Name_Repository ((char *) NULL, (char *) NULL);
  92.  
  93. #ifdef CLIENT_SUPPORT
  94.     if (client_active)
  95.       {
  96.     int i;
  97.     start_server ();
  98.     ign_setup ();
  99.     if (options) send_arg(options);
  100.     option_with_arg ("-m", message);
  101.     for (i = 0; i < argc; ++i)
  102.       /* FIXME: Does this erroneously call Create_Admin in error
  103.          conditions which are only detected once the server gets its
  104.          hands on things?  */
  105.       if (isdir (argv[i]))
  106.         {
  107.           char *tag;
  108.           char *date;
  109.           char *rcsdir = xmalloc (strlen (repository)
  110.                       + strlen (argv[i]) + 10);
  111.  
  112.           /* before we do anything else, see if we have any
  113.          per-directory tags */
  114.           ParseTag (&tag, &date);
  115.  
  116.           sprintf (rcsdir, "%s/%s", repository, argv[i]);
  117.  
  118.           strip_trailing_slashes (argv[i]);
  119.  
  120.           Create_Admin (argv[i], argv[i], rcsdir, tag, date);
  121.  
  122.           if (tag)
  123.         free (tag);
  124.           if (date)
  125.         free (date);
  126.           free (rcsdir);
  127.  
  128.           if (strchr (argv[i], '/') == NULL)
  129.           Subdir_Register ((List *) NULL, (char *) NULL, argv[i]);
  130.           else
  131.           {
  132.           char *cp, *b;
  133.  
  134.           cp = xstrdup (argv[i]);
  135.           b = strrchr (cp, '/');
  136.           *b++ = '\0';
  137.           Subdir_Register ((List *) NULL, cp, b);
  138.           free (cp);
  139.           }
  140.         }
  141.     send_file_names (argc, argv, SEND_EXPAND_WILD);
  142.     send_files (argc, argv, 0, 0);
  143.     send_to_server ("add\012", 0);
  144.     return get_responses_and_close ();
  145.       }
  146. #endif
  147.  
  148.     entries = Entries_Open (0);
  149.  
  150.     /* walk the arg list adding files/dirs */
  151.     for (i = 0; i < argc; i++)
  152.     {
  153.     int begin_err = err;
  154.     int begin_added_files = added_files;
  155.     struct file_info finfo;
  156.  
  157.     user = argv[i];
  158.     strip_trailing_slashes (user);
  159.     if (strchr (user, '/') != NULL)
  160.     {
  161.         error (0, 0,
  162.          "cannot add files with '/' in their name; %s not added", user);
  163.         err++;
  164.         continue;
  165.     }
  166.  
  167.     memset (&finfo, 0, sizeof finfo);
  168.     finfo.file = user;
  169.     finfo.update_dir = "";
  170.     finfo.fullname = user;
  171.     finfo.repository = repository;
  172.     finfo.entries = entries;
  173.     finfo.rcs = NULL;
  174.  
  175.     /* We pass force_tag_match as 1.  If the directory has a
  176.            sticky branch tag, and there is already an RCS file which
  177.            does not have that tag, then the head revision is
  178.            meaningless to us.  */
  179.     vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
  180.     if (vers->vn_user == NULL)
  181.     {
  182.         /* No entry available, ts_rcs is invalid */
  183.         if (vers->vn_rcs == NULL)
  184.         {
  185.         /* There is no RCS file either */
  186.         if (vers->ts_user == NULL)
  187.         {
  188.             /* There is no user file either */
  189.             error (0, 0, "nothing known about %s", user);
  190.             err++;
  191.         }
  192.         else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS))
  193.         {
  194.             /*
  195.              * See if a directory exists in the repository with
  196.              * the same name.  If so, blow this request off.
  197.              */
  198.             char dname[PATH_MAX];
  199.             (void) sprintf (dname, "%s/%s", repository, user);
  200.             if (isdir (dname))
  201.             {
  202.             error (0, 0,
  203.                    "cannot add file `%s' since the directory",
  204.                    user);
  205.             error (0, 0, "`%s' already exists in the repository",
  206.                    dname);
  207.             error (1, 0, "illegal filename overlap");
  208.             }
  209.  
  210.             if (vers->options == NULL || *vers->options == '\0')
  211.             {
  212.             /* No options specified on command line (or in
  213.                rcs file if it existed, e.g. the file exists
  214.                on another branch).  Check for a value from
  215.                the wrapper stuff.  */
  216.             if (wrap_name_has (user, WRAP_RCSOPTION))
  217.             {
  218.                 if (vers->options)
  219.                 free (vers->options);
  220.                 vers->options = wrap_rcsoption (user, 1);
  221.             }
  222.             }
  223.  
  224.             /* There is a user file, so build the entry for it */
  225.             if (build_entry (repository, user, vers->options,
  226.                      message, entries, vers->tag) != 0)
  227.             err++;
  228.             else
  229.             {
  230.             added_files++;
  231.             if (!quiet)
  232.             {
  233.                 if (vers->tag)
  234.                 error (0, 0, "\
  235. scheduling %s `%s' for addition on branch `%s'",
  236.                        (wrap_name_has (user, WRAP_TOCVS)
  237.                     ? "wrapper"
  238.                     : "file"),
  239.                        user, vers->tag);
  240.                 else
  241.                 error (0, 0, "scheduling %s `%s' for addition",
  242.                    (wrap_name_has (user, WRAP_TOCVS)
  243.                     ? "wrapper"
  244.                     : "file"),
  245.                    user);
  246.             }
  247.             }
  248.         }
  249.         }
  250.         else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
  251.         {
  252.         if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS))
  253.         {
  254.             error (0, 0, "the directory `%s' cannot be added because a file of the", user);
  255.             error (1, 0, "same name already exists in the repository.");
  256.         }
  257.         else
  258.         {
  259.             if (vers->tag)
  260.             error (0, 0, "file `%s' will be added on branch `%s' from version %s",
  261.                    user, vers->tag, vers->vn_rcs);
  262.             else
  263.             /* I'm not sure that mentioning vers->vn_rcs makes
  264.                any sense here; I can't think of a way to word the
  265.                message which is not confusing.  */
  266.             error (0, 0, "\
  267. re-adding file %s (in place of dead revision %s)",
  268.                    user, vers->vn_rcs);
  269.             Register (entries, user, "0", vers->ts_user, NULL,
  270.                   vers->tag, NULL, NULL);
  271.             ++added_files;
  272.         }
  273.         }
  274.         else
  275.         {
  276.         /*
  277.          * There is an RCS file already, so somebody else must've
  278.          * added it
  279.          */
  280.         error (0, 0, "%s added independently by second party", user);
  281.         err++;
  282.         }
  283.     }
  284.     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
  285.     {
  286.  
  287.         /*
  288.          * An entry for a new-born file, ts_rcs is dummy, but that is
  289.          * inappropriate here
  290.          */
  291.         error (0, 0, "%s has already been entered", user);
  292.         err++;
  293.     }
  294.     else if (vers->vn_user[0] == '-')
  295.     {
  296.         /* An entry for a removed file, ts_rcs is invalid */
  297.         if (vers->ts_user == NULL)
  298.         {
  299.         /* There is no user file (as it should be) */
  300.         if (vers->vn_rcs == NULL)
  301.         {
  302.  
  303.             /*
  304.              * There is no RCS file, so somebody else must've removed
  305.              * it from under us
  306.              */
  307.             error (0, 0,
  308.                "cannot resurrect %s; RCS file removed by second party", user);
  309.             err++;
  310.         }
  311.         else
  312.         {
  313.  
  314.             /*
  315.              * There is an RCS file, so remove the "-" from the
  316.              * version number and restore the file
  317.              */
  318.             char *tmp = xmalloc (strlen (user) + 50);
  319.  
  320.             (void) strcpy (tmp, vers->vn_user + 1);
  321.             (void) strcpy (vers->vn_user, tmp);
  322.             (void) sprintf (tmp, "Resurrected %s", user);
  323.             Register (entries, user, vers->vn_user, tmp, vers->options,
  324.                   vers->tag, vers->date, vers->ts_conflict);
  325.             free (tmp);
  326.  
  327.             /* XXX - bugs here; this really resurrect the head */
  328.             /* Note that this depends on the Register above actually
  329.                having written Entries, or else it won't really
  330.                check the file out.  */
  331.             if (update (2, argv + i - 1) == 0)
  332.             {
  333.             error (0, 0, "%s, version %s, resurrected", user,
  334.                    vers->vn_user);
  335.             }
  336.             else
  337.             {
  338.             error (0, 0, "could not resurrect %s", user);
  339.             err++;
  340.             }
  341.         }
  342.         }
  343.         else
  344.         {
  345.         /* The user file shouldn't be there */
  346.         error (0, 0, "%s should be removed and is still there (or is back again)", user);
  347.         err++;
  348.         }
  349.     }
  350.     else
  351.     {
  352.         /* A normal entry, ts_rcs is valid, so it must already be there */
  353.         error (0, 0, "%s already exists, with version number %s", user,
  354.            vers->vn_user);
  355.         err++;
  356.     }
  357.     freevers_ts (&vers);
  358.  
  359.     /* passed all the checks.  Go ahead and add it if its a directory */
  360.     if (begin_err == err
  361.         && isdir (user)
  362.         && !wrap_name_has (user, WRAP_TOCVS))
  363.     {
  364.         err += add_directory (repository, entries, user);
  365.         continue;
  366.     }
  367. #ifdef SERVER_SUPPORT
  368.     if (server_active && begin_added_files != added_files)
  369.         server_checked_in (user, ".", repository);
  370. #endif
  371.     }
  372.     if (added_files)
  373.     error (0, 0, "use 'cvs commit' to add %s permanently",
  374.            (added_files == 1) ? "this file" : "these files");
  375.  
  376.     Entries_Close (entries);
  377.  
  378.     if (message)
  379.     free (message);
  380.  
  381.     return (err);
  382. }
  383.  
  384. /*
  385.  * The specified user file is really a directory.  So, let's make sure that
  386.  * it is created in the RCS source repository, and that the user's directory
  387.  * is updated to include a CVS directory.
  388.  * 
  389.  * Returns 1 on failure, 0 on success.
  390.  */
  391. static int
  392. add_directory (repository, entries, dir)
  393.     char *repository;
  394.     List *entries;
  395.     char *dir;
  396. {
  397.     char rcsdir[PATH_MAX];
  398.     struct saved_cwd cwd;
  399.     char message[PATH_MAX + 100];
  400.     char *tag, *date;
  401.  
  402.     if (strchr (dir, '/') != NULL)
  403.     {
  404.     error (0, 0,
  405.            "directory %s not added; must be a direct sub-directory", dir);
  406.     return (1);
  407.     }
  408.     if (strcmp (dir, CVSADM) == 0)
  409.     {
  410.     error (0, 0, "cannot add a `%s' directory", CVSADM);
  411.     return (1);
  412.     }
  413.  
  414.     /* before we do anything else, see if we have any per-directory tags */
  415.     ParseTag (&tag, &date);
  416.  
  417.     /* now, remember where we were, so we can get back */
  418.     if (save_cwd (&cwd))
  419.     return (1);
  420.     if ( CVS_CHDIR (dir) < 0)
  421.     {
  422.     error (0, errno, "cannot chdir to %s", dir);
  423.     return (1);
  424.     }
  425. #ifdef SERVER_SUPPORT
  426.     if (!server_active && isfile (CVSADM))
  427. #else
  428.     if (isfile (CVSADM))
  429. #endif
  430.     {
  431.     error (0, 0, "%s/%s already exists", dir, CVSADM);
  432.     goto out;
  433.     }
  434.  
  435.     (void) sprintf (rcsdir, "%s/%s", repository, dir);
  436.     if (isfile (rcsdir) && !isdir (rcsdir))
  437.     {
  438.     error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
  439.     goto out;
  440.     }
  441.  
  442.     /* setup the log message */
  443.     (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
  444.     if (tag)
  445.     {
  446.     (void) strcat (message, "--> Using per-directory sticky tag `");
  447.     (void) strcat (message, tag);
  448.     (void) strcat (message, "'\n");
  449.     }
  450.     if (date)
  451.     {
  452.     (void) strcat (message, "--> Using per-directory sticky date `");
  453.     (void) strcat (message, date);
  454.     (void) strcat (message, "'\n");
  455.     }
  456.  
  457.     if (!isdir (rcsdir))
  458.     {
  459.     mode_t omask;
  460.     Node *p;
  461.     List *ulist;
  462.     struct logfile_info *li;
  463.  
  464. #if 0
  465.     char line[MAXLINELEN];
  466.  
  467.     (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
  468.                rcsdir);
  469.     (void) fflush (stdout);
  470.     clearerr (stdin);
  471.     if (fgets (line, sizeof (line), stdin) == NULL ||
  472.         (line[0] != 'y' && line[0] != 'Y'))
  473.     {
  474.         error (0, 0, "directory %s not added", rcsdir);
  475.         goto out;
  476.     }
  477. #endif
  478.  
  479.     if (!noexec)
  480.     {
  481.         omask = umask (cvsumask);
  482.         if (CVS_MKDIR (rcsdir, 0777) < 0)
  483.         {
  484.         error (0, errno, "cannot mkdir %s", rcsdir);
  485.         (void) umask (omask);
  486.         goto out;
  487.         }
  488.         (void) umask (omask);
  489.     }
  490.  
  491.     /*
  492.      * Set up an update list with a single title node for Update_Logfile
  493.      */
  494.     ulist = getlist ();
  495.     p = getnode ();
  496.     p->type = UPDATE;
  497.     p->delproc = update_delproc;
  498.     p->key = xstrdup ("- New directory");
  499.     li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
  500.     li->type = T_TITLE;
  501.     li->tag = xstrdup (tag);
  502.     p->data = (char *) li;
  503.     (void) addnode (ulist, p);
  504.     Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
  505.     dellist (&ulist);
  506.     }
  507.  
  508. #ifdef SERVER_SUPPORT
  509.     if (!server_active)
  510.     Create_Admin (".", dir, rcsdir, tag, date);
  511. #else
  512.     Create_Admin (".", dir, rcsdir, tag, date);
  513. #endif
  514.     if (tag)
  515.     free (tag);
  516.     if (date)
  517.     free (date);
  518.  
  519.     if (restore_cwd (&cwd, NULL))
  520.     exit (EXIT_FAILURE);
  521.     free_cwd (&cwd);
  522.  
  523.     Subdir_Register (entries, (char *) NULL, dir);
  524.  
  525.     (void) printf ("%s", message);
  526.  
  527.     return (0);
  528.  
  529. out:
  530.     if (restore_cwd (&cwd, NULL))
  531.       exit (EXIT_FAILURE);
  532.     free_cwd (&cwd);
  533.     return (0);
  534. }
  535.  
  536. /*
  537.  * Builds an entry for a new file and sets up "CVS/file",[pt] by
  538.  * interrogating the user.  Returns non-zero on error.
  539.  */
  540. static int
  541. build_entry (repository, user, options, message, entries, tag)
  542.     char *repository;
  543.     char *user;
  544.     char *options;
  545.     char *message;
  546.     List *entries;
  547.     char *tag;
  548. {
  549.     char fname[PATH_MAX];
  550.     char line[MAXLINELEN];
  551.     FILE *fp;
  552.  
  553.     if (noexec)
  554.     return (0);
  555.  
  556.     /*
  557.      * The requested log is read directly from the user and stored in the
  558.      * file user,t.  If the "message" argument is set, use it as the
  559.      * initial creation log (which typically describes the file).
  560.      */
  561.     (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
  562.     fp = open_file (fname, "w+");
  563.     if (message && fputs (message, fp) == EOF)
  564.         error (1, errno, "cannot write to %s", fname);
  565.     if (fclose(fp) == EOF)
  566.         error(1, errno, "cannot close %s", fname);
  567.  
  568.     /*
  569.      * Create the entry now, since this allows the user to interrupt us above
  570.      * without needing to clean anything up (well, we could clean up the
  571.      * ,t file, but who cares).
  572.      */
  573.     (void) sprintf (line, "Initial %s", user);
  574.     Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
  575.     return (0);
  576. }
  577.