diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/svnbench/svnbench.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/svnbench/svnbench.c')
-rw-r--r-- | subversion/svnbench/svnbench.c | 1005 |
1 files changed, 1005 insertions, 0 deletions
diff --git a/subversion/svnbench/svnbench.c b/subversion/svnbench/svnbench.c new file mode 100644 index 0000000..bd67e0b --- /dev/null +++ b/subversion/svnbench/svnbench.c @@ -0,0 +1,1005 @@ +/* + * svnbench.c: Subversion benchmark client. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include <string.h> +#include <assert.h> + +#include <apr_signal.h> + +#include "svn_cmdline.h" +#include "svn_dirent_uri.h" +#include "svn_pools.h" +#include "svn_utf.h" +#include "svn_version.h" + +#include "cl.h" + +#include "private/svn_opt_private.h" +#include "private/svn_cmdline_private.h" + +#include "svn_private_config.h" + + +/*** Option Processing ***/ + +/* Add an identifier here for long options that don't have a short + option. Options that have both long and short options should just + use the short option letter as identifier. */ +typedef enum svn_cl__longopt_t { + opt_auth_password = SVN_OPT_FIRST_LONGOPT_ID, + opt_auth_username, + opt_config_dir, + opt_config_options, + opt_depth, + opt_no_auth_cache, + opt_non_interactive, + opt_stop_on_copy, + opt_strict, + opt_targets, + opt_version, + opt_with_revprop, + opt_with_all_revprops, + opt_with_no_revprops, + opt_trust_server_cert, + opt_trust_server_cert_failures, + opt_changelist +} svn_cl__longopt_t; + + +/* Option codes and descriptions for the command line client. + * + * The entire list must be terminated with an entry of nulls. + */ +const apr_getopt_option_t svn_cl__options[] = +{ + {"help", 'h', 0, N_("show help on a subcommand")}, + {NULL, '?', 0, N_("show help on a subcommand")}, + {"quiet", 'q', 0, N_("print nothing, or only summary information")}, + {"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")}, + {"non-recursive", 'N', 0, N_("obsolete; try --depth=files or --depth=immediates")}, + {"change", 'c', 1, + N_("the change made by revision ARG (like -r ARG-1:ARG)\n" + " " + "If ARG is negative this is like -r ARG:ARG-1\n" + " " + "If ARG is of the form ARG1-ARG2 then this is like\n" + " " + "ARG1:ARG2, where ARG1 is inclusive")}, + {"revision", 'r', 1, + N_("ARG (some commands also take ARG1:ARG2 range)\n" + " " + "A revision argument can be one of:\n" + " " + " NUMBER revision number\n" + " " + " '{' DATE '}' revision at start of the date\n" + " " + " 'HEAD' latest in repository\n" + " " + " 'BASE' base rev of item's working copy\n" + " " + " 'COMMITTED' last commit at or before BASE\n" + " " + " 'PREV' revision just before COMMITTED")}, + {"version", opt_version, 0, N_("show program version information")}, + {"verbose", 'v', 0, N_("print extra information")}, + {"username", opt_auth_username, 1, N_("specify a username ARG")}, + {"password", opt_auth_password, 1, N_("specify a password ARG")}, + {"targets", opt_targets, 1, + N_("pass contents of file ARG as additional args")}, + {"depth", opt_depth, 1, + N_("limit operation by depth ARG ('empty', 'files',\n" + " " + "'immediates', or 'infinity')")}, + {"strict", opt_strict, 0, N_("use strict semantics")}, + {"stop-on-copy", opt_stop_on_copy, 0, + N_("do not cross copies while traversing history")}, + {"no-auth-cache", opt_no_auth_cache, 0, + N_("do not cache authentication tokens")}, + {"trust-server-cert", opt_trust_server_cert, 0, + N_("deprecated; same as\n" + " " + "--trust-server-cert-failures=unknown-ca")}, + {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, + 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).")}, + {"non-interactive", opt_non_interactive, 0, + N_("do no interactive prompting")}, + {"config-dir", opt_config_dir, 1, + N_("read user configuration files from directory ARG")}, + {"config-option", opt_config_options, 1, + N_("set user configuration option in the format:\n" + " " + " FILE:SECTION:OPTION=[VALUE]\n" + " " + "For example:\n" + " " + " servers:global:http-library=serf")}, + {"limit", 'l', 1, N_("maximum number of log entries")}, + {"with-all-revprops", opt_with_all_revprops, 0, + N_("retrieve all revision properties")}, + {"with-no-revprops", opt_with_no_revprops, 0, + N_("retrieve no revision properties")}, + {"with-revprop", opt_with_revprop, 1, + N_("set revision property ARG in new revision\n" + " " + "using the name[=value] format")}, + {"use-merge-history", 'g', 0, + N_("use/display additional information from merge\n" + " " + "history")}, + + /* Long-opt Aliases + * + * These have NULL desriptions, but an option code that matches some + * other option (whose description should probably mention its aliases). + */ + + {0, 0, 0, 0}, +}; + + + +/*** Command dispatch. ***/ + +/* Our array of available subcommands. + * + * The entire list must be terminated with an entry of nulls. + * + * In most of the help text "PATH" is used where a working copy path is + * required, "URL" where a repository URL is required and "TARGET" when + * either a path or a url can be used. Hmm, should this be part of the + * help text? + */ + +/* Options that apply to all commands. (While not every command may + currently require authentication or be interactive, allowing every + command to take these arguments allows scripts to just pass them + willy-nilly to every invocation of 'svn') . */ +const int svn_cl__global_options[] = +{ opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive, + opt_trust_server_cert, opt_trust_server_cert_failures, + opt_config_dir, opt_config_options, 0 +}; + +const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = +{ + { "help", svn_cl__help, {"?", "h"}, N_ + ("Describe the usage of this program or its subcommands.\n" + "usage: help [SUBCOMMAND...]\n"), + {0} }, + /* This command is also invoked if we see option "--help", "-h" or "-?". */ + + { "null-blame", svn_cl__null_blame, {0}, N_ + ("Fetch all versions of a file in a batch.\n" + "usage: null-blame [-rM:N] TARGET[@REV]...\n" + "\n" + " With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the last revision at or before rN that changed or added the line,\n" + " looking back no further than rM.\n" + "\n" + " With a reverse revision range '-r M:N' where M > N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the next revision after rN that changed or deleted the line,\n" + " looking forward no further than rM.\n" + "\n" + " If specified, REV determines in which revision the target is first\n" + " looked up.\n" + "\n" + " Write the annotated result to standard output.\n"), + {'r', 'g'} }, + + { "null-export", svn_cl__null_export, {0}, N_ + ("Create an unversioned copy of a tree.\n" + "usage: null-export [-r REV] URL[@PEGREV]\n" + "\n" + " Exports a clean directory tree from the repository specified by\n" + " URL, at revision REV if it is given, otherwise at HEAD.\n" + "\n" + " If specified, PEGREV determines in which revision the target is first\n" + " looked up.\n"), + {'r', 'q', 'N', opt_depth} }, + + { "null-list", svn_cl__null_list, {"ls"}, N_ + ("List directory entries in the repository.\n" + "usage: null-list [TARGET[@REV]...]\n" + "\n" + " List each TARGET file and the contents of each TARGET directory as\n" + " they exist in the repository. If TARGET is a working copy path, the\n" + " corresponding repository URL will be used. If specified, REV determines\n" + " in which revision the target is first looked up.\n" + "\n" + " The default TARGET is '.', meaning the repository URL of the current\n" + " working directory.\n" + "\n" + " With --verbose, the following fields will be fetched for each item:\n" + "\n" + " Revision number of the last commit\n" + " Author of the last commit\n" + " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n" + " Size (in bytes)\n" + " Date and time of the last commit\n"), + {'r', 'v', 'q', 'R', opt_depth} }, + + { "null-log", svn_cl__null_log, {0}, N_ + ("Fetch the log messages for a set of revision(s) and/or path(s).\n" + "usage: 1. null-log [PATH][@REV]\n" + " 2. null-log URL[@REV] [PATH...]\n" + "\n" + " 1. Fetch the log messages for the URL corresponding to PATH\n" + " (default: '.'). If specified, REV is the revision in which the\n" + " URL is first looked up, and the default revision range is REV:1.\n" + " If REV is not specified, the default revision range is BASE:1,\n" + " since the URL might not exist in the HEAD revision.\n" + "\n" + " 2. Fetch the log messages for the PATHs (default: '.') under URL.\n" + " If specified, REV is the revision in which the URL is first\n" + " looked up, and the default revision range is REV:1; otherwise,\n" + " the URL is looked up in HEAD, and the default revision range is\n" + " HEAD:1.\n" + "\n" + " Multiple '-c' or '-r' options may be specified (but not a\n" + " combination of '-c' and '-r' options), and mixing of forward and\n" + " reverse ranges is allowed.\n" + "\n" + " With -v, also print all affected paths with each log message.\n" + " With -q, don't print the log message body itself (note that this is\n" + " compatible with -v).\n" + "\n" + " Each log message is printed just once, even if more than one of the\n" + " affected paths for that revision were explicitly requested. Logs\n" + " follow copy history by default. Use --stop-on-copy to disable this\n" + " behavior, which can be useful for determining branchpoints.\n"), + {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, + 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,}, + {{opt_with_revprop, N_("retrieve revision property ARG")}, + {'c', N_("the change made in revision ARG")}} }, + + { "null-info", svn_cl__null_info, {0}, N_ + ("Display information about a local or remote item.\n" + "usage: null-info [TARGET[@REV]...]\n" + "\n" + " Print information about each TARGET (default: '.').\n" + " TARGET may be either a working-copy path or URL. If specified, REV\n" + " determines in which revision the target is first looked up.\n"), + {'r', 'R', opt_depth, opt_targets, opt_changelist} + }, + + { NULL, NULL, {0}, NULL, {0} } +}; + + +/* Version compatibility check */ +static svn_error_t * +check_lib_versions(void) +{ + static const svn_version_checklist_t checklist[] = + { + { "svn_subr", svn_subr_version }, + { "svn_client", svn_client_version }, + { "svn_wc", svn_wc_version }, + { "svn_ra", svn_ra_version }, + { "svn_delta", svn_delta_version }, + { NULL, NULL } + }; + SVN_VERSION_DEFINE(my_version); + + return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); +} + + +/* A flag to see if we've been cancelled by the client or not. */ +static volatile sig_atomic_t cancelled = FALSE; + +/* A signal handler to support cancellation. */ +static void +signal_handler(int signum) +{ + apr_signal(signum, SIG_IGN); + cancelled = TRUE; +} + +/* Our cancellation callback. */ +svn_error_t * +svn_cl__check_cancel(void *baton) +{ + if (cancelled) + return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); + else + return SVN_NO_ERROR; +} + + +/*** Main. ***/ + +/* + * 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) +{ + svn_error_t *err; + int opt_id; + apr_getopt_t *os; + svn_cl__opt_state_t opt_state = { 0, { 0 } }; + svn_client_ctx_t *ctx; + apr_array_header_t *received_opts; + int i; + const svn_opt_subcommand_desc2_t *subcommand = NULL; + svn_cl__cmd_baton_t command_baton; + svn_auth_baton_t *ab; + svn_config_t *cfg_config; + svn_boolean_t descend = TRUE; + svn_boolean_t use_notifier = TRUE; + + received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); + + /* Check library versions */ + SVN_ERR(check_lib_versions()); + +#if defined(WIN32) || defined(__CYGWIN__) + /* Set the working copy administrative directory name. */ + if (getenv("SVN_ASP_DOT_NET_HACK")) + { + SVN_ERR(svn_wc_set_adm_dir("_svn", pool)); + } +#endif + + /* Initialize the RA library. */ + SVN_ERR(svn_ra_initialize(pool)); + + /* Begin processing arguments. */ + opt_state.start_revision.kind = svn_opt_revision_unspecified; + opt_state.end_revision.kind = svn_opt_revision_unspecified; + opt_state.revision_ranges = + apr_array_make(pool, 0, sizeof(svn_opt_revision_range_t *)); + opt_state.depth = svn_depth_unknown; + + /* No args? Show usage. */ + if (argc <= 1) + { + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* Else, parse options. */ + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + + os->interleave = 1; + while (1) + { + const char *opt_arg; + const char *utf8_opt_arg; + + /* Parse the next option. */ + apr_status_t apr_err = apr_getopt_long(os, svn_cl__options, &opt_id, + &opt_arg); + if (APR_STATUS_IS_EOF(apr_err)) + break; + else if (apr_err) + { + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* Stash the option code in an array before parsing it. */ + APR_ARRAY_PUSH(received_opts, int) = opt_id; + + switch (opt_id) { + case 'l': + { + err = svn_cstring_atoi(&opt_state.limit, opt_arg); + if (err) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Non-numeric limit argument given")); + } + if (opt_state.limit <= 0) + { + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Argument to --limit must be positive")); + } + } + break; + case 'c': + { + apr_array_header_t *change_revs = + svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, pool); + + for (i = 0; i < change_revs->nelts; i++) + { + char *end; + svn_revnum_t changeno, changeno_end; + const char *change_str = + APR_ARRAY_IDX(change_revs, i, const char *); + const char *s = change_str; + svn_boolean_t is_negative; + + /* Check for a leading minus to allow "-c -r42". + * The is_negative flag is used to handle "-c -42" and "-c -r42". + * The "-c r-42" case is handled by strtol() returning a + * negative number. */ + is_negative = (*s == '-'); + if (is_negative) + s++; + + /* Allow any number of 'r's to prefix a revision number. */ + while (*s == 'r') + s++; + changeno = changeno_end = strtol(s, &end, 10); + if (end != s && *end == '-') + { + if (changeno < 0 || is_negative) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, + NULL, + _("Negative number in range (%s)" + " not supported with -c"), + change_str); + } + s = end + 1; + while (*s == 'r') + s++; + changeno_end = strtol(s, &end, 10); + } + if (end == change_str || *end != '\0') + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Non-numeric change argument (%s) " + "given to -c"), change_str); + } + + if (changeno == 0) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("There is no change 0")); + } + + if (is_negative) + changeno = -changeno; + + /* Figure out the range: + -c N -> -r N-1:N + -c -N -> -r N:N-1 + -c M-N -> -r M-1:N for M < N + -c M-N -> -r M:N-1 for M > N + -c -M-N -> error (too confusing/no valid use case) + */ + if (changeno > 0) + { + if (changeno <= changeno_end) + changeno--; + else + changeno_end--; + } + else + { + changeno = -changeno; + changeno_end = changeno - 1; + } + + opt_state.used_change_arg = TRUE; + APR_ARRAY_PUSH(opt_state.revision_ranges, + svn_opt_revision_range_t *) + = svn_opt__revision_range_from_revnums(changeno, changeno_end, + pool); + } + } + break; + case 'r': + opt_state.used_revision_arg = TRUE; + if (svn_opt_parse_revision_to_range(opt_state.revision_ranges, + opt_arg, pool) != 0) + { + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + return svn_error_createf + (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Syntax error in revision argument '%s'"), + utf8_opt_arg); + } + break; + case 'v': + opt_state.verbose = TRUE; + break; + case 'h': + case '?': + opt_state.help = TRUE; + break; + case 'q': + opt_state.quiet = TRUE; + break; + case opt_targets: + { + svn_stringbuf_t *buffer, *buffer_utf8; + + /* We need to convert to UTF-8 now, even before we divide + the targets into an array, because otherwise we wouldn't + know what delimiter to use for svn_cstring_split(). */ + + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool)); + SVN_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool)); + opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r", + TRUE, pool); + } + break; + case 'R': + opt_state.depth = svn_depth_infinity; + break; + case 'N': + descend = FALSE; + break; + case opt_depth: + err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool); + if (err) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Error converting depth " + "from locale to UTF-8")); + opt_state.depth = svn_depth_from_word(utf8_opt_arg); + if (opt_state.depth == svn_depth_unknown + || opt_state.depth == svn_depth_exclude) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid depth; try " + "'empty', 'files', 'immediates', " + "or 'infinity'"), + utf8_opt_arg); + } + break; + case opt_version: + opt_state.version = TRUE; + break; + case opt_auth_username: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_username, + opt_arg, pool)); + break; + case opt_auth_password: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_password, + opt_arg, pool)); + break; + case opt_stop_on_copy: + opt_state.stop_on_copy = TRUE; + break; + case opt_strict: + opt_state.strict = TRUE; + break; + case opt_no_auth_cache: + opt_state.no_auth_cache = TRUE; + break; + case opt_non_interactive: + opt_state.non_interactive = TRUE; + break; + case opt_trust_server_cert: /* backwards compat to 1.8 */ + opt_state.trust_server_cert_unknown_ca = TRUE; + break; + case opt_trust_server_cert_failures: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &opt_state.trust_server_cert_unknown_ca, + &opt_state.trust_server_cert_cn_mismatch, + &opt_state.trust_server_cert_expired, + &opt_state.trust_server_cert_not_yet_valid, + &opt_state.trust_server_cert_other_failure, + utf8_opt_arg, pool)); + break; + case opt_config_dir: + { + const char *path_utf8; + SVN_ERR(svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool)); + opt_state.config_dir = svn_dirent_internal_style(path_utf8, pool); + } + break; + case opt_config_options: + if (!opt_state.config_options) + opt_state.config_options = + apr_array_make(pool, 1, + sizeof(svn_cmdline__config_argument_t*)); + + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(opt_state.config_options, + opt_arg, "svnbench: ", pool)); + break; + case opt_with_all_revprops: + /* If --with-all-revprops is specified along with one or more + * --with-revprops options, --with-all-revprops takes precedence. */ + opt_state.all_revprops = TRUE; + break; + case opt_with_no_revprops: + opt_state.no_revprops = TRUE; + break; + case opt_with_revprop: + SVN_ERR(svn_opt_parse_revprop(&opt_state.revprop_table, + opt_arg, pool)); + break; + case 'g': + opt_state.use_merge_history = TRUE; + break; + default: + /* Hmmm. Perhaps this would be a good place to squirrel away + opts that commands like svn diff might need. Hmmm indeed. */ + break; + } + } + + /* ### This really belongs in libsvn_client. The trouble is, + there's no one place there to run it from, no + svn_client_init(). We'd have to add it to all the public + functions that a client might call. It's unmaintainable to do + initialization from within libsvn_client itself, but it seems + burdensome to demand that all clients call svn_client_init() + before calling any other libsvn_client function... On the other + hand, the alternative is effectively to demand that they call + svn_config_ensure() instead, so maybe we should have a generic + init function anyway. Thoughts? */ + SVN_ERR(svn_config_ensure(opt_state.config_dir, pool)); + + /* If the user asked for help, then the rest of the arguments are + the names of subcommands to get help on (if any), or else they're + just typos/mistakes. Whatever the case, the subcommand to + actually run is svn_cl__help(). */ + if (opt_state.help) + subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, "help"); + + /* If we're not running the `help' subcommand, then look for a + subcommand in the first argument. */ + if (subcommand == NULL) + { + if (os->ind >= os->argc) + { + if (opt_state.version) + { + /* Use the "help" subcommand to handle the "--version" option. */ + static const svn_opt_subcommand_desc2_t pseudo_cmd = + { "--version", svn_cl__help, {0}, "", + {opt_version, /* must accept its own option */ + 'q', /* brief output */ + 'v', /* verbose output */ + opt_config_dir /* all commands accept this */ + } }; + + subcommand = &pseudo_cmd; + } + else + { + svn_error_clear + (svn_cmdline_fprintf(stderr, pool, + _("Subcommand argument required\n"))); + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + else + { + const char *first_arg = os->argv[os->ind++]; + subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, + first_arg); + if (subcommand == NULL) + { + const char *first_arg_utf8; + SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, + first_arg, pool)); + svn_error_clear + (svn_cmdline_fprintf(stderr, pool, + _("Unknown subcommand: '%s'\n"), + first_arg_utf8)); + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + } + + /* Check that the subcommand wasn't passed any inappropriate options. */ + for (i = 0; i < received_opts->nelts; i++) + { + opt_id = APR_ARRAY_IDX(received_opts, i, int); + + /* All commands implicitly accept --help, so just skip over this + when we see it. Note that we don't want to include this option + in their "accepted options" list because it would be awfully + redundant to display it in every commands' help text. */ + if (opt_id == 'h' || opt_id == '?') + continue; + + if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, + svn_cl__global_options)) + { + const char *optstr; + const apr_getopt_option_t *badopt = + svn_opt_get_option_from_code2(opt_id, svn_cl__options, + subcommand, pool); + svn_opt_format_option(&optstr, badopt, FALSE, pool); + if (subcommand->name[0] == '-') + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + else + svn_error_clear + (svn_cmdline_fprintf + (stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n" + "Type 'svnbench help %s' for usage.\n"), + subcommand->name, optstr, subcommand->name)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + + /* Only merge and log support multiple revisions/revision ranges. */ + if (subcommand->cmd_func != svn_cl__null_log) + { + if (opt_state.revision_ranges->nelts > 1) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments " + "encountered; can't specify -c twice, " + "or both -c and -r")); + } + } + + /* Disallow simultaneous use of both --with-all-revprops and + --with-no-revprops. */ + if (opt_state.all_revprops && opt_state.no_revprops) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-all-revprops and --with-no-revprops " + "are mutually exclusive")); + } + + /* Disallow simultaneous use of both --with-revprop and + --with-no-revprops. */ + if (opt_state.revprop_table && opt_state.no_revprops) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-revprop and --with-no-revprops " + "are mutually exclusive")); + } + + /* --trust-* options can only be used with --non-interactive */ + if (!opt_state.non_interactive) + { + if (opt_state.trust_server_cert_unknown_ca + || opt_state.trust_server_cert_cn_mismatch + || opt_state.trust_server_cert_expired + || opt_state.trust_server_cert_not_yet_valid + || opt_state.trust_server_cert_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); + } + + /* Ensure that 'revision_ranges' has at least one item, and make + 'start_revision' and 'end_revision' match that item. */ + if (opt_state.revision_ranges->nelts == 0) + { + svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range)); + range->start.kind = svn_opt_revision_unspecified; + range->end.kind = svn_opt_revision_unspecified; + APR_ARRAY_PUSH(opt_state.revision_ranges, + svn_opt_revision_range_t *) = range; + } + opt_state.start_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0, + svn_opt_revision_range_t *)->start; + opt_state.end_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0, + svn_opt_revision_range_t *)->end; + + /* Create a client context object. */ + command_baton.opt_state = &opt_state; + SVN_ERR(svn_client_create_context2(&ctx, NULL, pool)); + command_baton.ctx = ctx; + + /* Only a few commands can accept a revision range; the rest can take at + most one revision number. */ + if (subcommand->cmd_func != svn_cl__null_blame + && subcommand->cmd_func != svn_cl__null_log) + { + if (opt_state.end_revision.kind != svn_opt_revision_unspecified) + { + return svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL); + } + } + + /* -N has a different meaning depending on the command */ + if (!descend) + opt_state.depth = svn_depth_files; + + err = svn_config_get_config(&(ctx->config), + opt_state.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, "svn: "); + svn_error_clear(err); + } + else + return err; + } + + cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, + APR_HASH_KEY_STRING); + + /* Update the options in the config */ + if (opt_state.config_options) + { + svn_error_clear( + svn_cmdline__apply_config_options(ctx->config, + opt_state.config_options, + "svn: ", "--config-option")); + } + + /* Set up the notifier. + + In general, we use it any time we aren't in --quiet mode. 'svn + status' is unique, though, in that we don't want it in --quiet mode + unless we're also in --verbose mode. When in --xml mode, + though, we never want it. */ + if (opt_state.quiet) + use_notifier = FALSE; + if (use_notifier) + { + SVN_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, + pool)); + } + + /* Set up our cancellation support. */ + ctx->cancel_func = svn_cl__check_cancel; + apr_signal(SIGINT, signal_handler); +#ifdef SIGBREAK + /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ + apr_signal(SIGBREAK, signal_handler); +#endif +#ifdef SIGHUP + apr_signal(SIGHUP, signal_handler); +#endif +#ifdef SIGTERM + apr_signal(SIGTERM, signal_handler); +#endif + +#ifdef SIGPIPE + /* Disable SIGPIPE generation for the platforms that have it. */ + apr_signal(SIGPIPE, SIG_IGN); +#endif + +#ifdef SIGXFSZ + /* Disable SIGXFSZ generation for the platforms that have it, otherwise + * working with large files when compiled against an APR that doesn't have + * large file support will crash the program, which is uncool. */ + apr_signal(SIGXFSZ, SIG_IGN); +#endif + + /* Set up Authentication stuff. */ + SVN_ERR(svn_cmdline_create_auth_baton2( + &ab, + opt_state.non_interactive, + opt_state.auth_username, + opt_state.auth_password, + opt_state.config_dir, + opt_state.no_auth_cache, + opt_state.trust_server_cert_unknown_ca, + opt_state.trust_server_cert_cn_mismatch, + opt_state.trust_server_cert_expired, + opt_state.trust_server_cert_not_yet_valid, + opt_state.trust_server_cert_other_failure, + cfg_config, + ctx->cancel_func, + ctx->cancel_baton, + pool)); + + ctx->auth_baton = ab; + + /* The new svn behavior is to postpone everything until after the operation + completed */ + ctx->conflict_func = NULL; + ctx->conflict_baton = NULL; + ctx->conflict_func2 = NULL; + ctx->conflict_baton2 = NULL; + + /* And now we finally run the subcommand. */ + err = (*subcommand->cmd_func)(os, &command_baton, pool); + if (err) + { + /* For argument-related problems, suggest using the 'help' + subcommand. */ + if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS + || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR) + { + err = svn_error_quick_wrapf( + err, _("Try 'svnbench help %s' for more information"), + subcommand->name); + } + if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) + { + err = svn_error_quick_wrap(err, + _("Please see the 'svn upgrade' command")); + } + + /* Tell the user about 'svn cleanup' if any error on the stack + was about locked working copies. */ + if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED)) + { + err = svn_error_quick_wrap( + err, _("Run 'svn cleanup' to remove locks " + "(type 'svn help cleanup' for details)")); + } + + return err; + } + + 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("svnbench", 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, "svnbench: "); + } + + svn_pool_destroy(pool); + return exit_code; +} |