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 + -
显示快捷键?