summaryrefslogtreecommitdiff
path: root/subversion/svnmucc/svnmucc.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svnmucc/svnmucc.c')
-rw-r--r--subversion/svnmucc/svnmucc.c1252
1 files changed, 381 insertions, 871 deletions
diff --git a/subversion/svnmucc/svnmucc.c b/subversion/svnmucc/svnmucc.c
index d53f18f6ff76..1d2be38bba9b 100644
--- a/subversion/svnmucc/svnmucc.c
+++ b/subversion/svnmucc/svnmucc.c
@@ -40,96 +40,40 @@
#include <apr_lib.h>
+#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_client.h"
+#include "private/svn_client_mtcc.h"
#include "svn_cmdline.h"
#include "svn_config.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_props.h"
-#include "svn_ra.h"
#include "svn_string.h"
#include "svn_subst.h"
#include "svn_utf.h"
#include "svn_version.h"
#include "private/svn_cmdline_private.h"
-#include "private/svn_ra_private.h"
-#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
-#include "svn_private_config.h"
-
-static void handle_error(svn_error_t *err, apr_pool_t *pool)
-{
- if (err)
- svn_handle_error2(err, stderr, FALSE, "svnmucc: ");
- svn_error_clear(err);
- if (pool)
- svn_pool_destroy(pool);
- exit(EXIT_FAILURE);
-}
-
-static apr_pool_t *
-init(const char *application)
-{
- svn_error_t *err;
- const svn_version_checklist_t checklist[] = {
- {"svn_client", svn_client_version},
- {"svn_subr", svn_subr_version},
- {"svn_ra", svn_ra_version},
- {NULL, NULL}
- };
- SVN_VERSION_DEFINE(my_version);
-
- if (svn_cmdline_init(application, stderr))
- exit(EXIT_FAILURE);
-
- err = svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
- if (err)
- handle_error(err, NULL);
-
- return apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
-}
-
+/* Version compatibility check */
static svn_error_t *
-open_tmp_file(apr_file_t **fp,
- void *callback_baton,
- apr_pool_t *pool)
+check_lib_versions(void)
{
- /* Open a unique file; use APR_DELONCLOSE. */
- return svn_io_open_unique_file3(fp, NULL, NULL, svn_io_file_del_on_close,
- pool, pool);
-}
-
-static svn_error_t *
-create_ra_callbacks(svn_ra_callbacks2_t **callbacks,
- const char *username,
- const char *password,
- const char *config_dir,
- svn_config_t *cfg_config,
- svn_boolean_t non_interactive,
- svn_boolean_t trust_server_cert,
- svn_boolean_t no_auth_cache,
- apr_pool_t *pool)
-{
- SVN_ERR(svn_ra_create_callbacks(callbacks, pool));
-
- SVN_ERR(svn_cmdline_create_auth_baton(&(*callbacks)->auth_baton,
- non_interactive,
- username, password, config_dir,
- no_auth_cache,
- trust_server_cert,
- cfg_config, NULL, NULL, pool));
-
- (*callbacks)->open_tmp_file = open_tmp_file;
+ static const svn_version_checklist_t checklist[] =
+ {
+ { "svn_client", svn_client_version },
+ { "svn_subr", svn_subr_version },
+ { "svn_ra", svn_ra_version },
+ { NULL, NULL }
+ };
+ SVN_VERSION_DEFINE(my_version);
- return SVN_NO_ERROR;
+ return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}
-
-
static svn_error_t *
commit_callback(const svn_commit_info_t *commit_info,
void *baton,
@@ -154,204 +98,6 @@ typedef enum action_code_t {
ACTION_RM
} action_code_t;
-struct operation {
- enum {
- OP_OPEN,
- OP_DELETE,
- OP_ADD,
- OP_REPLACE,
- OP_PROPSET /* only for files for which no other operation is
- occuring; directories are OP_OPEN with non-empty
- props */
- } operation;
- svn_node_kind_t kind; /* to copy, mkdir, put or set revprops */
- svn_revnum_t rev; /* to copy, valid for add and replace */
- const char *url; /* to copy, valid for add and replace */
- const char *src_file; /* for put, the source file for contents */
- apr_hash_t *children; /* const char *path -> struct operation * */
- apr_hash_t *prop_mods; /* const char *prop_name ->
- const svn_string_t *prop_value */
- apr_array_header_t *prop_dels; /* const char *prop_name deletions */
- void *baton; /* as returned by the commit editor */
-};
-
-
-/* An iterator (for use via apr_table_do) which sets node properties.
- REC is a pointer to a struct driver_state. */
-static svn_error_t *
-change_props(const svn_delta_editor_t *editor,
- void *baton,
- struct operation *child,
- apr_pool_t *pool)
-{
- apr_pool_t *iterpool = svn_pool_create(pool);
-
- if (child->prop_dels)
- {
- int i;
- for (i = 0; i < child->prop_dels->nelts; i++)
- {
- const char *prop_name;
-
- svn_pool_clear(iterpool);
- prop_name = APR_ARRAY_IDX(child->prop_dels, i, const char *);
- if (child->kind == svn_node_dir)
- SVN_ERR(editor->change_dir_prop(baton, prop_name,
- NULL, iterpool));
- else
- SVN_ERR(editor->change_file_prop(baton, prop_name,
- NULL, iterpool));
- }
- }
- if (apr_hash_count(child->prop_mods))
- {
- apr_hash_index_t *hi;
- for (hi = apr_hash_first(pool, child->prop_mods);
- hi; hi = apr_hash_next(hi))
- {
- const char *propname = svn__apr_hash_index_key(hi);
- const svn_string_t *val = svn__apr_hash_index_val(hi);
-
- svn_pool_clear(iterpool);
- if (child->kind == svn_node_dir)
- SVN_ERR(editor->change_dir_prop(baton, propname, val, iterpool));
- else
- SVN_ERR(editor->change_file_prop(baton, propname, val, iterpool));
- }
- }
-
- svn_pool_destroy(iterpool);
- return SVN_NO_ERROR;
-}
-
-
-/* Drive EDITOR to affect the change represented by OPERATION. HEAD
- is the last-known youngest revision in the repository. */
-static svn_error_t *
-drive(struct operation *operation,
- svn_revnum_t head,
- const svn_delta_editor_t *editor,
- apr_pool_t *pool)
-{
- apr_pool_t *subpool = svn_pool_create(pool);
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(pool, operation->children);
- hi; hi = apr_hash_next(hi))
- {
- const char *key = svn__apr_hash_index_key(hi);
- struct operation *child = svn__apr_hash_index_val(hi);
- void *file_baton = NULL;
-
- svn_pool_clear(subpool);
-
- /* Deletes and replacements are simple -- delete something. */
- if (child->operation == OP_DELETE || child->operation == OP_REPLACE)
- {
- SVN_ERR(editor->delete_entry(key, head, operation->baton, subpool));
- }
- /* Opens could be for directories or files. */
- if (child->operation == OP_OPEN || child->operation == OP_PROPSET)
- {
- if (child->kind == svn_node_dir)
- {
- SVN_ERR(editor->open_directory(key, operation->baton, head,
- subpool, &child->baton));
- }
- else
- {
- SVN_ERR(editor->open_file(key, operation->baton, head,
- subpool, &file_baton));
- }
- }
- /* Adds and replacements could also be for directories or files. */
- if (child->operation == OP_ADD || child->operation == OP_REPLACE)
- {
- if (child->kind == svn_node_dir)
- {
- SVN_ERR(editor->add_directory(key, operation->baton,
- child->url, child->rev,
- subpool, &child->baton));
- }
- else
- {
- SVN_ERR(editor->add_file(key, operation->baton, child->url,
- child->rev, subpool, &file_baton));
- }
- }
- /* If there's a source file and an open file baton, we get to
- change textual contents. */
- if ((child->src_file) && (file_baton))
- {
- svn_txdelta_window_handler_t handler;
- void *handler_baton;
- svn_stream_t *contents;
-
- SVN_ERR(editor->apply_textdelta(file_baton, NULL, subpool,
- &handler, &handler_baton));
- if (strcmp(child->src_file, "-") != 0)
- {
- SVN_ERR(svn_stream_open_readonly(&contents, child->src_file,
- pool, pool));
- }
- else
- {
- SVN_ERR(svn_stream_for_stdin(&contents, pool));
- }
- SVN_ERR(svn_txdelta_send_stream(contents, handler,
- handler_baton, NULL, pool));
- }
- /* If we opened a file, we need to apply outstanding propmods,
- then close it. */
- if (file_baton)
- {
- if (child->kind == svn_node_file)
- {
- SVN_ERR(change_props(editor, file_baton, child, subpool));
- }
- SVN_ERR(editor->close_file(file_baton, NULL, subpool));
- }
- /* If we opened, added, or replaced a directory, we need to
- recurse, apply outstanding propmods, and then close it. */
- if ((child->kind == svn_node_dir)
- && child->operation != OP_DELETE)
- {
- SVN_ERR(change_props(editor, child->baton, child, subpool));
-
- SVN_ERR(drive(child, head, editor, subpool));
-
- SVN_ERR(editor->close_directory(child->baton, subpool));
- }
- }
- svn_pool_destroy(subpool);
- return SVN_NO_ERROR;
-}
-
-
-/* Find the operation associated with PATH, which is a single-path
- component representing a child of the path represented by
- OPERATION. If no such child operation exists, create a new one of
- type OP_OPEN. */
-static struct operation *
-get_operation(const char *path,
- struct operation *operation,
- apr_pool_t *pool)
-{
- struct operation *child = svn_hash_gets(operation->children, path);
- if (! child)
- {
- child = apr_pcalloc(pool, sizeof(*child));
- child->children = apr_hash_make(pool);
- child->operation = OP_OPEN;
- child->rev = SVN_INVALID_REVNUM;
- child->kind = svn_node_dir;
- child->prop_mods = apr_hash_make(pool);
- child->prop_dels = apr_array_make(pool, 1, sizeof(const char *));
- svn_hash_sets(operation->children, path, child);
- }
- return child;
-}
-
/* Return the portion of URL that is relative to ANCHOR (URI-decoded). */
static const char *
subtract_anchor(const char *anchor, const char *url, apr_pool_t *pool)
@@ -359,221 +105,6 @@ subtract_anchor(const char *anchor, const char *url, apr_pool_t *pool)
return svn_uri_skip_ancestor(anchor, url, pool);
}
-/* Add PATH to the operations tree rooted at OPERATION, creating any
- intermediate nodes that are required. Here's what's expected for
- each action type:
-
- ACTION URL REV SRC-FILE PROPNAME
- ------------ ----- ------- -------- --------
- ACTION_MKDIR NULL invalid NULL NULL
- ACTION_CP valid valid NULL NULL
- ACTION_PUT NULL invalid valid NULL
- ACTION_RM NULL invalid NULL NULL
- ACTION_PROPSET valid invalid NULL valid
- ACTION_PROPDEL valid invalid NULL valid
-
- Node type information is obtained for any copy source (to determine
- whether to create a file or directory) and for any deleted path (to
- ensure it exists since svn_delta_editor_t->delete_entry doesn't
- return an error on non-existent nodes). */
-static svn_error_t *
-build(action_code_t action,
- const char *path,
- const char *url,
- svn_revnum_t rev,
- const char *prop_name,
- const svn_string_t *prop_value,
- const char *src_file,
- svn_revnum_t head,
- const char *anchor,
- svn_ra_session_t *session,
- struct operation *operation,
- apr_pool_t *pool)
-{
- apr_array_header_t *path_bits = svn_path_decompose(path, pool);
- const char *path_so_far = "";
- const char *copy_src = NULL;
- svn_revnum_t copy_rev = SVN_INVALID_REVNUM;
- int i;
-
- /* Look for any previous operations we've recognized for PATH. If
- any of PATH's ancestors have not yet been traversed, we'll be
- creating OP_OPEN operations for them as we walk down PATH's path
- components. */
- for (i = 0; i < path_bits->nelts; ++i)
- {
- const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
- path_so_far = svn_relpath_join(path_so_far, path_bit, pool);
- operation = get_operation(path_so_far, operation, pool);
-
- /* If we cross a replace- or add-with-history, remember the
- source of those things in case we need to lookup the node kind
- of one of their children. And if this isn't such a copy,
- but we've already seen one in of our parent paths, we just need
- to extend that copy source path by our current path
- component. */
- if (operation->url
- && SVN_IS_VALID_REVNUM(operation->rev)
- && (operation->operation == OP_REPLACE
- || operation->operation == OP_ADD))
- {
- copy_src = subtract_anchor(anchor, operation->url, pool);
- copy_rev = operation->rev;
- }
- else if (copy_src)
- {
- copy_src = svn_relpath_join(copy_src, path_bit, pool);
- }
- }
-
- /* Handle property changes. */
- if (prop_name)
- {
- if (operation->operation == OP_DELETE)
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "cannot set properties on a location being"
- " deleted ('%s')", path);
- /* If we're not adding this thing ourselves, check for existence. */
- if (! ((operation->operation == OP_ADD) ||
- (operation->operation == OP_REPLACE)))
- {
- SVN_ERR(svn_ra_check_path(session,
- copy_src ? copy_src : path,
- copy_src ? copy_rev : head,
- &operation->kind, pool));
- if (operation->kind == svn_node_none)
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "propset: '%s' not found", path);
- else if ((operation->kind == svn_node_file)
- && (operation->operation == OP_OPEN))
- operation->operation = OP_PROPSET;
- }
- if (! prop_value)
- APR_ARRAY_PUSH(operation->prop_dels, const char *) = prop_name;
- else
- svn_hash_sets(operation->prop_mods, prop_name, prop_value);
- if (!operation->rev)
- operation->rev = rev;
- return SVN_NO_ERROR;
- }
-
- /* We won't fuss about multiple operations on the same path in the
- following cases:
-
- - the prior operation was, in fact, a no-op (open)
- - the prior operation was a propset placeholder
- - the prior operation was a deletion
-
- Note: while the operation structure certainly supports the
- ability to do a copy of a file followed by a put of new contents
- for the file, we don't let that happen (yet).
- */
- if (operation->operation != OP_OPEN
- && operation->operation != OP_PROPSET
- && operation->operation != OP_DELETE)
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "unsupported multiple operations on '%s'", path);
-
- /* For deletions, we validate that there's actually something to
- delete. If this is a deletion of the child of a copied
- directory, we need to remember to look in the copy source tree to
- verify that this thing actually exists. */
- if (action == ACTION_RM)
- {
- operation->operation = OP_DELETE;
- SVN_ERR(svn_ra_check_path(session,
- copy_src ? copy_src : path,
- copy_src ? copy_rev : head,
- &operation->kind, pool));
- if (operation->kind == svn_node_none)
- {
- if (copy_src && strcmp(path, copy_src))
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' (from '%s:%ld') not found",
- path, copy_src, copy_rev);
- else
- return svn_error_createf(SVN_ERR_BAD_URL, NULL, "'%s' not found",
- path);
- }
- }
- /* Handle copy operations (which can be adds or replacements). */
- else if (action == ACTION_CP)
- {
- if (rev > head)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- "Copy source revision cannot be younger "
- "than base revision");
- operation->operation =
- operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
- if (operation->operation == OP_ADD)
- {
- /* There is a bug in the current version of mod_dav_svn
- which incorrectly replaces existing directories.
- Therefore we need to check if the target exists
- and raise an error here. */
- SVN_ERR(svn_ra_check_path(session,
- copy_src ? copy_src : path,
- copy_src ? copy_rev : head,
- &operation->kind, pool));
- if (operation->kind != svn_node_none)
- {
- if (copy_src && strcmp(path, copy_src))
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' (from '%s:%ld') already exists",
- path, copy_src, copy_rev);
- else
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' already exists", path);
- }
- }
- SVN_ERR(svn_ra_check_path(session, subtract_anchor(anchor, url, pool),
- rev, &operation->kind, pool));
- if (operation->kind == svn_node_none)
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' not found",
- subtract_anchor(anchor, url, pool));
- operation->url = url;
- operation->rev = rev;
- }
- /* Handle mkdir operations (which can be adds or replacements). */
- else if (action == ACTION_MKDIR)
- {
- operation->operation =
- operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
- operation->kind = svn_node_dir;
- }
- /* Handle put operations (which can be adds, replacements, or opens). */
- else if (action == ACTION_PUT)
- {
- if (operation->operation == OP_DELETE)
- {
- operation->operation = OP_REPLACE;
- }
- else
- {
- SVN_ERR(svn_ra_check_path(session,
- copy_src ? copy_src : path,
- copy_src ? copy_rev : head,
- &operation->kind, pool));
- if (operation->kind == svn_node_file)
- operation->operation = OP_OPEN;
- else if (operation->kind == svn_node_none)
- operation->operation = OP_ADD;
- else
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' is not a file", path);
- }
- operation->kind = svn_node_file;
- operation->src_file = src_file;
- }
- else
- {
- /* We shouldn't get here. */
- SVN_ERR_MALFUNCTION();
- }
-
- return SVN_NO_ERROR;
-}
struct action {
action_code_t action;
@@ -597,274 +128,89 @@ struct action {
const svn_string_t *prop_value;
};
-struct fetch_baton
-{
- svn_ra_session_t *session;
- svn_revnum_t head;
-};
-
-static svn_error_t *
-fetch_base_func(const char **filename,
- void *baton,
- const char *path,
- svn_revnum_t base_revision,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- struct fetch_baton *fb = baton;
- svn_stream_t *fstream;
- svn_error_t *err;
-
- if (! SVN_IS_VALID_REVNUM(base_revision))
- base_revision = fb->head;
-
- SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
- svn_io_file_del_on_pool_cleanup,
- result_pool, scratch_pool));
-
- err = svn_ra_get_file(fb->session, path, base_revision, fstream, NULL, NULL,
- scratch_pool);
- if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
- {
- svn_error_clear(err);
- SVN_ERR(svn_stream_close(fstream));
-
- *filename = NULL;
- return SVN_NO_ERROR;
- }
- else if (err)
- return svn_error_trace(err);
-
- SVN_ERR(svn_stream_close(fstream));
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-fetch_props_func(apr_hash_t **props,
- void *baton,
- const char *path,
- svn_revnum_t base_revision,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- struct fetch_baton *fb = baton;
- svn_node_kind_t node_kind;
-
- if (! SVN_IS_VALID_REVNUM(base_revision))
- base_revision = fb->head;
-
- SVN_ERR(svn_ra_check_path(fb->session, path, base_revision, &node_kind,
- scratch_pool));
-
- if (node_kind == svn_node_file)
- {
- SVN_ERR(svn_ra_get_file(fb->session, path, base_revision, NULL, NULL,
- props, result_pool));
- }
- else if (node_kind == svn_node_dir)
- {
- apr_array_header_t *tmp_props;
-
- SVN_ERR(svn_ra_get_dir2(fb->session, NULL, NULL, props, path,
- base_revision, 0 /* Dirent fields */,
- result_pool));
- tmp_props = svn_prop_hash_to_array(*props, result_pool);
- SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
- result_pool));
- *props = svn_prop_array_to_hash(tmp_props, result_pool);
- }
- else
- {
- *props = apr_hash_make(result_pool);
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-fetch_kind_func(svn_node_kind_t *kind,
- void *baton,
- const char *path,
- svn_revnum_t base_revision,
- apr_pool_t *scratch_pool)
-{
- struct fetch_baton *fb = baton;
-
- if (! SVN_IS_VALID_REVNUM(base_revision))
- base_revision = fb->head;
-
- SVN_ERR(svn_ra_check_path(fb->session, path, base_revision, kind,
- scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-static svn_delta_shim_callbacks_t *
-get_shim_callbacks(svn_ra_session_t *session,
- svn_revnum_t head,
- apr_pool_t *result_pool)
-{
- svn_delta_shim_callbacks_t *callbacks =
- svn_delta_shim_callbacks_default(result_pool);
- struct fetch_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
-
- fb->session = session;
- fb->head = head;
-
- callbacks->fetch_props_func = fetch_props_func;
- callbacks->fetch_kind_func = fetch_kind_func;
- callbacks->fetch_base_func = fetch_base_func;
- callbacks->fetch_baton = fb;
-
- return callbacks;
-}
-
static svn_error_t *
execute(const apr_array_header_t *actions,
const char *anchor,
apr_hash_t *revprops,
- const char *username,
- const char *password,
- const char *config_dir,
- const apr_array_header_t *config_options,
- svn_boolean_t non_interactive,
- svn_boolean_t trust_server_cert,
- svn_boolean_t no_auth_cache,
svn_revnum_t base_revision,
+ svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- svn_ra_session_t *session;
- svn_ra_session_t *aux_session;
- const char *repos_root;
- svn_revnum_t head;
- const svn_delta_editor_t *editor;
- svn_ra_callbacks2_t *ra_callbacks;
- void *editor_baton;
- struct operation root;
+ svn_client__mtcc_t *mtcc;
+ apr_pool_t *iterpool = svn_pool_create(pool);
svn_error_t *err;
- apr_hash_t *config;
- svn_config_t *cfg_config;
int i;
- SVN_ERR(svn_config_get_config(&config, config_dir, pool));
- SVN_ERR(svn_cmdline__apply_config_options(config, config_options,
- "svnmucc: ", "--config-option"));
- cfg_config = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
-
- if (! svn_hash_gets(revprops, SVN_PROP_REVISION_LOG))
- {
- svn_string_t *msg = svn_string_create("", pool);
-
- /* If we can do so, try to pop up $EDITOR to fetch a log message. */
- if (non_interactive)
- {
- return svn_error_create
- (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
- _("Cannot invoke editor to get log message "
- "when non-interactive"));
- }
- else
- {
- SVN_ERR(svn_cmdline__edit_string_externally(
- &msg, NULL, NULL, "", msg, "svnmucc-commit", config,
- TRUE, NULL, apr_hash_pool_get(revprops)));
- }
-
- svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, msg);
- }
-
- SVN_ERR(create_ra_callbacks(&ra_callbacks, username, password, config_dir,
- cfg_config, non_interactive, trust_server_cert,
- no_auth_cache, pool));
- SVN_ERR(svn_ra_open4(&session, NULL, anchor, NULL, ra_callbacks,
- NULL, config, pool));
- /* Open, then reparent to avoid AUTHZ errors when opening the reposroot */
- SVN_ERR(svn_ra_open4(&aux_session, NULL, anchor, NULL, ra_callbacks,
- NULL, config, pool));
- SVN_ERR(svn_ra_get_repos_root2(aux_session, &repos_root, pool));
- SVN_ERR(svn_ra_reparent(aux_session, repos_root, pool));
- SVN_ERR(svn_ra_get_latest_revnum(session, &head, pool));
-
- /* Reparent to ANCHOR's dir, if ANCHOR is not a directory. */
- {
- svn_node_kind_t kind;
-
- SVN_ERR(svn_ra_check_path(aux_session,
- svn_uri_skip_ancestor(repos_root, anchor, pool),
- head, &kind, pool));
- if (kind != svn_node_dir)
- {
- anchor = svn_uri_dirname(anchor, pool);
- SVN_ERR(svn_ra_reparent(session, anchor, pool));
- }
- }
-
- if (SVN_IS_VALID_REVNUM(base_revision))
- {
- if (base_revision > head)
- return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
- "No such revision %ld (youngest is %ld)",
- base_revision, head);
- head = base_revision;
- }
-
- memset(&root, 0, sizeof(root));
- root.children = apr_hash_make(pool);
- root.operation = OP_OPEN;
- root.kind = svn_node_dir; /* For setting properties */
- root.prop_mods = apr_hash_make(pool);
- root.prop_dels = apr_array_make(pool, 1, sizeof(const char *));
+ SVN_ERR(svn_client__mtcc_create(&mtcc, anchor,
+ SVN_IS_VALID_REVNUM(base_revision)
+ ? base_revision
+ : SVN_INVALID_REVNUM,
+ ctx, pool, iterpool));
for (i = 0; i < actions->nelts; ++i)
{
struct action *action = APR_ARRAY_IDX(actions, i, struct action *);
const char *path1, *path2;
+ svn_node_kind_t kind;
+
+ svn_pool_clear(iterpool);
+
switch (action->action)
{
case ACTION_MV:
path1 = subtract_anchor(anchor, action->path[0], pool);
path2 = subtract_anchor(anchor, action->path[1], pool);
- SVN_ERR(build(ACTION_RM, path1, NULL,
- SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor,
- session, &root, pool));
- SVN_ERR(build(ACTION_CP, path2, action->path[0],
- head, NULL, NULL, NULL, head, anchor,
- session, &root, pool));
+ SVN_ERR(svn_client__mtcc_add_move(path1, path2, mtcc, iterpool));
break;
case ACTION_CP:
+ path1 = subtract_anchor(anchor, action->path[0], pool);
path2 = subtract_anchor(anchor, action->path[1], pool);
- if (action->rev == SVN_INVALID_REVNUM)
- action->rev = head;
- SVN_ERR(build(ACTION_CP, path2, action->path[0],
- action->rev, NULL, NULL, NULL, head, anchor,
- session, &root, pool));
+ SVN_ERR(svn_client__mtcc_add_copy(path1, action->rev, path2,
+ mtcc, iterpool));
break;
case ACTION_RM:
path1 = subtract_anchor(anchor, action->path[0], pool);
- SVN_ERR(build(ACTION_RM, path1, NULL,
- SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor,
- session, &root, pool));
+ SVN_ERR(svn_client__mtcc_add_delete(path1, mtcc, iterpool));
break;
case ACTION_MKDIR:
path1 = subtract_anchor(anchor, action->path[0], pool);
- SVN_ERR(build(ACTION_MKDIR, path1, action->path[0],
- SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor,
- session, &root, pool));
+ SVN_ERR(svn_client__mtcc_add_mkdir(path1, mtcc, iterpool));
break;
case ACTION_PUT:
path1 = subtract_anchor(anchor, action->path[0], pool);
- SVN_ERR(build(ACTION_PUT, path1, action->path[0],
- SVN_INVALID_REVNUM, NULL, NULL, action->path[1],
- head, anchor, session, &root, pool));
+ SVN_ERR(svn_client__mtcc_check_path(&kind, path1, TRUE, mtcc, pool));
+
+ if (kind == svn_node_dir)
+ {
+ SVN_ERR(svn_client__mtcc_add_delete(path1, mtcc, pool));
+ kind = svn_node_none;
+ }
+
+ {
+ svn_stream_t *src;
+
+ if (strcmp(action->path[1], "-") != 0)
+ SVN_ERR(svn_stream_open_readonly(&src, action->path[1],
+ pool, iterpool));
+ else
+ SVN_ERR(svn_stream_for_stdin(&src, pool));
+
+
+ if (kind == svn_node_file)
+ SVN_ERR(svn_client__mtcc_add_update_file(path1, src, NULL,
+ NULL, NULL,
+ mtcc, iterpool));
+ else if (kind == svn_node_none)
+ SVN_ERR(svn_client__mtcc_add_add_file(path1, src, NULL,
+ mtcc, iterpool));
+ }
break;
case ACTION_PROPSET:
case ACTION_PROPDEL:
path1 = subtract_anchor(anchor, action->path[0], pool);
- SVN_ERR(build(action->action, path1, action->path[0],
- SVN_INVALID_REVNUM,
- action->prop_name, action->prop_value,
- NULL, head, anchor, session, &root, pool));
+ SVN_ERR(svn_client__mtcc_add_propset(path1, action->prop_name,
+ action->prop_value, FALSE,
+ mtcc, iterpool));
break;
case ACTION_PROPSETF:
default:
@@ -872,25 +218,11 @@ execute(const apr_array_header_t *actions,
}
}
- SVN_ERR(svn_ra__register_editor_shim_callbacks(session,
- get_shim_callbacks(aux_session, head, pool)));
- SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &editor_baton, revprops,
- commit_callback, NULL, NULL, FALSE, pool));
+ err = svn_client__mtcc_commit(revprops, commit_callback, NULL,
+ mtcc, iterpool);
- SVN_ERR(editor->open_root(editor_baton, head, pool, &root.baton));
- err = change_props(editor, root.baton, &root, pool);
- if (!err)
- err = drive(&root, head, editor, pool);
- if (!err)
- err = editor->close_directory(root.baton, pool);
- if (!err)
- err = editor->close_edit(editor_baton, pool);
-
- if (err)
- err = svn_error_compose_create(err,
- editor->abort_edit(editor_baton, pool));
-
- return err;
+ svn_pool_destroy(iterpool);
+ return svn_error_trace(err);
}
static svn_error_t *
@@ -920,12 +252,20 @@ sanitize_url(const char *url,
}
static void
-usage(apr_pool_t *pool, int exit_val)
+usage(apr_pool_t *pool)
+{
+ svn_error_clear(svn_cmdline_fprintf
+ (stderr, pool, _("Type 'svnmucc --help' for usage.\n")));
+}
+
+/* Print a usage message on STREAM. */
+static void
+help(FILE *stream, apr_pool_t *pool)
{
- FILE *stream = exit_val == EXIT_SUCCESS ? stdout : stderr;
svn_error_clear(svn_cmdline_fputs(
- _("Subversion multiple URL command client\n"
- "usage: svnmucc ACTION...\n"
+ _("usage: svnmucc ACTION...\n"
+ "Subversion multiple URL command client.\n"
+ "Type 'svnmucc --version' to see the program version and RA modules.\n"
"\n"
" Perform one or more Subversion repository URL-based ACTIONs, committing\n"
" the result as a (single) new revision.\n"
@@ -955,9 +295,16 @@ usage(apr_pool_t *pool, int exit_val)
" prompt only if standard input is a terminal)\n"
" --force-interactive : do interactive prompting even if standard\n"
" input is not a terminal\n"
- " --trust-server-cert : accept SSL server certificates from unknown\n"
- " certificate authorities without prompting (but\n"
- " only with '--non-interactive')\n"
+ " --trust-server-cert : deprecated;\n"
+ " same as --trust-server-cert-failures=unknown-ca\n"
+ " --trust-server-cert-failures ARG\n"
+ " with --non-interactive, accept SSL server\n"
+ " certificates with failures; ARG is comma-separated\n"
+ " list of 'unknown-ca' (Unknown Authority),\n"
+ " 'cn-mismatch' (Hostname mismatch), 'expired'\n"
+ " (Expired certificate),'not-yet-valid' (Not yet\n"
+ " valid certificate) and 'other' (all other not\n"
+ " separately classified certificate errors).\n"
" -X [--extra-args] ARG : append arguments from file ARG (one per line;\n"
" use \"-\" to read from standard input)\n"
" --config-dir ARG : use ARG to override the config directory\n"
@@ -965,20 +312,17 @@ usage(apr_pool_t *pool, int exit_val)
" --no-auth-cache : do not cache authentication tokens\n"
" --version : print version information\n"),
stream, pool));
- svn_pool_destroy(pool);
- exit(exit_val);
}
-static void
-insufficient(apr_pool_t *pool)
+static svn_error_t *
+insufficient(void)
{
- handle_error(svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
- "insufficient arguments"),
- pool);
+ return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "insufficient arguments");
}
static svn_error_t *
-display_version(apr_getopt_t *os, apr_pool_t *pool)
+display_version(apr_pool_t *pool)
{
const char *ra_desc_start
= "The following repository access (RA) modules are available:\n\n";
@@ -987,7 +331,7 @@ display_version(apr_getopt_t *os, apr_pool_t *pool)
version_footer = svn_stringbuf_create(ra_desc_start, pool);
SVN_ERR(svn_ra_print_modules(version_footer, pool));
- SVN_ERR(svn_opt_print_help4(os, "svnmucc", TRUE, FALSE, FALSE,
+ SVN_ERR(svn_opt_print_help4(NULL, "svnmucc", TRUE, FALSE, FALSE,
version_footer->data,
NULL, NULL, NULL, NULL, NULL, pool));
@@ -1005,46 +349,113 @@ mutually_exclusive_logs_error(void)
"exclusive"));
}
-/* Ensure that the REVPROPS hash contains a command-line-provided log
- message, if any, and that there was but one source of such a thing
- provided on that command-line. */
+/* Obtain the log message from multiple sources, producing an error
+ if there are multiple sources. Store the result in *FINAL_MESSAGE. */
static svn_error_t *
-sanitize_log_sources(apr_hash_t *revprops,
+sanitize_log_sources(const char **final_message,
const char *message,
- svn_stringbuf_t *filedata)
+ apr_hash_t *revprops,
+ svn_stringbuf_t *filedata,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_pool_t *hash_pool = apr_hash_pool_get(revprops);
+ svn_string_t *msg;
+ *final_message = NULL;
/* If we already have a log message in the revprop hash, then just
make sure the user didn't try to also use -m or -F. Otherwise,
we need to consult -m or -F to find a log message, if any. */
- if (svn_hash_gets(revprops, SVN_PROP_REVISION_LOG))
+ msg = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG);
+ if (msg)
{
if (filedata || message)
return mutually_exclusive_logs_error();
+
+ *final_message = apr_pstrdup(result_pool, msg->data);
+
+ /* Will be re-added by libsvn_client */
+ svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, NULL);
}
else if (filedata)
{
if (message)
return mutually_exclusive_logs_error();
- SVN_ERR(svn_utf_cstring_to_utf8(&message, filedata->data, hash_pool));
- svn_hash_sets(revprops, SVN_PROP_REVISION_LOG,
- svn_stringbuf__morph_into_string(filedata));
+ *final_message = apr_pstrdup(result_pool, filedata->data);
}
else if (message)
{
- svn_hash_sets(revprops, SVN_PROP_REVISION_LOG,
- svn_string_create(message, hash_pool));
+ *final_message = apr_pstrdup(result_pool, message);
}
return SVN_NO_ERROR;
}
-int
-main(int argc, const char **argv)
+/* Baton for log_message_func */
+struct log_message_baton
+{
+ svn_boolean_t non_interactive;
+ const char *log_message;
+ svn_client_ctx_t *ctx;
+};
+
+/* Implements svn_client_get_commit_log3_t */
+static svn_error_t *
+log_message_func(const char **log_msg,
+ const char **tmp_file,
+ const apr_array_header_t *commit_items,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct log_message_baton *lmb = baton;
+
+ *tmp_file = NULL;
+
+ if (lmb->log_message)
+ {
+ svn_string_t *message = svn_string_create(lmb->log_message, pool);
+
+ SVN_ERR_W(svn_subst_translate_string2(&message, NULL, NULL,
+ message, NULL, FALSE,
+ pool, pool),
+ _("Error normalizing log message to internal format"));
+
+ *log_msg = message->data;
+
+ return SVN_NO_ERROR;
+ }
+
+ if (lmb->non_interactive)
+ {
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Cannot invoke editor to get log message "
+ "when non-interactive"));
+ }
+ else
+ {
+ svn_string_t *msg = svn_string_create("", pool);
+
+ SVN_ERR(svn_cmdline__edit_string_externally(
+ &msg, NULL, NULL, "", msg, "svnmucc-commit",
+ lmb->ctx->config, TRUE, NULL, pool));
+
+ if (msg && msg->data)
+ *log_msg = msg->data;
+ else
+ *log_msg = NULL;
+
+ return SVN_NO_ERROR;
+ }
+}
+
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
- apr_pool_t *pool = init("svnmucc");
apr_array_header_t *actions = apr_array_make(pool, 1,
sizeof(struct action *));
const char *anchor = NULL;
@@ -1058,7 +469,8 @@ main(int argc, const char **argv)
with_revprop_opt,
non_interactive_opt,
force_interactive_opt,
- trust_server_cert_opt
+ trust_server_cert_opt,
+ trust_server_cert_failures_opt,
};
static const apr_getopt_option_t options[] = {
{"message", 'm', 1, ""},
@@ -1074,6 +486,7 @@ main(int argc, const char **argv)
{"non-interactive", non_interactive_opt, 0, ""},
{"force-interactive", force_interactive_opt, 0, ""},
{"trust-server-cert", trust_server_cert_opt, 0, ""},
+ {"trust-server-cert-failures", trust_server_cert_failures_opt, 1, ""},
{"config-dir", config_dir_opt, 1, ""},
{"config-option", config_inline_opt, 1, ""},
{"no-auth-cache", no_auth_cache_opt, 0, ""},
@@ -1088,13 +501,26 @@ main(int argc, const char **argv)
apr_array_header_t *config_options;
svn_boolean_t non_interactive = FALSE;
svn_boolean_t force_interactive = FALSE;
- svn_boolean_t trust_server_cert = FALSE;
+ svn_boolean_t trust_unknown_ca = FALSE;
+ svn_boolean_t trust_cn_mismatch = FALSE;
+ svn_boolean_t trust_expired = FALSE;
+ svn_boolean_t trust_not_yet_valid = FALSE;
+ svn_boolean_t trust_other_failure = FALSE;
svn_boolean_t no_auth_cache = FALSE;
+ svn_boolean_t show_version = FALSE;
+ svn_boolean_t show_help = FALSE;
svn_revnum_t base_revision = SVN_INVALID_REVNUM;
apr_array_header_t *action_args;
apr_hash_t *revprops = apr_hash_make(pool);
+ apr_hash_t *cfg_hash;
+ svn_config_t *cfg_config;
+ svn_client_ctx_t *ctx;
+ struct log_message_baton lmb;
int i;
+ /* Check library versions */
+ SVN_ERR(check_lib_versions());
+
config_options = apr_array_make(pool, 0,
sizeof(svn_cmdline__config_argument_t*));
@@ -1110,22 +536,21 @@ main(int argc, const char **argv)
if (APR_STATUS_IS_EOF(status))
break;
if (status != APR_SUCCESS)
- handle_error(svn_error_wrap_apr(status, "getopt failure"), pool);
+ {
+ usage(pool);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
switch(opt)
{
case 'm':
- err = svn_utf_cstring_to_utf8(&message, arg, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&message, arg, pool));
break;
case 'F':
{
const char *arg_utf8;
- err = svn_utf_cstring_to_utf8(&arg_utf8, arg, pool);
- if (! err)
- err = svn_stringbuf_from_file2(&filedata, arg, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&arg_utf8, arg, pool));
+ SVN_ERR(svn_stringbuf_from_file2(&filedata, arg, pool));
}
break;
case 'u':
@@ -1135,31 +560,29 @@ main(int argc, const char **argv)
password = apr_pstrdup(pool, arg);
break;
case 'U':
- err = svn_utf_cstring_to_utf8(&root_url, arg, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&root_url, arg, pool));
if (! svn_path_is_url(root_url))
- handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
- "'%s' is not a URL\n", root_url),
- pool);
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "'%s' is not a URL\n", root_url);
root_url = sanitize_url(root_url, pool);
break;
case 'r':
{
+ const char *saved_arg = arg;
char *digits_end = NULL;
+ while (*arg == 'r')
+ arg++;
base_revision = strtol(arg, &digits_end, 10);
if ((! SVN_IS_VALID_REVNUM(base_revision))
|| (! digits_end)
|| *digits_end)
- handle_error(svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR,
- NULL, "Invalid revision number"),
- pool);
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Invalid revision number '%s'"),
+ saved_arg);
}
break;
case with_revprop_opt:
- err = svn_opt_parse_revprop(&revprops, arg, pool);
- if (err != SVN_NO_ERROR)
- handle_error(err, pool);
+ SVN_ERR(svn_opt_parse_revprop(&revprops, arg, pool));
break;
case 'X':
extra_args_file = apr_pstrdup(pool, arg);
@@ -1170,72 +593,81 @@ main(int argc, const char **argv)
case force_interactive_opt:
force_interactive = TRUE;
break;
- case trust_server_cert_opt:
- trust_server_cert = TRUE;
+ case trust_server_cert_opt: /* backward compat */
+ trust_unknown_ca = TRUE;
+ break;
+ case trust_server_cert_failures_opt:
+ SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, arg, pool));
+ SVN_ERR(svn_cmdline__parse_trust_options(
+ &trust_unknown_ca,
+ &trust_cn_mismatch,
+ &trust_expired,
+ &trust_not_yet_valid,
+ &trust_other_failure,
+ opt_arg, pool));
break;
case config_dir_opt:
- err = svn_utf_cstring_to_utf8(&config_dir, arg, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&config_dir, arg, pool));
break;
case config_inline_opt:
- err = svn_utf_cstring_to_utf8(&opt_arg, arg, pool);
- if (err)
- handle_error(err, pool);
-
- err = svn_cmdline__parse_config_option(config_options, opt_arg,
- pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, arg, pool));
+ SVN_ERR(svn_cmdline__parse_config_option(config_options, opt_arg,
+ "svnmucc: ",
+ pool));
break;
case no_auth_cache_opt:
no_auth_cache = TRUE;
break;
case version_opt:
- SVN_INT_ERR(display_version(opts, pool));
- exit(EXIT_SUCCESS);
+ show_version = TRUE;
break;
case 'h':
case '?':
- usage(pool, EXIT_SUCCESS);
+ show_help = TRUE;
break;
}
}
+ if (show_help)
+ {
+ help(stdout, pool);
+ return SVN_NO_ERROR;
+ }
+
+ if (show_version)
+ {
+ SVN_ERR(display_version(pool));
+ return SVN_NO_ERROR;
+ }
+
if (non_interactive && force_interactive)
{
- err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("--non-interactive and --force-interactive "
- "are mutually exclusive"));
- return svn_cmdline_handle_exit_error(err, pool, "svnmucc: ");
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--non-interactive and --force-interactive "
+ "are mutually exclusive"));
}
else
non_interactive = !svn_cmdline__be_interactive(non_interactive,
force_interactive);
- if (trust_server_cert && !non_interactive)
+ if (!non_interactive)
{
- err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("--trust-server-cert requires "
- "--non-interactive"));
- return svn_cmdline_handle_exit_error(err, pool, "svnmucc: ");
+ if (trust_unknown_ca || trust_cn_mismatch || trust_expired
+ || trust_not_yet_valid || trust_other_failure)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--trust-server-cert-failures requires "
+ "--non-interactive"));
}
- /* Make sure we have a log message to use. */
- err = sanitize_log_sources(revprops, message, filedata);
- if (err)
- handle_error(err, pool);
-
/* Copy the rest of our command-line arguments to an array,
UTF-8-ing them along the way. */
action_args = apr_array_make(pool, opts->argc, sizeof(const char *));
while (opts->ind < opts->argc)
{
const char *arg = opts->argv[opts->ind++];
- if ((err = svn_utf_cstring_to_utf8(&(APR_ARRAY_PUSH(action_args,
- const char *)),
- arg, pool)))
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&APR_ARRAY_PUSH(action_args,
+ const char *),
+ arg, pool));
}
/* If there are extra arguments in a supplementary file, tack those
@@ -1245,18 +677,69 @@ main(int argc, const char **argv)
const char *extra_args_file_utf8;
svn_stringbuf_t *contents, *contents_utf8;
- err = svn_utf_cstring_to_utf8(&extra_args_file_utf8,
- extra_args_file, pool);
- if (! err)
- err = svn_stringbuf_from_file2(&contents, extra_args_file_utf8, pool);
- if (! err)
- err = svn_utf_stringbuf_to_utf8(&contents_utf8, contents, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(svn_utf_cstring_to_utf8(&extra_args_file_utf8,
+ extra_args_file, pool));
+ SVN_ERR(svn_stringbuf_from_file2(&contents, extra_args_file_utf8, pool));
+ SVN_ERR(svn_utf_stringbuf_to_utf8(&contents_utf8, contents, pool));
svn_cstring_split_append(action_args, contents_utf8->data, "\n\r",
FALSE, pool);
}
+ /* Now initialize the client context */
+
+ err = svn_config_get_config(&cfg_hash, config_dir, pool);
+ if (err)
+ {
+ /* Fallback to default config if the config directory isn't readable
+ or is not a directory. */
+ if (APR_STATUS_IS_EACCES(err->apr_err)
+ || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
+ {
+ svn_handle_warning2(stderr, err, "svnmucc: ");
+ svn_error_clear(err);
+
+ SVN_ERR(svn_config__get_default_config(&cfg_hash, pool));
+ }
+ else
+ return err;
+ }
+
+ if (config_options)
+ {
+ svn_error_clear(
+ svn_cmdline__apply_config_options(cfg_hash, config_options,
+ "svnmucc: ", "--config-option"));
+ }
+
+ SVN_ERR(svn_client_create_context2(&ctx, cfg_hash, pool));
+
+ cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG);
+ SVN_ERR(svn_cmdline_create_auth_baton2(
+ &ctx->auth_baton,
+ non_interactive,
+ username,
+ password,
+ config_dir,
+ no_auth_cache,
+ trust_unknown_ca,
+ trust_cn_mismatch,
+ trust_expired,
+ trust_not_yet_valid,
+ trust_other_failure,
+ cfg_config,
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ pool));
+
+ lmb.non_interactive = non_interactive;
+ lmb.ctx = ctx;
+ /* Make sure we have a log message to use. */
+ SVN_ERR(sanitize_log_sources(&lmb.log_message, message, revprops, filedata,
+ pool, pool));
+
+ ctx->log_msg_func3 = log_message_func;
+ ctx->log_msg_baton3 = &lmb;
+
/* Now, we iterate over the combined set of arguments -- our actions. */
for (i = 0; i < action_args->nelts; )
{
@@ -1283,13 +766,16 @@ main(int argc, const char **argv)
action->action = ACTION_PROPDEL;
else if (! strcmp(action_string, "?") || ! strcmp(action_string, "h")
|| ! strcmp(action_string, "help"))
- usage(pool, EXIT_SUCCESS);
+ {
+ help(stdout, pool);
+ return SVN_NO_ERROR;
+ }
else
- handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
- "'%s' is not an action\n",
- action_string), pool);
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "'%s' is not an action\n",
+ action_string);
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
/* For copies, there should be a revision number next. */
if (action->action == ACTION_CP)
@@ -1308,12 +794,12 @@ main(int argc, const char **argv)
action->rev = strtol(rev_str, &end, 0);
if (*end)
- handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
- "'%s' is not a revision\n",
- rev_str), pool);
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "'%s' is not a revision\n",
+ rev_str);
}
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
}
else
{
@@ -1327,7 +813,7 @@ main(int argc, const char **argv)
svn_dirent_internal_style(APR_ARRAY_IDX(action_args, i,
const char *), pool);
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
}
/* For propset, propsetf, and propdel, a property name (and
@@ -1338,7 +824,7 @@ main(int argc, const char **argv)
{
action->prop_name = APR_ARRAY_IDX(action_args, i, const char *);
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
if (action->action == ACTION_PROPDEL)
{
@@ -1350,7 +836,7 @@ main(int argc, const char **argv)
svn_string_create(APR_ARRAY_IDX(action_args, i,
const char *), pool);
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
}
else
{
@@ -1359,12 +845,10 @@ main(int argc, const char **argv)
const char *), pool);
if (++i == action_args->nelts)
- insufficient(pool);
+ return insufficient();
- err = read_propvalue_file(&(action->prop_value),
- propval_file, pool);
- if (err)
- handle_error(err, pool);
+ SVN_ERR(read_propvalue_file(&(action->prop_value),
+ propval_file, pool));
action->action = ACTION_PROPSET;
}
@@ -1373,14 +857,10 @@ main(int argc, const char **argv)
&& svn_prop_needs_translation(action->prop_name))
{
svn_string_t *translated_value;
- err = svn_subst_translate_string2(&translated_value, NULL,
- NULL, action->prop_value, NULL,
- FALSE, pool, pool);
- if (err)
- handle_error(
- svn_error_quick_wrap(err,
- "Error normalizing property value"),
- pool);
+ SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL,
+ NULL, action->prop_value,
+ NULL, FALSE, pool, pool),
+ "Error normalizing property value");
action->prop_value = translated_value;
}
}
@@ -1408,14 +888,14 @@ main(int argc, const char **argv)
if (! svn_path_is_url(url))
{
if (! root_url)
- handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
- "'%s' is not a URL, and "
- "--root-url (-U) not provided\n",
- url), pool);
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "'%s' is not a URL, and "
+ "--root-url (-U) not provided\n",
+ url);
/* ### These relpaths are already URI-encoded. */
url = apr_pstrcat(pool, root_url, "/",
svn_relpath_canonicalize(url, pool),
- (char *)NULL);
+ SVN_VA_NULL);
}
url = sanitize_url(url, pool);
action->path[j] = url;
@@ -1433,36 +913,66 @@ main(int argc, const char **argv)
{
anchor = svn_uri_get_longest_ancestor(anchor, url, pool);
if (!anchor || !anchor[0])
- handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
- "URLs in the action list do not "
- "share a common ancestor"),
- pool);
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ "URLs in the action list do not "
+ "share a common ancestor");
}
if ((++i == action_args->nelts) && (j + 1 < num_url_args))
- insufficient(pool);
+ return insufficient();
}
+
APR_ARRAY_PUSH(actions, struct action *) = action;
}
if (! actions->nelts)
- usage(pool, EXIT_FAILURE);
+ {
+ *exit_code = EXIT_FAILURE;
+ help(stderr, pool);
+ return SVN_NO_ERROR;
+ }
- if ((err = execute(actions, anchor, revprops, username, password,
- config_dir, config_options, non_interactive,
- trust_server_cert, no_auth_cache, base_revision, pool)))
+ if ((err = execute(actions, anchor, revprops, base_revision, ctx, pool)))
{
if (err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive)
err = svn_error_quick_wrap(err,
_("Authentication failed and interactive"
" prompting is disabled; see the"
" --force-interactive option"));
- handle_error(err, pool);
+ return err;
}
- /* Ensure that stdout is flushed, so the user will see all results. */
- svn_error_clear(svn_cmdline_fflush(stdout));
+ return SVN_NO_ERROR;
+}
+
+int
+main(int argc, const char *argv[])
+{
+ apr_pool_t *pool;
+ int exit_code = EXIT_SUCCESS;
+ svn_error_t *err;
+
+ /* Initialize the app. */
+ if (svn_cmdline_init("svnmucc", stderr) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+
+ /* Create our top-level pool. Use a separate mutexless allocator,
+ * given this application is single threaded.
+ */
+ pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+ err = sub_main(&exit_code, argc, argv, pool);
+
+ /* Flush stdout and report if it fails. It would be flushed on exit anyway
+ but this makes sure that output is not silently lost if it fails. */
+ err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+ if (err)
+ {
+ exit_code = EXIT_FAILURE;
+ svn_cmdline_handle_exit_error(err, NULL, "svnmucc: ");
+ }
svn_pool_destroy(pool);
- return EXIT_SUCCESS;
+ return exit_code;
}