main.c

来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,281 行 · 第 1/3 页

C
1,281
字号
  copyfrom_path = apr_hash_get (headers,
                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH,
                                APR_HASH_KEY_STRING);

  /* Ensure that paths start with a leading '/'. */
  node_path = svn_path_join ("/", node_path, pool);
  if (copyfrom_path)
    copyfrom_path = svn_path_join ("/", copyfrom_path, pool);

  /* Shame, shame, shame ... this is NXOR. */
  nb->do_skip = (ary_prefix_match (pb->prefixes, node_path)
                 ? pb->do_exclude : (! pb->do_exclude));

  /* If we're skipping the node, take note of path, discarding the
     rest.  */
  if (nb->do_skip)
    {
      apr_hash_set (pb->dropped_nodes, 
                    apr_pstrdup (apr_hash_pool_get (pb->dropped_nodes), 
                                 node_path),
                    APR_HASH_KEY_STRING, (void *)1);
      nb->rb->had_dropped_nodes = TRUE;
    }
  else
    {
      tcl = apr_hash_get (headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH,
                          APR_HASH_KEY_STRING);

      /* Test if this node was copied from dropped source. */
      if (copyfrom_path &&
          (ary_prefix_match (pb->prefixes, copyfrom_path) 
           ? pb->do_exclude : (! pb->do_exclude)))
        {
          /* This node was copied from dropped source.
             We have a problem, since we did not want to drop this node too.

             However, there is one special case we'll handle.  If the node is
             a file, and this was a copy-and-modify operation, then the
             dumpfile should contain the new contents of the file.  In this
             scenario, we'll just do an add without history using the new
             contents.  */
          const char *kind;
          kind = apr_hash_get (headers, SVN_REPOS_DUMPFILE_NODE_KIND,
                               APR_HASH_KEY_STRING);

          /* If there is a Text-content-length header, and the kind is
             "file", we just fallback to an add without history. */
          if (tcl && (strcmp (kind, "file") == 0))
            {
              apr_hash_set (headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH,
                            APR_HASH_KEY_STRING, NULL);
              apr_hash_set (headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
                            APR_HASH_KEY_STRING, NULL);
              copyfrom_path = NULL;
            }
          /* Else, this is either a directory or a file whose contents we
             don't have readily available.  */
          else
            {
              return svn_error_createf 
                (SVN_ERR_INCOMPLETE_DATA, 0,
                 _("Invalid copy source path '%s'"), copyfrom_path);
            }
        }

      nb->has_props = FALSE;
      nb->has_text = FALSE;
      nb->writing_begun = FALSE;
      nb->tcl = tcl ? svn__atoui64 (tcl) : 0;
      nb->header = svn_stringbuf_create ("", pool);
      nb->props = svn_stringbuf_create ("", pool);

      /* Now we know for sure that we have a node that will not be
         skipped, flush the revision if it has not already been done. */
      nb->rb->has_nodes = TRUE;
      if (! nb->rb->writing_begun)
        output_revision (nb->rb);

      for (hi = apr_hash_first (pool, headers); hi; hi = apr_hash_next (hi))
        {
          apr_hash_this (hi, (const void **) &key, NULL, &val);
          if ((!strcmp (key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH))
              || (!strcmp (key, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH))
              || (!strcmp (key, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH)))
            continue;

          /* Rewrite Node-Copyfrom-Rev if we are renumbering revisions.
             The number points to some revision in the past. We keep track
             of revision renumbering in an apr_hash, which maps original
             revisions to new ones. Dropped revision are mapped to -1.
             This should never happen here.
          */
          if (pb->do_renumber_revs
              && (!strcmp (key, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
            {
              cf_orig_rev = SVN_STR_TO_REV(val);
              cf_renum_rev = apr_hash_get (pb->renumber_history,
                                           &cf_orig_rev,
                                           sizeof (svn_revnum_t));
              if ((cf_renum_rev == NULL) || (*cf_renum_rev == -1))
                {
                  /* bail out with an error */
                  return svn_error_createf
                    (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                     "Node with dropped parent sneaked in");
                }
              SVN_ERR (svn_stream_printf
                       (nb->rb->pb->out_stream, pool,
                        SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n",
                        *cf_renum_rev));
              continue;
            }

          /* passthru: put header straight to output */

          SVN_ERR (svn_stream_printf (nb->rb->pb->out_stream,
                                      pool, "%s: %s\n",
                                      (const char *)key,
                                      (const char *)val));
        }
    }

  return SVN_NO_ERROR;
}


/* Output node header and props to dumpstream
   This will be called by set_fulltext() after setting nb->has_text to TRUE,
   if the node has any text, or by close_node() otherwise. This must only
   be called if nb->writing_begun is FALSE. */
static svn_error_t *
output_node (struct node_baton_t *nb)
{
  int bytes_used;
  char buf[SVN_KEYLINE_MAXLEN];

  nb->writing_begun = TRUE;

  /* when there are no props nb->props->len would be zero and won't mess up
     Content-Length. */
  if (nb->has_props)
    svn_stringbuf_appendcstr (nb->props, "PROPS-END\n");

  /* 1. recalculate & check text-md5 if present. Passed through right now. */

  /* 2. recalculate and add content-lengths */

  if (nb->has_props)
    {
      svn_stringbuf_appendcstr (nb->header,
                                SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH);
      sprintf (buf, ": %" APR_SIZE_T_FMT "%n", nb->props->len, &bytes_used);
      svn_stringbuf_appendbytes (nb->header, buf, bytes_used);
      svn_stringbuf_appendbytes (nb->header, "\n", 1);
    }
  if (nb->has_text)
    {
      svn_stringbuf_appendcstr (nb->header,
                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH);
      sprintf (buf, ": %" SVN_FILESIZE_T_FMT "%n", nb->tcl, &bytes_used);
      svn_stringbuf_appendbytes (nb->header, buf, bytes_used);
      svn_stringbuf_appendbytes (nb->header, "\n", 1);
    }
  svn_stringbuf_appendcstr (nb->header, SVN_REPOS_DUMPFILE_CONTENT_LENGTH);
  sprintf (buf, ": %" SVN_FILESIZE_T_FMT "%n",
           (svn_filesize_t) (nb->props->len + nb->tcl), &bytes_used);
  svn_stringbuf_appendbytes (nb->header, buf, bytes_used);
  svn_stringbuf_appendbytes (nb->header, "\n", 1);

  /* put an end to headers */
  svn_stringbuf_appendbytes (nb->header, "\n", 1);

  /* 3. output all the stuff */

  SVN_ERR (svn_stream_write (nb->rb->pb->out_stream,
                             nb->header->data , &(nb->header->len)));
  SVN_ERR (svn_stream_write (nb->rb->pb->out_stream,
                             nb->props->data , &(nb->props->len)));

  return SVN_NO_ERROR;
}


static svn_error_t *
set_revision_property (void *revision_baton,
                       const char *name,
                       const svn_string_t *value)
{
  struct revision_baton_t *rb = revision_baton;
  apr_pool_t *hash_pool = apr_hash_pool_get (rb->props);

  rb->has_props = TRUE;
  apr_hash_set (rb->props, apr_pstrdup (hash_pool, name),
                APR_HASH_KEY_STRING, svn_string_dup (value, hash_pool));
  return SVN_NO_ERROR;
}


static svn_error_t *
set_node_property (void *node_baton,
                   const char *name,
                   const svn_string_t *value)
{
  struct node_baton_t *nb = node_baton;

  if (nb->do_skip)
    return SVN_NO_ERROR;

  if (!nb->has_props)
    return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                             _("Delta property block detected - "
                               "not supported by svndumpfilter"));

  write_prop_to_stringbuf (&(nb->props), name, value);

  return SVN_NO_ERROR;
}


static svn_error_t *
remove_node_props (void *node_baton)
{
  struct node_baton_t *nb = node_baton;

  /* In this case, not actually indicating that the node *has* props,
     rather that we know about all the props that it has, since it now
     has none. */
  nb->has_props = TRUE;

  return SVN_NO_ERROR;
}


static svn_error_t *
set_fulltext (svn_stream_t **stream, void *node_baton)
{
  struct node_baton_t *nb = node_baton;

  if (!nb->do_skip)
    {
      nb->has_text = TRUE;
      if (! nb->writing_begun)
        SVN_ERR(output_node (nb));
      *stream = nb->rb->pb->out_stream;
    }

  return SVN_NO_ERROR;
}


/* Finalize node */
static svn_error_t *
close_node (void *node_baton)
{
  struct node_baton_t *nb = node_baton;
  int len = 2;

  /* Get out of here if we can. */
  if (nb->do_skip)
    return SVN_NO_ERROR;

  /* If the node was not flushed already to output its text, do it now. */
  if (! nb->writing_begun)
    SVN_ERR (output_node (nb));
  
  /* put an end to node. */
  SVN_ERR (svn_stream_write (nb->rb->pb->out_stream, "\n\n", &len));

  return SVN_NO_ERROR;
}


/* Finalize revision */
static svn_error_t *
close_revision (void *revision_baton)
{
  struct revision_baton_t *rb = revision_baton;

  /* If no node has yet flushed the revision, do it now. */
  if (! rb->writing_begun)
    return output_revision (rb);
  else
    return SVN_NO_ERROR;
}


/* Filtering vtable */
svn_repos_parser_fns_t filtering_vtable =
  {
    new_revision_record,
    uuid_record,
    new_node_record,
    set_revision_property,
    set_node_property,
    remove_node_props,
    set_fulltext,
    close_node,
    close_revision
  };



/** Subcommands. **/

static svn_opt_subcommand_t
  subcommand_help,
  subcommand_exclude,
  subcommand_include;

enum
  {
    svndumpfilter__drop_empty_revs = SVN_OPT_FIRST_LONGOPT_ID,
    svndumpfilter__renumber_revs,
    svndumpfilter__preserve_revprops,
    svndumpfilter__quiet,
    svndumpfilter__version
  };

/* Option codes and descriptions.
 *
 * This must not have more than SVN_OPT_MAX_OPTIONS entries; if you
 * need more, increase that limit first.
 *
 * The entire list must be terminated with an entry of nulls.
 */
static const apr_getopt_option_t options_table[] =
  {
    {"help",          'h', 0,
     "show help on a subcommand"},

    {NULL,            '?', 0,
     "show help on a subcommand"},

    {"version",            svndumpfilter__version, 0,
     "show version information" },
    {"quiet",              svndumpfilter__quiet, 0,
     "Do not display filtering statistics." },
    {"drop-empty-revs",    svndumpfilter__drop_empty_revs, 0,
     "Remove revisions emptied by filtering."},
    {"renumber-revs",      svndumpfilter__renumber_revs, 0,
     "Renumber revisions left after filtering." },
    {"preserve-revprops",  svndumpfilter__preserve_revprops, 0,
     "Don't filter revision properties." },
    {NULL}
  };


/* Array of available subcommands.
 * The entire list must be terminated with an entry of nulls.
 */
static const svn_opt_subcommand_desc_t cmd_table[] =
  {
    {"exclude", subcommand_exclude, {0},
     "Filter out nodes with given prefixes from dumpstream.\n"
     "usage: svndumpfilter exclude PATH_PREFIX...\n",
     {svndumpfilter__drop_empty_revs, svndumpfilter__renumber_revs,
      svndumpfilter__preserve_revprops, svndumpfilter__quiet} },

    {"include", subcommand_include, {0},
     "Filter out nodes without given prefixes from dumpstream.\n"
     "usage: svndumpfilter include PATH_PREFIX...\n",
     {svndumpfilter__drop_empty_revs, svndumpfilter__renumber_revs,
      svndumpfilter__preserve_revprops, svndumpfilter__quiet} },

    {"help", subcommand_help, {"?", "h"},
     "Describe the usage of this program or its subcommands.\n"
     "usage: svndumpfilter help [SUBCOMMAND...]\n",
     {svndumpfilter__version} },

    { NULL, NULL, {0}, NULL, {0} }
  };


/* Baton for passing option/argument state to a subcommand function. */
struct svndumpfilter_opt_state
{
  svn_opt_revision_t start_revision;     /* -r X[:Y] is         */
  svn_opt_revision_t end_revision;       /* not implemented.    */
  svn_boolean_t quiet;                   /* --quiet             */
  svn_boolean_t version;                 /* --version           */
  svn_boolean_t drop_empty_revs;         /* --drop-empty-revs   */
  svn_boolean_t help;                    /* --help or -?        */
  svn_boolean_t renumber_revs;           /* --renumber-revs     */
  svn_boolean_t preserve_revprops;       /* --preserve-revprops */
  apr_array_header_t *prefixes;          /* mainargs.           */
};


static svn_error_t *
parse_baton_initialize (struct parse_baton_t **pb,
                        struct svndumpfilter_opt_state *opt_state,
                        svn_boolean_t do_exclude,
                        apr_pool_t *pool)
{
  struct parse_baton_t *baton = apr_palloc (pool, sizeof (*baton));

  /* Read the stream from STDIN.  Users can redirect a file. */
  SVN_ERR (create_stdio_stream (&(baton->in_stream),
                                apr_file_open_stdin, pool));

  /* Have the parser dump results to STDOUT. Users can redirect a file. */
  SVN_ERR (create_stdio_stream (&(baton->out_stream),
                                apr_file_open_stdout, pool));

  baton->do_exclude = do_exclude;
  baton->do_renumber_revs = opt_state->renumber_revs;
  baton->drop_empty_revs = opt_state->drop_empty_revs;
  baton->preserve_revprops = opt_state->preserve_revprops;
  baton->quiet = opt_state->quiet;
  baton->prefixes = opt_state->prefixes;
  baton->rev_drop_count = 0; /* used to shift revnums while filtering */
  baton->dropped_nodes = apr_hash_make (pool);
  baton->renumber_history = apr_hash_make (pool);

  /* This is non-ideal: We should pass through the version of the
   * input dumpstream.  However, our API currently doesn't allow that.
   * Hardcoding version 2 is acceptable because:
   *   - We currently do not accept version 3 or greater.
   *   - Dumpstream version 1 is so ancient as to be ignorable
   *     (0.17.x and earlier)
   */
  SVN_ERR (svn_stream_printf (baton->out_stream, pool,
                              SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
                              2));

  *pb = baton;
  return SVN_NO_ERROR;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?