📄 main.c
字号:
struct node_baton_t *nb; char *node_path, *copyfrom_path; apr_hash_index_t *hi; const void *key; void *val; const char *tcl; *node_baton = apr_palloc(pool, sizeof(struct node_baton_t)); nb = *node_baton; nb->rb = rev_baton; pb = nb->rb->pb; node_path = apr_hash_get(headers, SVN_REPOS_DUMPFILE_NODE_PATH, APR_HASH_KEY_STRING); 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) SVN_ERR(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))) { svn_revnum_t cf_orig_rev; struct revmap_t *cf_renum_val; cf_orig_rev = SVN_STR_TO_REV(val); cf_renum_val = apr_hash_get(pb->renumber_history, &cf_orig_rev, sizeof(svn_revnum_t)); if (! (cf_renum_val && SVN_IS_VALID_REVNUM(cf_renum_val->rev))) return svn_error_createf (SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("No valid copyfrom revision in filtered stream")); SVN_ERR(svn_stream_printf (nb->rb->pb->out_stream, pool, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n", cf_renum_val->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; apr_size_t 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_fns2_t filtering_vtable = { new_revision_record, uuid_record, new_node_record, set_revision_property, set_node_property, NULL, remove_node_props, set_fulltext, NULL, 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. * * The entire list must be terminated with an entry of nulls. */static const apr_getopt_option_t options_table[] = { {"help", 'h', 0, N_("show help on a subcommand")}, {NULL, '?', 0, N_("show help on a subcommand")}, {"version", svndumpfilter__version, 0, N_("show program version information") }, {"quiet", svndumpfilter__quiet, 0, N_("Do not display filtering statistics.") }, {"drop-empty-revs", svndumpfilter__drop_empty_revs, 0, N_("Remove revisions emptied by filtering.")}, {"renumber-revs", svndumpfilter__renumber_revs, 0, N_("Renumber revisions left after filtering.") }, {"preserve-revprops", svndumpfilter__preserve_revprops, 0, N_("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}, N_("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}, N_("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"}, N_("Describe the usage of this program or its subcommands.\n" "usage: svndumpfilter help [SUBCOMMAND...]\n"), {0} }, { 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); baton->last_live_revision = SVN_INVALID_REVNUM; /* 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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -