📄 main.c
字号:
/* * main.c: Subversion dump stream filtering tool. * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <stdlib.h>#include <apr_file_io.h>#include "svn_private_config.h"#include "svn_cmdline.h"#include "svn_error.h"#include "svn_opt.h"#include "svn_utf.h"#include "svn_path.h"#include "svn_hash.h"#include "svn_repos.h"#include "svn_fs.h"#include "svn_pools.h"#include "svn_sorts.h"#include "svn_props.h"/*** Code. ***//* Helper to open stdio streams *//* NOTE: we used to call svn_stream_from_stdio(), which wraps a stream around a standard stdio.h FILE pointer. The problem is that these pointers operate through C Run Time (CRT) on Win32, which does all sorts of translation on them: LF's become CRLF's, and ctrl-Z's embedded in Word documents are interpreted as premature EOF's. So instead, we use apr_file_open_std*, which bypass the CRT and directly wrap the OS's file-handles, which don't know or care about translation. Thus dump/load works correctly on Win32.*/static svn_error_t *create_stdio_stream(svn_stream_t **stream, APR_DECLARE(apr_status_t) open_fn(apr_file_t **, apr_pool_t *), apr_pool_t *pool){ apr_file_t *stdio_file; apr_status_t apr_err = open_fn(&stdio_file, pool); if (apr_err) return svn_error_wrap_apr(apr_err, _("Can't open stdio file")); *stream = svn_stream_from_aprfile(stdio_file, pool); return SVN_NO_ERROR;}/* Writes a property in dumpfile format to given stringbuf. */static voidwrite_prop_to_stringbuf(svn_stringbuf_t **strbuf, const char *name, const svn_string_t *value){ int bytes_used, namelen; char buf[SVN_KEYLINE_MAXLEN]; /* Output name length, then name. */ namelen = strlen(name); svn_stringbuf_appendbytes(*strbuf, "K ", 2); sprintf(buf, "%d%n", namelen, &bytes_used); svn_stringbuf_appendbytes(*strbuf, buf, bytes_used); svn_stringbuf_appendbytes(*strbuf, "\n", 1); svn_stringbuf_appendbytes(*strbuf, name, namelen); svn_stringbuf_appendbytes(*strbuf, "\n", 1); /* Output value length, then value. */ svn_stringbuf_appendbytes(*strbuf, "V ", 2); sprintf(buf, "%" APR_SIZE_T_FMT "%n", value->len, &bytes_used); svn_stringbuf_appendbytes(*strbuf, buf, bytes_used); svn_stringbuf_appendbytes(*strbuf, "\n", 1); svn_stringbuf_appendbytes(*strbuf, value->data, value->len); svn_stringbuf_appendbytes(*strbuf, "\n", 1);}/* Prefix matching function to compare node-path with set of prefixes. */static svn_boolean_tary_prefix_match(apr_array_header_t *pfxlist, const char *path){ int i, pfx_len, path_len = strlen(path); const char *pfx; for (i = 0; i < pfxlist->nelts; i++) { pfx = APR_ARRAY_IDX(pfxlist, i, const char *); pfx_len = strlen(pfx); if (path_len < pfx_len) continue; if (strncmp(path, pfx, pfx_len) == 0) return TRUE; } return FALSE;}/* Note: the input stream parser calls us with events. Output of the filtered dump occurs for the most part streamily with the event callbacks, to avoid caching large quantities of data in memory. The exceptions this are: - All revision data (headers and props) must be cached until a non-skipped node within the revision is found, or the revision is closed. - Node headers and props must be cached until all props have been received (to allow the Prop-content-length to be found). This is signalled either by the node text arriving, or the node being closed. The writing_begun members of the associated object batons track the state. output_revision() and output_node() are called to cause this flushing of cached data to occur.*//* Filtering batons */struct revmap_t{ svn_revnum_t rev; /* Last non-dropped revision to which this maps. */ svn_boolean_t was_dropped; /* Was this revision dropped? */};struct parse_baton_t { /* Command-line options values. */ svn_boolean_t do_exclude; svn_boolean_t quiet; svn_boolean_t drop_empty_revs; svn_boolean_t do_renumber_revs; svn_boolean_t preserve_revprops; apr_array_header_t *prefixes; /* Input and output streams. */ svn_stream_t *in_stream; svn_stream_t *out_stream; /* State for the filtering process. */ apr_int32_t rev_drop_count; apr_hash_t *dropped_nodes; apr_hash_t *renumber_history; /* svn_revnum_t -> struct revmap_t */ svn_revnum_t last_live_revision;};struct revision_baton_t { /* Reference to the global parse baton. */ struct parse_baton_t *pb; /* Does this revision have node or prop changes? */ svn_boolean_t has_nodes; svn_boolean_t has_props; /* Did we drop any nodes? */ svn_boolean_t had_dropped_nodes; /* Written to output stream? */ svn_boolean_t writing_begun; /* The original and new (re-mapped) revision numbers. */ svn_revnum_t rev_orig; svn_revnum_t rev_actual; /* Pointers to dumpfile data. */ svn_stringbuf_t *header; apr_hash_t *props;};struct node_baton_t { /* Reference to the current revision baton. */ struct revision_baton_t *rb; /* Are we skipping this node? */ svn_boolean_t do_skip; /* Have we been instructed to change or remove props on, or change the text of, this node? */ svn_boolean_t has_props; svn_boolean_t has_text; /* Written to output stream? */ svn_boolean_t writing_begun; /* The text content length according to the dumpfile headers, because we need the length before we have the actual text. */ svn_filesize_t tcl; /* Pointers to dumpfile data. */ svn_stringbuf_t *header; svn_stringbuf_t *props;};/* Filtering vtable members *//* New revision: set up revision_baton, decide if we skip it. */static svn_error_t *new_revision_record(void **revision_baton, apr_hash_t *headers, void *parse_baton, apr_pool_t *pool){ struct revision_baton_t *rb; apr_hash_index_t *hi; const void *key; void *val; svn_stream_t *header_stream; *revision_baton = apr_palloc(pool, sizeof(struct revision_baton_t)); rb = *revision_baton; rb->pb = parse_baton; rb->has_nodes = FALSE; rb->has_props = FALSE; rb->had_dropped_nodes = FALSE; rb->writing_begun = FALSE; rb->header = svn_stringbuf_create("", pool); rb->props = apr_hash_make(pool); header_stream = svn_stream_from_stringbuf(rb->header, pool); val = apr_hash_get(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER, APR_HASH_KEY_STRING); rb->rev_orig = SVN_STR_TO_REV(val); if (rb->pb->do_renumber_revs) { rb->rev_actual = rb->rev_orig - rb->pb->rev_drop_count; } else { rb->rev_actual = rb->rev_orig; } SVN_ERR(svn_stream_printf(header_stream, pool, SVN_REPOS_DUMPFILE_REVISION_NUMBER ": %ld\n", rb->rev_actual)); for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, &key, NULL, &val); if ((!strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH)) || (!strcmp(key, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH)) || (!strcmp(key, SVN_REPOS_DUMPFILE_REVISION_NUMBER))) continue; /* passthru: put header into header stringbuf. */ SVN_ERR(svn_stream_printf(header_stream, pool, "%s: %s\n", (const char *)key, (const char *)val)); } SVN_ERR(svn_stream_close(header_stream)); return SVN_NO_ERROR;}/* Output revision to dumpstream This may be called by new_node_record(), iff rb->has_nodes has been set to TRUE, or by close_revision() otherwise. This must only be called if rb->writing_begun is FALSE. */static svn_error_t *output_revision(struct revision_baton_t *rb){ int bytes_used; char buf[SVN_KEYLINE_MAXLEN]; apr_hash_index_t *hi; apr_pool_t *hash_pool = apr_hash_pool_get(rb->props); svn_stringbuf_t *props = svn_stringbuf_create("", hash_pool); apr_pool_t *subpool = svn_pool_create(hash_pool); rb->writing_begun = TRUE; /* If this revision has no nodes left because the ones it had were dropped, and we are not dropping empty revisions, and we were not told to preserve revision props, then we want to fixup the revision props to only contain: - the date - a log message that reports that this revision is just stuffing. */ if ((! rb->pb->preserve_revprops) && (! rb->has_nodes) && rb->had_dropped_nodes && (! rb->pb->drop_empty_revs)) { apr_hash_t *old_props = rb->props; rb->has_props = TRUE; rb->props = apr_hash_make(hash_pool); apr_hash_set(rb->props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING, apr_hash_get(old_props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING)); apr_hash_set(rb->props, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, svn_string_create(_("This is an empty revision for " "padding."), hash_pool)); } /* Now, "rasterize" the props to a string, and append the property information to the header string. */ if (rb->has_props) { for (hi = apr_hash_first(subpool, rb->props); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_this(hi, &key, NULL, &val); write_prop_to_stringbuf(&props, key, val); } svn_stringbuf_appendcstr(props, "PROPS-END\n"); svn_stringbuf_appendcstr(rb->header, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH); sprintf(buf, ": %" APR_SIZE_T_FMT "%n", props->len, &bytes_used); svn_stringbuf_appendbytes(rb->header, buf, bytes_used); svn_stringbuf_appendbytes(rb->header, "\n", 1); } svn_stringbuf_appendcstr(rb->header, SVN_REPOS_DUMPFILE_CONTENT_LENGTH); sprintf(buf, ": %" APR_SIZE_T_FMT "%n", props->len, &bytes_used); svn_stringbuf_appendbytes(rb->header, buf, bytes_used); svn_stringbuf_appendbytes(rb->header, "\n", 1); /* put an end to headers */ svn_stringbuf_appendbytes(rb->header, "\n", 1); /* put an end to revision */ svn_stringbuf_appendbytes(props, "\n", 1); /* write out the revision */ /* Revision is written out in the following cases: 1. No --drop-empty-revs has been supplied. 2. --drop-empty-revs has been supplied, but revision has not all nodes dropped 3. Revision had no nodes to begin with. */ if (rb->has_nodes || (! rb->pb->drop_empty_revs) || (! rb->had_dropped_nodes)) { /* This revision is a keeper. */ SVN_ERR(svn_stream_write(rb->pb->out_stream, rb->header->data, &(rb->header->len))); SVN_ERR(svn_stream_write(rb->pb->out_stream, props->data, &(props->len))); if (rb->pb->do_renumber_revs) { svn_revnum_t *rr_key; struct revmap_t *rr_val; apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history); rr_key = apr_palloc(rr_pool, sizeof(*rr_key)); rr_val = apr_palloc(rr_pool, sizeof(*rr_val)); *rr_key = rb->rev_orig; rr_val->rev = rb->rev_actual; rr_val->was_dropped = FALSE; apr_hash_set(rb->pb->renumber_history, rr_key, sizeof(*rr_key), rr_val); rb->pb->last_live_revision = rb->rev_actual; } if (! rb->pb->quiet) SVN_ERR(svn_cmdline_fprintf(stderr, subpool, _("Revision %ld committed as %ld.\n"), rb->rev_orig, rb->rev_actual)); } else { /* We're dropping this revision. */ rb->pb->rev_drop_count++; if (rb->pb->do_renumber_revs) { svn_revnum_t *rr_key; struct revmap_t *rr_val; apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history); rr_key = apr_palloc(rr_pool, sizeof(*rr_key)); rr_val = apr_palloc(rr_pool, sizeof(*rr_val)); *rr_key = rb->rev_orig; rr_val->rev = rb->pb->last_live_revision; rr_val->was_dropped = TRUE; apr_hash_set(rb->pb->renumber_history, rr_key, sizeof(*rr_key), rr_val); } if (! rb->pb->quiet) SVN_ERR(svn_cmdline_fprintf(stderr, subpool, _("Revision %ld skipped.\n"), rb->rev_orig)); } svn_pool_destroy(subpool); return SVN_NO_ERROR;}/* UUID record here: dump it, as we do not filter them. */static svn_error_t *uuid_record(const char *uuid, void *parse_baton, apr_pool_t *pool){ struct parse_baton_t *pb = parse_baton; SVN_ERR(svn_stream_printf(pb->out_stream, pool, SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid)); return SVN_NO_ERROR;}/* New node here. Set up node_baton by copying headers. */static svn_error_t *new_node_record(void **node_baton, apr_hash_t *headers, void *rev_baton, apr_pool_t *pool){ struct parse_baton_t *pb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -