summaryrefslogtreecommitdiff
path: root/subversion/svnbench
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svnbench')
-rw-r--r--subversion/svnbench/cl.h203
-rw-r--r--subversion/svnbench/help-cmd.c90
-rw-r--r--subversion/svnbench/notify.c1045
-rw-r--r--subversion/svnbench/null-blame-cmd.c276
-rw-r--r--subversion/svnbench/null-export-cmd.c346
-rw-r--r--subversion/svnbench/null-info-cmd.c287
-rw-r--r--subversion/svnbench/null-list-cmd.c169
-rw-r--r--subversion/svnbench/null-log-cmd.c243
-rw-r--r--subversion/svnbench/svnbench.c1005
-rw-r--r--subversion/svnbench/util.c92
10 files changed, 3756 insertions, 0 deletions
diff --git a/subversion/svnbench/cl.h b/subversion/svnbench/cl.h
new file mode 100644
index 0000000..5b15fe5
--- /dev/null
+++ b/subversion/svnbench/cl.h
@@ -0,0 +1,203 @@
+/*
+ * cl.h: shared stuff in the command line program
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+#ifndef SVN_CL_H
+#define SVN_CL_H
+
+/*** Includes. ***/
+
+#include <apr_tables.h>
+
+#include "svn_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*** Command dispatch. ***/
+
+/* Hold results of option processing that are shared by multiple
+ commands. */
+typedef struct svn_cl__opt_state_t
+{
+ /* An array of svn_opt_revision_range_t *'s representing revisions
+ ranges indicated on the command-line via the -r and -c options.
+ For each range in the list, if only one revision was provided
+ (-rN), its 'end' member remains 'svn_opt_revision_unspecified'.
+ This array always has at least one element, even if that is a
+ null range in which both ends are 'svn_opt_revision_unspecified'. */
+ apr_array_header_t *revision_ranges;
+
+ /* These are simply a copy of the range start and end values present
+ in the first item of the revision_ranges list. */
+ svn_opt_revision_t start_revision;
+ svn_opt_revision_t end_revision;
+
+ /* Flag which is only set if the '-c' option was used. */
+ svn_boolean_t used_change_arg;
+
+ /* Flag which is only set if the '-r' option was used. */
+ svn_boolean_t used_revision_arg;
+
+ /* Max number of log messages to get back from svn_client_log2. */
+ int limit;
+
+ /* After option processing is done, reflects the switch actually
+ given on the command line, or svn_depth_unknown if none. */
+ svn_depth_t depth;
+
+ svn_boolean_t quiet; /* sssh...avoid unnecessary output */
+ svn_boolean_t non_interactive; /* do no interactive prompting */
+ svn_boolean_t version; /* print version information */
+ svn_boolean_t verbose; /* be verbose */
+ svn_boolean_t strict; /* do strictly what was requested */
+ const char *encoding; /* the locale/encoding of the data*/
+ svn_boolean_t help; /* print usage message */
+ const char *auth_username; /* auth username */ /* UTF-8! */
+ const char *auth_password; /* auth password */ /* UTF-8! */
+ apr_array_header_t *targets; /* target list from file */ /* UTF-8! */
+ svn_boolean_t no_auth_cache; /* do not cache authentication information */
+ svn_boolean_t stop_on_copy; /* don't cross copies during processing */
+ const char *config_dir; /* over-riding configuration directory */
+ apr_array_header_t *config_options; /* over-riding configuration options */
+ svn_boolean_t all_revprops; /* retrieve all revprops */
+ svn_boolean_t no_revprops; /* retrieve no revprops */
+ apr_hash_t *revprop_table; /* table of revision properties to get/set */
+ svn_boolean_t use_merge_history; /* use/display extra merge information */
+ /* trust server SSL certs that would otherwise be rejected as "untrusted" */
+ svn_boolean_t trust_server_cert_unknown_ca;
+ svn_boolean_t trust_server_cert_cn_mismatch;
+ svn_boolean_t trust_server_cert_expired;
+ svn_boolean_t trust_server_cert_not_yet_valid;
+ svn_boolean_t trust_server_cert_other_failure;
+} svn_cl__opt_state_t;
+
+
+typedef struct svn_cl__cmd_baton_t
+{
+ svn_cl__opt_state_t *opt_state;
+ svn_client_ctx_t *ctx;
+} svn_cl__cmd_baton_t;
+
+
+/* Declare all the command procedures */
+svn_opt_subcommand_t
+ svn_cl__help,
+ svn_cl__null_blame,
+ svn_cl__null_export,
+ svn_cl__null_list,
+ svn_cl__null_log,
+ svn_cl__null_info;
+
+
+/* See definition in main.c for documentation. */
+extern const svn_opt_subcommand_desc2_t svn_cl__cmd_table[];
+
+/* See definition in main.c for documentation. */
+extern const int svn_cl__global_options[];
+
+/* See definition in main.c for documentation. */
+extern const apr_getopt_option_t svn_cl__options[];
+
+
+/* A helper for the many subcommands that wish to merely warn when
+ * invoked on an unversioned, nonexistent, or otherwise innocuously
+ * errorful resource. Meant to be wrapped with SVN_ERR().
+ *
+ * If ERR is null, return SVN_NO_ERROR.
+ *
+ * Else if ERR->apr_err is one of the error codes supplied in varargs,
+ * then handle ERR as a warning (unless QUIET is true), clear ERR, and
+ * return SVN_NO_ERROR, and push the value of ERR->apr_err into the
+ * ERRORS_SEEN array, if ERRORS_SEEN is not NULL.
+ *
+ * Else return ERR.
+ *
+ * Typically, error codes like SVN_ERR_UNVERSIONED_RESOURCE,
+ * SVN_ERR_ENTRY_NOT_FOUND, etc, are supplied in varargs. Don't
+ * forget to terminate the argument list with 0 (or APR_SUCCESS).
+ */
+svn_error_t *
+svn_cl__try(svn_error_t *err,
+ apr_array_header_t *errors_seen,
+ svn_boolean_t quiet,
+ ...);
+
+
+/* Our cancellation callback. */
+svn_error_t *
+svn_cl__check_cancel(void *baton);
+
+
+
+/*** Notification functions to display results on the terminal. */
+
+/* Set *NOTIFY_FUNC_P and *NOTIFY_BATON_P to a notifier/baton for all
+ * operations, allocated in POOL.
+ */
+svn_error_t *
+svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
+ void **notify_baton_p,
+ apr_pool_t *pool);
+
+/* Make the notifier for use with BATON print the appropriate summary
+ * line at the end of the output.
+ */
+svn_error_t *
+svn_cl__notifier_mark_export(void *baton);
+
+/* Like svn_client_args_to_target_array() but, if the only error is that some
+ * arguments are reserved file names, then print warning messages for those
+ * targets, store the rest of the targets in TARGETS_P and return success. */
+svn_error_t *
+svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets_p,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t keep_dest_origpath_on_truepath_collision,
+ apr_pool_t *pool);
+
+/* Return an error if TARGET is a URL; otherwise return SVN_NO_ERROR. */
+svn_error_t *
+svn_cl__check_target_is_local_path(const char *target);
+
+/* Return a copy of PATH, converted to the local path style, skipping
+ * PARENT_PATH if it is non-null and is a parent of or equal to PATH.
+ *
+ * This function assumes PARENT_PATH and PATH are both absolute "dirents"
+ * or both relative "dirents". */
+const char *
+svn_cl__local_style_skip_ancestor(const char *parent_path,
+ const char *path,
+ apr_pool_t *pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_CL_H */
diff --git a/subversion/svnbench/help-cmd.c b/subversion/svnbench/help-cmd.c
new file mode 100644
index 0000000..ead4861
--- /dev/null
+++ b/subversion/svnbench/help-cmd.c
@@ -0,0 +1,90 @@
+/*
+ * help-cmd.c -- Provide help
+ *
+ * ====================================================================
+ * 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 "svn_string.h"
+#include "svn_error.h"
+#include "svn_version.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__help(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state;
+
+ char help_header[] =
+ N_("usage: svnbench <subcommand> [options] [args]\n"
+ "Subversion benchmarking tool.\n"
+ "Type 'svnbench help <subcommand>' for help on a specific subcommand.\n"
+ "Type 'svnbench --version' to see the program version and RA modules\n"
+ " or 'svnbench --version --quiet' to see just the version number.\n"
+ "\n"
+ "Most subcommands take file and/or directory arguments, recursing\n"
+ "on the directories. If no arguments are supplied to such a\n"
+ "command, it recurses on the current directory (inclusive) by default.\n"
+ "\n"
+ "Available subcommands:\n");
+
+ char help_footer[] =
+ N_("Subversion is a tool for version control.\n"
+ "For additional information, see http://subversion.apache.org/\n");
+
+ const char *ra_desc_start
+ = _("The following repository access (RA) modules are available:\n\n");
+
+ svn_stringbuf_t *version_footer;
+
+ if (baton)
+ opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ else
+ opt_state = NULL;
+
+ version_footer = svn_stringbuf_create(ra_desc_start, pool);
+ SVN_ERR(svn_ra_print_modules(version_footer, pool));
+
+ return svn_opt_print_help4(os,
+ "svnbench", /* ### erm, derive somehow? */
+ opt_state ? opt_state->version : FALSE,
+ opt_state ? opt_state->quiet : FALSE,
+ opt_state ? opt_state->verbose : FALSE,
+ version_footer->data,
+ help_header, /* already gettext()'d */
+ svn_cl__cmd_table,
+ svn_cl__options,
+ svn_cl__global_options,
+ _(help_footer),
+ pool);
+}
diff --git a/subversion/svnbench/notify.c b/subversion/svnbench/notify.c
new file mode 100644
index 0000000..b8ca2ef
--- /dev/null
+++ b/subversion/svnbench/notify.c
@@ -0,0 +1,1045 @@
+/*
+ * notify.c: feedback handlers for cmdline 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. ***/
+
+#define APR_WANT_STDIO
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include "svn_cmdline.h"
+#include "svn_pools.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_sorts.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/* Baton for notify and friends. */
+struct notify_baton
+{
+ svn_boolean_t received_some_change;
+ svn_boolean_t is_checkout;
+ svn_boolean_t is_export;
+ svn_boolean_t is_wc_to_repos_copy;
+ svn_boolean_t sent_first_txdelta;
+ svn_boolean_t in_external;
+ svn_boolean_t had_print_error; /* Used to not keep printing error messages
+ when we've already had one print error. */
+
+ /* Conflict stats for update and merge. */
+ unsigned int text_conflicts;
+ unsigned int prop_conflicts;
+ unsigned int tree_conflicts;
+ unsigned int skipped_paths;
+ apr_hash_t *conflicted_paths;
+
+ /* The cwd, for use in decomposing absolute paths. */
+ const char *path_prefix;
+};
+
+
+/* Add a conflicted path to the list of conflicted paths stored
+ * in the notify baton. */
+static void
+add_conflicted_path(struct notify_baton *nb, const char *path)
+{
+ apr_hash_set(nb->conflicted_paths,
+ apr_pstrdup(apr_hash_pool_get(nb->conflicted_paths), path),
+ APR_HASH_KEY_STRING, "");
+}
+
+/* This implements `svn_wc_notify_func2_t'.
+ * NOTE: This function can't fail, so we just ignore any print errors. */
+static void
+notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool)
+{
+ struct notify_baton *nb = baton;
+ char statchar_buf[5] = " ";
+ const char *path_local;
+ svn_error_t *err;
+
+ if (n->url)
+ path_local = n->url;
+ else
+ {
+ if (n->path_prefix)
+ path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path,
+ pool);
+ else /* skip nb->path_prefix, if it's non-null */
+ path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path,
+ pool);
+ }
+
+ switch (n->action)
+ {
+ case svn_wc_notify_skip:
+ nb->skipped_paths++;
+ if (n->content_state == svn_wc_notify_state_missing)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped missing target: '%s'\n"),
+ path_local)))
+ goto print_error;
+ }
+ else if (n->content_state == svn_wc_notify_state_source_missing)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped target: '%s' -- copy-source is missing\n"),
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped '%s'\n"), path_local)))
+ goto print_error;
+ }
+ break;
+ case svn_wc_notify_update_skip_obstruction:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- An obstructing working copy was found\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_skip_working_only:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Has no versioned parent\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_skip_access_denied:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Access denied\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_skip_conflicted:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Node remains in conflict\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_delete:
+ case svn_wc_notify_exclude:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "D %s\n", path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_broken_lock:
+ if ((err = svn_cmdline_printf(pool, "B %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_external_removed:
+ nb->received_some_change = TRUE;
+ if (n->err && n->err->message)
+ {
+ if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n",
+ path_local, n->err->message)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n",
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_left_local_modifications:
+ if ((err = svn_cmdline_printf(pool, "Left local modifications as '%s'\n",
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_replace:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "R %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_add:
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ add_conflicted_path(nb, n->path);
+ if ((err = svn_cmdline_printf(pool, "C %s\n", path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "A %s\n", path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_exists:
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[0] = 'C';
+ }
+ else
+ statchar_buf[0] = 'E';
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_merged)
+ statchar_buf[1] = 'G';
+
+ if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_restore:
+ if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revert:
+ if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_revert:
+ if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- "
+ "try updating instead.\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_resolved:
+ if ((err = svn_cmdline_printf(pool,
+ _("Resolved conflicted state of '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_add:
+ /* We *should* only get the MIME_TYPE if PATH is a file. If we
+ do get it, and the mime-type is not textual, note that this
+ is a binary addition. */
+ if (n->mime_type && (svn_mime_type_is_binary(n->mime_type)))
+ {
+ if ((err = svn_cmdline_printf(pool, "A (bin) %s\n",
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "A %s\n",
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_delete:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "D %s\n",
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_patch:
+ {
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[0] = 'C';
+ }
+ else if (n->kind == svn_node_file)
+ {
+ if (n->content_state == svn_wc_notify_state_merged)
+ statchar_buf[0] = 'G';
+ else if (n->content_state == svn_wc_notify_state_changed)
+ statchar_buf[0] = 'U';
+ }
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_changed)
+ statchar_buf[1] = 'U';
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
+ {
+ if ((err = svn_cmdline_printf(pool, "%s %s\n",
+ statchar_buf, path_local)))
+ goto print_error;
+ }
+ }
+ break;
+
+ case svn_wc_notify_patch_applied_hunk:
+ nb->received_some_change = TRUE;
+ if (n->hunk_original_start != n->hunk_matched_line)
+ {
+ apr_uint64_t off;
+ const char *s;
+ const char *minus;
+
+ if (n->hunk_matched_line > n->hunk_original_start)
+ {
+ off = n->hunk_matched_line - n->hunk_original_start;
+ minus = "";
+ }
+ else
+ {
+ off = n->hunk_original_start - n->hunk_matched_line;
+ minus = "-";
+ }
+
+ /* ### We're creating the localized strings without
+ * ### APR_INT64_T_FMT since it isn't translator-friendly */
+ if (n->hunk_fuzz)
+ {
+
+ if (n->prop_name)
+ {
+ s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with offset %s");
+
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT
+ " and fuzz %lu (%s)\n",
+ SVN_VA_NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->hunk_fuzz,
+ n->prop_name);
+ }
+ else
+ {
+ s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with offset %s");
+
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT
+ " and fuzz %lu\n",
+ SVN_VA_NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->hunk_fuzz);
+ }
+
+ if (err)
+ goto print_error;
+ }
+ else
+ {
+
+ if (n->prop_name)
+ {
+ s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with offset %s");
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT" (%s)\n",
+ SVN_VA_NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->prop_name);
+ }
+ else
+ {
+ s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with offset %s");
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT"\n",
+ SVN_VA_NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off);
+ }
+
+ if (err)
+ goto print_error;
+ }
+ }
+ else if (n->hunk_fuzz)
+ {
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with fuzz %lu (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->hunk_fuzz,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with fuzz %lu\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->hunk_fuzz);
+ if (err)
+ goto print_error;
+
+ }
+ break;
+
+ case svn_wc_notify_patch_rejected_hunk:
+ nb->received_some_change = TRUE;
+
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> rejected hunk "
+ "## -%lu,%lu +%lu,%lu ## (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> rejected hunk "
+ "@@ -%lu,%lu +%lu,%lu @@\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_patch_hunk_already_applied:
+ nb->received_some_change = TRUE;
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> hunk "
+ "## -%lu,%lu +%lu,%lu ## "
+ "already applied (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> hunk "
+ "@@ -%lu,%lu +%lu,%lu @@ "
+ "already applied\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_update:
+ case svn_wc_notify_merge_record_info:
+ {
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[0] = 'C';
+ }
+ else if (n->kind == svn_node_file)
+ {
+ if (n->content_state == svn_wc_notify_state_merged)
+ statchar_buf[0] = 'G';
+ else if (n->content_state == svn_wc_notify_state_changed)
+ statchar_buf[0] = 'U';
+ }
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ add_conflicted_path(nb, n->path);
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_merged)
+ statchar_buf[1] = 'G';
+ else if (n->prop_state == svn_wc_notify_state_changed)
+ statchar_buf[1] = 'U';
+
+ if (n->lock_state == svn_wc_notify_lock_state_unlocked)
+ statchar_buf[2] = 'B';
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
+ nb->received_some_change = TRUE;
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' '
+ || statchar_buf[2] != ' ')
+ {
+ if ((err = svn_cmdline_printf(pool, "%s %s\n",
+ statchar_buf, path_local)))
+ goto print_error;
+ }
+ }
+ break;
+
+ case svn_wc_notify_update_external:
+ /* Remember that we're now "inside" an externals definition. */
+ nb->in_external = TRUE;
+
+ /* Currently this is used for checkouts and switches too. If we
+ want different output, we'll have to add new actions. */
+ if ((err = svn_cmdline_printf(pool,
+ _("\nFetching external item into '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_external:
+ /* If we are currently inside the handling of an externals
+ definition, then we can simply present n->err as a warning
+ and feel confident that after this, we aren't handling that
+ externals definition any longer. */
+ if (nb->in_external)
+ {
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ nb->in_external = FALSE;
+ if ((err = svn_cmdline_printf(pool, "\n")))
+ goto print_error;
+ }
+ /* Otherwise, we'll just print two warnings. Why? Because
+ svn_handle_warning2() only shows the single "best message",
+ but we have two pretty important ones: that the external at
+ '/some/path' didn't pan out, and then the more specific
+ reason why (from n->err). */
+ else
+ {
+ svn_error_t *warn_err =
+ svn_error_createf(SVN_ERR_BASE, NULL,
+ _("Error handling externals definition for '%s':"),
+ path_local);
+ svn_handle_warning2(stderr, warn_err, "svn: ");
+ svn_error_clear(warn_err);
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ }
+ break;
+
+ case svn_wc_notify_update_started:
+ if (! (nb->in_external ||
+ nb->is_checkout ||
+ nb->is_export))
+ {
+ if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"),
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_update_completed:
+ {
+ if (SVN_IS_VALID_REVNUM(n->revision))
+ {
+ if (nb->is_export)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Exported external at revision %ld.\n")
+ : _("Exported revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else if (nb->is_checkout)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Checked out external at revision %ld.\n")
+ : _("Checked out revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else
+ {
+ if (nb->received_some_change)
+ {
+ nb->received_some_change = FALSE;
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Updated external to revision %ld.\n")
+ : _("Updated to revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External at revision %ld.\n")
+ : _("At revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ }
+ }
+ else /* no revision */
+ {
+ if (nb->is_export)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External export complete.\n")
+ : _("Export complete.\n"))))
+ goto print_error;
+ }
+ else if (nb->is_checkout)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External checkout complete.\n")
+ : _("Checkout complete.\n"))))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External update complete.\n")
+ : _("Update complete.\n"))))
+ goto print_error;
+ }
+ }
+ }
+
+ if (nb->in_external)
+ {
+ nb->in_external = FALSE;
+ if ((err = svn_cmdline_printf(pool, "\n")))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_status_external:
+ if ((err = svn_cmdline_printf
+ (pool, _("\nPerforming status on external item at '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_status_completed:
+ if (SVN_IS_VALID_REVNUM(n->revision))
+ if ((err = svn_cmdline_printf(pool,
+ _("Status against revision: %6ld\n"),
+ n->revision)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_modified:
+ /* xgettext: Align the %s's on this and the following 4 messages */
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Sending copy of %s\n")
+ : _("Sending %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_added:
+ case svn_wc_notify_commit_copied:
+ if (n->mime_type && svn_mime_type_is_binary(n->mime_type))
+ {
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Adding copy of (bin) %s\n")
+ : _("Adding (bin) %s\n"),
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Adding copy of %s\n")
+ : _("Adding %s\n"),
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_commit_deleted:
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Deleting copy of %s\n")
+ : _("Deleting %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_replaced:
+ case svn_wc_notify_commit_copied_replaced:
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Replacing copy of %s\n")
+ : _("Replacing %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_postfix_txdelta:
+ if (! nb->sent_first_txdelta)
+ {
+ nb->sent_first_txdelta = TRUE;
+ if ((err = svn_cmdline_printf(pool,
+ _("Transmitting file data "))))
+ goto print_error;
+ }
+
+ if ((err = svn_cmdline_printf(pool, ".")))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_locked:
+ if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
+ path_local, n->lock->owner)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_unlocked:
+ if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_lock:
+ case svn_wc_notify_failed_unlock:
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ break;
+
+ case svn_wc_notify_changelist_set:
+ if ((err = svn_cmdline_printf(pool, "A [%s] %s\n",
+ n->changelist_name, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_changelist_clear:
+ case svn_wc_notify_changelist_moved:
+ if ((err = svn_cmdline_printf(pool,
+ "D [%s] %s\n",
+ n->changelist_name, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_begin:
+ if (n->merge_range == NULL)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging differences between "
+ "repository URLs into '%s':\n"),
+ path_local);
+ else if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging r%ld through r%ld into "
+ "'%s':\n"),
+ n->merge_range->start + 1,
+ n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging r%ld through r%ld "
+ "into '%s':\n"),
+ n->merge_range->start,
+ n->merge_range->end + 1, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_record_info_begin:
+ if (!n->merge_range)
+ {
+ err = svn_cmdline_printf(pool,
+ _("--- Recording mergeinfo for merge "
+ "between repository URLs into '%s':\n"),
+ path_local);
+ }
+ else
+ {
+ if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for merge of r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"),
+ n->merge_range->start + 1, n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"),
+ n->merge_range->start, n->merge_range->end + 1, path_local);
+ }
+
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_elide_info:
+ if ((err = svn_cmdline_printf(pool,
+ _("--- Eliding mergeinfo from '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_foreign_merge_begin:
+ if (n->merge_range == NULL)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging differences between "
+ "foreign repository URLs into '%s':\n"),
+ path_local);
+ else if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging (from foreign repository) "
+ "r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging (from foreign "
+ "repository) r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging (from foreign repository) "
+ "r%ld through r%ld into '%s':\n"),
+ n->merge_range->start + 1,
+ n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging (from foreign "
+ "repository) r%ld through r%ld into "
+ "'%s':\n"),
+ n->merge_range->start,
+ n->merge_range->end + 1, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_tree_conflict:
+ nb->tree_conflicts++;
+ add_conflicted_path(nb, n->path);
+ if ((err = svn_cmdline_printf(pool, " C %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_add:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " A %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_update:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " U %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_delete:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " D %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_modified:
+ case svn_wc_notify_property_added:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' set on '%s'\n"),
+ n->prop_name, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_deleted:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' deleted from '%s'.\n"),
+ n->prop_name, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_deleted_nonexistent:
+ err = svn_cmdline_printf(pool,
+ _("Attempting to delete nonexistent "
+ "property '%s' on '%s'\n"), n->prop_name,
+ path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revprop_set:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' set on repository revision %ld\n"),
+ n->prop_name, n->revision);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revprop_deleted:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' deleted from repository revision %ld\n"),
+ n->prop_name, n->revision);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_upgraded_path:
+ err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_url_redirect:
+ err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"),
+ n->url);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_path_nonexistent:
+ err = svn_cmdline_printf(pool, _("'%s' is not under version control"),
+ path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_conflict_resolver_starting:
+ /* Once all operations invoke the interactive conflict resolution after
+ * they've completed, we can run svn_cl__print_conflict_stats() here. */
+ break;
+
+ case svn_wc_notify_conflict_resolver_done:
+ break;
+
+ default:
+ break;
+ }
+
+ if ((err = svn_cmdline_fflush(stdout)))
+ goto print_error;
+
+ return;
+
+ print_error:
+ /* If we had no errors before, print this error to stderr. Else, don't print
+ anything. The user already knows there were some output errors,
+ so there is no point in flooding her with an error per notification. */
+ if (!nb->had_print_error)
+ {
+ nb->had_print_error = TRUE;
+ /* Issue #3014:
+ * Don't print anything on broken pipes. The pipe was likely
+ * closed by the process at the other end. We expect that
+ * process to perform error reporting as necessary.
+ *
+ * ### This assumes that there is only one error in a chain for
+ * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+ if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+ svn_handle_error2(err, stderr, FALSE, "svn: ");
+ }
+ svn_error_clear(err);
+}
+
+
+svn_error_t *
+svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
+ void **notify_baton_p,
+ apr_pool_t *pool)
+{
+ struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb));
+
+ nb->received_some_change = FALSE;
+ nb->sent_first_txdelta = FALSE;
+ nb->is_checkout = FALSE;
+ nb->is_export = FALSE;
+ nb->is_wc_to_repos_copy = FALSE;
+ nb->in_external = FALSE;
+ nb->had_print_error = FALSE;
+ nb->text_conflicts = 0;
+ nb->prop_conflicts = 0;
+ nb->tree_conflicts = 0;
+ nb->skipped_paths = 0;
+ nb->conflicted_paths = apr_hash_make(pool);
+ SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
+
+ *notify_func_p = notify;
+ *notify_baton_p = nb;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__notifier_mark_export(void *baton)
+{
+ struct notify_baton *nb = baton;
+
+ nb->is_export = TRUE;
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnbench/null-blame-cmd.c b/subversion/svnbench/null-blame-cmd.c
new file mode 100644
index 0000000..74d87dc
--- /dev/null
+++ b/subversion/svnbench/null-blame-cmd.c
@@ -0,0 +1,276 @@
+/*
+ * null-blame-cmd.c -- Subversion export command
+ *
+ * ====================================================================
+ * 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 "svn_client.h"
+#include "svn_cmdline.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "svn_sorts.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_string_private.h"
+#include "private/svn_client_private.h"
+
+struct file_rev_baton {
+ apr_int64_t byte_count;
+ apr_int64_t delta_count;
+ apr_int64_t rev_count;
+};
+
+/* Implements svn_txdelta_window_handler_t */
+static svn_error_t *
+delta_handler(svn_txdelta_window_t *window, void *baton)
+{
+ struct file_rev_baton *frb = baton;
+
+ if (window != NULL)
+ frb->byte_count += window->tview_len;
+
+ return SVN_NO_ERROR;
+}
+
+/* Implementes svn_file_rev_handler_t */
+static svn_error_t *
+file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
+ apr_hash_t *rev_props,
+ svn_boolean_t merged_revision,
+ svn_txdelta_window_handler_t *content_delta_handler,
+ void **content_delta_baton,
+ apr_array_header_t *prop_diffs,
+ apr_pool_t *pool)
+{
+ struct file_rev_baton *frb = baton;
+
+ frb->rev_count++;
+
+ if (content_delta_handler)
+ {
+ *content_delta_handler = delta_handler;
+ *content_delta_baton = baton;
+ frb->delta_count++;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+bench_null_blame(const char *target,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *start,
+ const svn_opt_revision_t *end,
+ svn_boolean_t include_merged_revisions,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ struct file_rev_baton frb = { 0, 0, 0};
+ svn_ra_session_t *ra_session;
+ svn_revnum_t start_revnum, end_revnum;
+ svn_boolean_t backwards;
+ const char *target_abspath_or_url;
+
+ if (start->kind == svn_opt_revision_unspecified
+ || end->kind == svn_opt_revision_unspecified)
+ return svn_error_create
+ (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
+
+ if (svn_path_is_url(target))
+ target_abspath_or_url = target;
+ else
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
+
+
+ /* Get an RA plugin for this filesystem object. */
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL,
+ target, NULL, peg_revision,
+ peg_revision,
+ ctx, pool));
+
+ SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
+ target_abspath_or_url, ra_session,
+ start, pool));
+
+ SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx,
+ target_abspath_or_url, ra_session,
+ end, pool));
+
+ {
+ svn_client__pathrev_t *loc;
+ svn_opt_revision_t younger_end;
+ younger_end.kind = svn_opt_revision_number;
+ younger_end.value.number = MAX(start_revnum, end_revnum);
+
+ SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session,
+ target, peg_revision,
+ &younger_end,
+ ctx, pool));
+
+ /* Make the session point to the real URL. */
+ SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool));
+ }
+
+ backwards = (start_revnum > end_revnum);
+
+ /* Collect all blame information.
+ We need to ensure that we get one revision before the start_rev,
+ if available so that we can know what was actually changed in the start
+ revision. */
+ SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
+ backwards ? start_revnum
+ : MAX(0, start_revnum-1),
+ end_revnum,
+ include_merged_revisions,
+ file_rev_handler, &frb, pool));
+
+ if (!quiet)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("%15s revisions\n"
+ "%15s deltas\n"
+ "%15s bytes in deltas\n"),
+ svn__ui64toa_sep(frb.rev_count, ',', pool),
+ svn__ui64toa_sep(frb.delta_count, ',', pool),
+ svn__ui64toa_sep(frb.byte_count, ',', pool)));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__null_blame(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ apr_pool_t *iterpool;
+ apr_array_header_t *targets;
+ int i;
+ svn_boolean_t end_revision_unspecified = FALSE;
+ svn_boolean_t seen_nonexistent_target = FALSE;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Blame needs a file on which to operate. */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+ {
+ if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ /* In the case that -rX was specified, we actually want to set the
+ range to be -r1:X. */
+
+ opt_state->end_revision = opt_state->start_revision;
+ opt_state->start_revision.kind = svn_opt_revision_number;
+ opt_state->start_revision.value.number = 1;
+ }
+ else
+ end_revision_unspecified = TRUE;
+ }
+
+ if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
+ {
+ opt_state->start_revision.kind = svn_opt_revision_number;
+ opt_state->start_revision.value.number = 1;
+ }
+
+ /* The final conclusion from issue #2431 is that blame info
+ is client output (unlike 'svn cat' which plainly cats the file),
+ so the EOL style should be the platform local one.
+ */
+ iterpool = svn_pool_create(pool);
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ svn_error_t *err;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *parsed_path;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Check for a peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target,
+ iterpool));
+
+ if (end_revision_unspecified)
+ {
+ if (peg_revision.kind != svn_opt_revision_unspecified)
+ opt_state->end_revision = peg_revision;
+ else if (svn_path_is_url(target))
+ opt_state->end_revision.kind = svn_opt_revision_head;
+ else
+ opt_state->end_revision.kind = svn_opt_revision_working;
+ }
+
+ err = bench_null_blame(parsed_path,
+ &peg_revision,
+ &opt_state->start_revision,
+ &opt_state->end_revision,
+ opt_state->use_merge_history,
+ opt_state->quiet,
+ ctx,
+ iterpool);
+
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+ err->apr_err == SVN_ERR_ENTRY_NOT_FOUND ||
+ err->apr_err == SVN_ERR_FS_NOT_FILE ||
+ err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ err = NULL;
+ seen_nonexistent_target = TRUE;
+ }
+ else
+ {
+ return svn_error_trace(err);
+ }
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ if (seen_nonexistent_target)
+ return svn_error_create(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Could not perform blame on all targets because some "
+ "targets don't exist"));
+ else
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnbench/null-export-cmd.c b/subversion/svnbench/null-export-cmd.c
new file mode 100644
index 0000000..8220bfb
--- /dev/null
+++ b/subversion/svnbench/null-export-cmd.c
@@ -0,0 +1,346 @@
+/*
+ * export-cmd.c -- Subversion export command
+ *
+ * ====================================================================
+ * 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 "svn_client.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_cmdline.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_string_private.h"
+#include "private/svn_client_private.h"
+
+/*** The export editor code. ***/
+
+/* ---------------------------------------------------------------------- */
+
+/*** A dedicated 'export' editor, which does no .svn/ accounting. ***/
+
+typedef struct edit_baton_t
+{
+ apr_int64_t file_count;
+ apr_int64_t dir_count;
+ apr_int64_t byte_count;
+ apr_int64_t prop_count;
+ apr_int64_t prop_byte_count;
+} edit_baton_t;
+
+static svn_error_t *
+set_target_revision(void *edit_baton,
+ svn_revnum_t target_revision,
+ apr_pool_t *pool)
+{
+ return SVN_NO_ERROR;
+}
+
+
+/* Just ensure that the main export directory exists. */
+static svn_error_t *
+open_root(void *edit_baton,
+ svn_revnum_t base_revision,
+ apr_pool_t *pool,
+ void **root_baton)
+{
+ *root_baton = edit_baton;
+ return SVN_NO_ERROR;
+}
+
+
+/* Ensure the directory exists, and send feedback. */
+static svn_error_t *
+add_directory(const char *path,
+ void *parent_baton,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ apr_pool_t *pool,
+ void **baton)
+{
+ edit_baton_t *eb = parent_baton;
+ eb->dir_count++;
+
+ *baton = parent_baton;
+ return SVN_NO_ERROR;
+}
+
+
+/* Build a file baton. */
+static svn_error_t *
+add_file(const char *path,
+ void *parent_baton,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ apr_pool_t *pool,
+ void **baton)
+{
+ edit_baton_t *eb = parent_baton;
+ eb->file_count++;
+
+ *baton = parent_baton;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+window_handler(svn_txdelta_window_t *window, void *baton)
+{
+ edit_baton_t *eb = baton;
+ if (window != NULL)
+ eb->byte_count += window->tview_len;
+
+ return SVN_NO_ERROR;
+}
+
+/* Write incoming data into the tmpfile stream */
+
+static svn_error_t *
+apply_textdelta(void *file_baton,
+ const char *base_checksum,
+ apr_pool_t *pool,
+ svn_txdelta_window_handler_t *handler,
+ void **handler_baton)
+{
+ *handler_baton = file_baton;
+ *handler = window_handler;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_file_prop(void *file_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ edit_baton_t *eb = file_baton;
+ eb->prop_count++;
+ eb->prop_byte_count += value->len;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_dir_prop(void *dir_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ edit_baton_t *eb = dir_baton;
+ eb->prop_count++;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_file(void *file_baton,
+ const char *text_checksum,
+ apr_pool_t *pool)
+{
+ return SVN_NO_ERROR;
+}
+
+
+/*** Public Interfaces ***/
+
+static svn_error_t *
+bench_null_export(svn_revnum_t *result_rev,
+ const char *from_path_or_url,
+ svn_opt_revision_t *peg_revision,
+ svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t quiet,
+ apr_pool_t *pool)
+{
+ svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
+ svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
+
+ SVN_ERR_ASSERT(peg_revision != NULL);
+ SVN_ERR_ASSERT(revision != NULL);
+
+ if (peg_revision->kind == svn_opt_revision_unspecified)
+ peg_revision->kind = svn_path_is_url(from_path_or_url)
+ ? svn_opt_revision_head
+ : svn_opt_revision_working;
+
+ if (revision->kind == svn_opt_revision_unspecified)
+ revision = peg_revision;
+
+ if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
+ {
+ svn_client__pathrev_t *loc;
+ svn_ra_session_t *ra_session;
+ svn_node_kind_t kind;
+
+ /* Get the RA connection. */
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
+ from_path_or_url, NULL,
+ peg_revision,
+ revision, ctx, pool));
+
+ SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));
+
+ if (kind == svn_node_file)
+ {
+ apr_hash_t *props;
+
+ /* Since you cannot actually root an editor at a file, we
+ * manually drive a few functions of our editor. */
+
+ /* Step outside the editor-likeness for a moment, to actually talk
+ * to the repository. */
+ /* ### note: the stream will not be closed */
+ SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
+ svn_stream_empty(pool),
+ NULL, &props, pool));
+ }
+ else if (kind == svn_node_dir)
+ {
+ void *edit_baton = NULL;
+ const svn_delta_editor_t *export_editor = NULL;
+ const svn_ra_reporter3_t *reporter;
+ void *report_baton;
+
+ svn_delta_editor_t *editor = svn_delta_default_editor(pool);
+
+ editor->set_target_revision = set_target_revision;
+ editor->open_root = open_root;
+ editor->add_directory = add_directory;
+ editor->add_file = add_file;
+ editor->apply_textdelta = apply_textdelta;
+ editor->close_file = close_file;
+ editor->change_file_prop = change_file_prop;
+ editor->change_dir_prop = change_dir_prop;
+
+ /* for ra_svn, we don't need an editior in quiet mode */
+ if (!quiet || strncmp(loc->repos_root_url, "svn:", 4))
+ SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
+ ctx->cancel_baton,
+ editor,
+ baton,
+ &export_editor,
+ &edit_baton,
+ pool));
+
+ /* Manufacture a basic 'report' to the update reporter. */
+ SVN_ERR(svn_ra_do_update3(ra_session,
+ &reporter, &report_baton,
+ loc->rev,
+ "", /* no sub-target */
+ depth,
+ FALSE, /* don't want copyfrom-args */
+ FALSE, /* don't want ignore_ancestry */
+ export_editor, edit_baton,
+ pool, pool));
+
+ SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
+ /* Depth is irrelevant, as we're
+ passing start_empty=TRUE anyway. */
+ svn_depth_infinity,
+ TRUE, /* "help, my dir is empty!" */
+ NULL, pool));
+
+ SVN_ERR(reporter->finish_report(report_baton, pool));
+ }
+ else if (kind == svn_node_none)
+ {
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("URL '%s' doesn't exist"),
+ from_path_or_url);
+ }
+ /* kind == svn_node_unknown not handled */
+ }
+
+
+ if (result_rev)
+ *result_rev = edit_revision;
+
+ return SVN_NO_ERROR;
+}
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__null_export(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *from;
+ apr_array_header_t *targets;
+ svn_error_t *err;
+ svn_opt_revision_t peg_revision;
+ const char *truefrom;
+ edit_baton_t eb = { 0 };
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We want exactly 1 or 2 targets for this subcommand. */
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ /* The first target is the `from' path. */
+ from = APR_ARRAY_IDX(targets, 0, const char *);
+
+ /* Get the peg revision if present. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool));
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_infinity;
+
+ /* Do the export. */
+ err = bench_null_export(NULL, truefrom, &peg_revision,
+ &(opt_state->start_revision),
+ opt_state->depth,
+ &eb,
+ ctx, opt_state->quiet, pool);
+
+ if (!opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("%15s directories\n"
+ "%15s files\n"
+ "%15s bytes in files\n"
+ "%15s properties\n"
+ "%15s bytes in properties\n"),
+ svn__ui64toa_sep(eb.dir_count, ',', pool),
+ svn__ui64toa_sep(eb.file_count, ',', pool),
+ svn__ui64toa_sep(eb.byte_count, ',', pool),
+ svn__ui64toa_sep(eb.prop_count, ',', pool),
+ svn__ui64toa_sep(eb.prop_byte_count, ',', pool)));
+
+ return svn_error_trace(err);
+}
diff --git a/subversion/svnbench/null-info-cmd.c b/subversion/svnbench/null-info-cmd.c
new file mode 100644
index 0000000..c9b8710
--- /dev/null
+++ b/subversion/svnbench/null-info-cmd.c
@@ -0,0 +1,287 @@
+/*
+ * info-cmd.c -- Display information about a resource
+ *
+ * ====================================================================
+ * 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 "svn_string.h"
+#include "svn_cmdline.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_time.h"
+#include "svn_xml.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_client_private.h"
+
+
+/*** Code. ***/
+
+/* The dirent fields we care about for our calls to svn_ra_get_dir2. */
+#define DIRENT_FIELDS (SVN_DIRENT_KIND | \
+ SVN_DIRENT_CREATED_REV | \
+ SVN_DIRENT_TIME | \
+ SVN_DIRENT_LAST_AUTHOR)
+
+/* Helper func for recursively fetching svn_dirent_t's from a remote
+ directory and pushing them at an info-receiver callback.
+
+ DEPTH is the depth starting at DIR, even though RECEIVER is never
+ invoked on DIR: if DEPTH is svn_depth_immediates, then increment
+ *COUNTER on all children of DIR, but none of their children; if
+ svn_depth_files, then increment *COUNTER on file children of DIR but
+ not on subdirectories; if svn_depth_infinity, recurse fully.
+ DIR is a relpath, relative to the root of RA_SESSION.
+*/
+static svn_error_t *
+push_dir_info(svn_ra_session_t *ra_session,
+ const svn_client__pathrev_t *pathrev,
+ const char *dir,
+ int *counter,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ apr_hash_t *tmpdirents;
+ apr_hash_index_t *hi;
+ apr_pool_t *subpool = svn_pool_create(pool);
+
+ SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL,
+ dir, pathrev->rev, DIRENT_FIELDS, pool));
+
+ for (hi = apr_hash_first(pool, tmpdirents); hi; hi = apr_hash_next(hi))
+ {
+ const char *path;
+ const char *name = apr_hash_this_key(hi);
+ svn_dirent_t *the_ent = apr_hash_this_val(hi);
+ svn_client__pathrev_t *child_pathrev;
+
+ svn_pool_clear(subpool);
+
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
+ path = svn_relpath_join(dir, name, subpool);
+ child_pathrev = svn_client__pathrev_join_relpath(pathrev, name, subpool);
+
+ if (depth >= svn_depth_immediates
+ || (depth == svn_depth_files && the_ent->kind == svn_node_file))
+ ++(*counter);
+
+ if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
+ SVN_ERR(push_dir_info(ra_session, child_pathrev, path,
+ counter, depth, ctx, subpool));
+ }
+
+ svn_pool_destroy(subpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Stripped-down version of svn_client_info3 */
+static svn_error_t *
+client_info(const char *abspath_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ svn_boolean_t fetch_excluded,
+ svn_boolean_t fetch_actual_only,
+ const apr_array_header_t *changelists,
+ int *counter,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_ra_session_t *ra_session;
+ svn_client__pathrev_t *pathrev;
+ svn_lock_t *lock;
+ const char *base_name;
+ svn_dirent_t *the_ent;
+ svn_error_t *err;
+
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_empty;
+
+ /* Go repository digging instead. */
+
+ /* Trace rename history (starting at path_or_url@peg_revision) and
+ return RA session to the possibly-renamed URL as it exists in REVISION.
+ The ra_session returned will be anchored on this "final" URL. */
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
+ abspath_or_url, NULL, peg_revision,
+ revision, ctx, pool));
+
+ svn_uri_split(NULL, &base_name, pathrev->url, pool);
+
+ /* Get the dirent for the URL itself. */
+ SVN_ERR(svn_ra_stat(ra_session, "", pathrev->rev, &the_ent, pool));
+
+ if (! the_ent)
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("URL '%s' non-existent in revision %ld"),
+ pathrev->url, pathrev->rev);
+
+ /* check for locks */
+ err = svn_ra_get_lock(ra_session, &lock, "", pool);
+
+ /* An old mod_dav_svn will always work; there's nothing wrong with
+ doing a PROPFIND for a property named "DAV:supportedlock". But
+ an old svnserve will error. */
+ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
+ {
+ svn_error_clear(err);
+ lock = NULL;
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ /* Push the URL's dirent (and lock) at the callback.*/
+ ++(*counter);
+
+ /* Possibly recurse, using the original RA session. */
+ if (depth > svn_depth_empty && (the_ent->kind == svn_node_dir))
+ {
+ apr_hash_t *locks;
+
+ if (peg_revision->kind == svn_opt_revision_head)
+ {
+ err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool);
+
+ /* Catch specific errors thrown by old mod_dav_svn or svnserve. */
+ if (err &&
+ (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED
+ || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
+ svn_error_clear(err);
+ else if (err)
+ return svn_error_trace(err);
+ }
+
+ SVN_ERR(push_dir_info(ra_session, pathrev, "",
+ counter, depth, ctx, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__null_info(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ int i;
+ svn_error_t *err;
+ svn_boolean_t seen_nonexistent_target = FALSE;
+ svn_opt_revision_t peg_revision;
+ const char *path_prefix;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments. */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *truepath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ int received_count = 0;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Get peg revisions. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool));
+
+ /* If no peg-rev was attached to a URL target, then assume HEAD. */
+ if (svn_path_is_url(truepath))
+ {
+ if (peg_revision.kind == svn_opt_revision_unspecified)
+ peg_revision.kind = svn_opt_revision_head;
+ }
+ else
+ {
+ SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
+ }
+
+ err = client_info(truepath,
+ &peg_revision, &(opt_state->start_revision),
+ opt_state->depth, TRUE, TRUE,
+ NULL,
+ &received_count,
+ ctx, subpool);
+
+ if (err)
+ {
+ /* If one of the targets is a non-existent URL or wc-entry,
+ don't bail out. Just warn and move on to the next target. */
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+ err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+ {
+ svn_handle_warning2(stderr, err, "svnbench: ");
+ svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n"));
+ }
+ else
+ {
+ return svn_error_trace(err);
+ }
+
+ svn_error_clear(err);
+ err = NULL;
+ seen_nonexistent_target = TRUE;
+ }
+ else
+ {
+ SVN_ERR(svn_cmdline_printf(pool, _("Number of status notifications "
+ "received: %d\n"),
+ received_count));
+ }
+ }
+ svn_pool_destroy(subpool);
+
+ if (seen_nonexistent_target)
+ return svn_error_create(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Could not display info for all targets because some "
+ "targets don't exist"));
+ else
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnbench/null-list-cmd.c b/subversion/svnbench/null-list-cmd.c
new file mode 100644
index 0000000..3f19209
--- /dev/null
+++ b/subversion/svnbench/null-list-cmd.c
@@ -0,0 +1,169 @@
+/*
+ * list-cmd.c -- list a URL
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_time.h"
+#include "svn_xml.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_utf.h"
+#include "svn_opt.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_string_private.h"
+
+
+
+/* Baton used when printing directory entries. */
+struct print_baton {
+ svn_boolean_t verbose;
+ apr_int64_t directories;
+ apr_int64_t files;
+ apr_int64_t locks;
+ svn_client_ctx_t *ctx;
+};
+
+/* This implements the svn_client_list_func2_t API, printing a single
+ directory entry in text format. */
+static svn_error_t *
+print_dirent(void *baton,
+ const char *path,
+ const svn_dirent_t *dirent,
+ const svn_lock_t *lock,
+ const char *abs_path,
+ const char *external_parent_url,
+ const char *external_target,
+ apr_pool_t *pool)
+{
+ struct print_baton *pb = baton;
+
+ if (pb->ctx->cancel_func)
+ SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
+
+ if (dirent->kind == svn_node_dir)
+ pb->directories++;
+ if (dirent->kind == svn_node_file)
+ pb->files++;
+ if (lock)
+ pb->locks++;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__null_list(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ apr_array_header_t *targets;
+ int i;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ apr_uint32_t dirent_fields;
+ struct print_baton pb = { FALSE };
+ svn_boolean_t seen_nonexistent_target = FALSE;
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ if (opt_state->verbose)
+ dirent_fields = SVN_DIRENT_ALL;
+ else
+ dirent_fields = SVN_DIRENT_KIND; /* the only thing we actually need... */
+
+ pb.ctx = ctx;
+ pb.verbose = opt_state->verbose;
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_immediates;
+
+ /* For each target, try to list it. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Get peg revisions. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ subpool));
+
+ err = svn_client_list3(truepath, &peg_revision,
+ &(opt_state->start_revision),
+ opt_state->depth,
+ dirent_fields,
+ opt_state->verbose,
+ FALSE, /* include externals */
+ print_dirent,
+ &pb, ctx, subpool);
+
+ if (err)
+ {
+ /* If one of the targets is a non-existent URL or wc-entry,
+ don't bail out. Just warn and move on to the next target. */
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+ err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ svn_handle_warning2(stderr, err, "svnbench: ");
+ else
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ err = NULL;
+ seen_nonexistent_target = TRUE;
+ }
+ else if (!opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("%15s directories\n"
+ "%15s files\n"
+ "%15s locks\n"),
+ svn__ui64toa_sep(pb.directories, ',', pool),
+ svn__ui64toa_sep(pb.files, ',', pool),
+ svn__ui64toa_sep(pb.locks, ',', pool)));
+ }
+
+ svn_pool_destroy(subpool);
+
+ if (seen_nonexistent_target)
+ return svn_error_create(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Could not list all targets because some targets don't exist"));
+ else
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnbench/null-log-cmd.c b/subversion/svnbench/null-log-cmd.c
new file mode 100644
index 0000000..e8f9734
--- /dev/null
+++ b/subversion/svnbench/null-log-cmd.c
@@ -0,0 +1,243 @@
+/*
+ * log-cmd.c -- Display log messages
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#define APR_WANT_STRFUNC
+#define APR_WANT_STDIO
+#include <apr_want.h>
+
+#include "svn_cmdline.h"
+#include "svn_compat.h"
+#include "svn_path.h"
+#include "svn_props.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_string_private.h"
+
+
+/*** Code. ***/
+
+/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */
+struct log_receiver_baton
+{
+ /* Client context. */
+ svn_client_ctx_t *ctx;
+
+ /* Level of merge revision nesting */
+ apr_size_t merge_depth;
+
+ /* collect counters? */
+ svn_boolean_t quiet;
+
+ /* total revision counters */
+ apr_int64_t revisions;
+ apr_int64_t changes;
+ apr_int64_t message_lines;
+
+ /* part that came from merges */
+ apr_int64_t merges;
+ apr_int64_t merged_revs;
+ apr_int64_t merged_changes;
+ apr_int64_t merged_message_lines;
+};
+
+
+/* Implement `svn_log_entry_receiver_t', printing the logs in
+ * a human-readable and machine-parseable format.
+ *
+ * BATON is of type `struct log_receiver_baton'.
+ */
+static svn_error_t *
+log_entry_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct log_receiver_baton *lb = baton;
+ const char *author;
+ const char *date;
+ const char *message;
+
+ if (lb->ctx->cancel_func)
+ SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
+
+ if (! SVN_IS_VALID_REVNUM(log_entry->revision))
+ {
+ lb->merge_depth--;
+ return SVN_NO_ERROR;
+ }
+
+ /* if we don't want counters, we are done */
+ if (lb->quiet)
+ return SVN_NO_ERROR;
+
+ /* extract the message and do all the other counting */
+ svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
+ if (log_entry->revision == 0 && message == NULL)
+ return SVN_NO_ERROR;
+
+ lb->revisions++;
+ if (lb->merge_depth)
+ lb->merged_revs++;
+
+ if (message != NULL)
+ {
+ int count = svn_cstring_count_newlines(message) + 1;
+ lb->message_lines += count;
+ if (lb->merge_depth)
+ lb->merged_message_lines += count;
+ }
+
+ if (log_entry->changed_paths2)
+ {
+ unsigned count = apr_hash_count(log_entry->changed_paths2);
+ lb->changes += count;
+ if (lb->merge_depth)
+ lb->merged_changes += count;
+ }
+
+ if (log_entry->has_children)
+ {
+ lb->merge_depth++;
+ lb->merges++;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__null_log(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ apr_array_header_t *targets;
+ struct log_receiver_baton lb = { 0 };
+ const char *target;
+ int i;
+ apr_array_header_t *revprops;
+ svn_opt_revision_t target_peg_revision;
+ const char *target_path_or_url;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ /* Determine if they really want a two-revision range. */
+ if (opt_state->used_change_arg)
+ {
+ if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1)
+ {
+ return svn_error_create
+ (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("-c and -r are mutually exclusive"));
+ }
+ for (i = 0; i < opt_state->revision_ranges->nelts; i++)
+ {
+ svn_opt_revision_range_t *range;
+ range = APR_ARRAY_IDX(opt_state->revision_ranges, i,
+ svn_opt_revision_range_t *);
+ if (range->start.value.number < range->end.value.number)
+ range->start.value.number++;
+ else
+ range->end.value.number++;
+ }
+ }
+
+ /* Parse the first target into path-or-url and peg revision. */
+ target = APR_ARRAY_IDX(targets, 0, const char *);
+ SVN_ERR(svn_opt_parse_path(&target_peg_revision, &target_path_or_url,
+ target, pool));
+ if (target_peg_revision.kind == svn_opt_revision_unspecified)
+ target_peg_revision.kind = (svn_path_is_url(target)
+ ? svn_opt_revision_head
+ : svn_opt_revision_working);
+ APR_ARRAY_IDX(targets, 0, const char *) = target_path_or_url;
+
+ if (svn_path_is_url(target))
+ {
+ for (i = 1; i < targets->nelts; i++)
+ {
+ target = APR_ARRAY_IDX(targets, i, const char *);
+
+ if (svn_path_is_url(target) || target[0] == '/')
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Only relative paths can be specified"
+ " after a URL for 'svnbench log', "
+ "but '%s' is not a relative path"),
+ target);
+ }
+ }
+
+ lb.ctx = ctx;
+ lb.quiet = opt_state->quiet;
+
+ revprops = apr_array_make(pool, 3, sizeof(char *));
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
+ if (!opt_state->quiet)
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
+ SVN_ERR(svn_client_log5(targets,
+ &target_peg_revision,
+ opt_state->revision_ranges,
+ opt_state->limit,
+ opt_state->verbose,
+ opt_state->stop_on_copy,
+ opt_state->use_merge_history,
+ revprops,
+ log_entry_receiver,
+ &lb,
+ ctx,
+ pool));
+
+ if (!opt_state->quiet)
+ {
+ if (opt_state->use_merge_history)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("%15s revisions, %15s merged in %s merges\n"
+ "%15s msg lines, %15s in merged revisions\n"
+ "%15s changes, %15s in merged revisions\n"),
+ svn__ui64toa_sep(lb.revisions, ',', pool),
+ svn__ui64toa_sep(lb.merged_revs, ',', pool),
+ svn__ui64toa_sep(lb.merges, ',', pool),
+ svn__ui64toa_sep(lb.message_lines, ',', pool),
+ svn__ui64toa_sep(lb.merged_message_lines, ',', pool),
+ svn__ui64toa_sep(lb.changes, ',', pool),
+ svn__ui64toa_sep(lb.merged_changes, ',', pool)));
+ else
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("%15s revisions\n"
+ "%15s msg lines\n"
+ "%15s changes\n"),
+ svn__ui64toa_sep(lb.revisions, ',', pool),
+ svn__ui64toa_sep(lb.message_lines, ',', pool),
+ svn__ui64toa_sep(lb.changes, ',', pool)));
+ }
+
+ return SVN_NO_ERROR;
+}
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;
+}
diff --git a/subversion/svnbench/util.c b/subversion/svnbench/util.c
new file mode 100644
index 0000000..2aedde6
--- /dev/null
+++ b/subversion/svnbench/util.c
@@ -0,0 +1,92 @@
+/*
+ * util.c: Subversion command line client utility functions. Any
+ * functions that need to be shared across subcommands should be put
+ * in here.
+ *
+ * ====================================================================
+ * 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 <ctype.h>
+#include <assert.h>
+
+#include "svn_private_config.h"
+#include "svn_error.h"
+#include "svn_path.h"
+
+#include "cl.h"
+
+
+
+svn_error_t *
+svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t keep_last_origpath_on_truepath_collision,
+ apr_pool_t *pool)
+{
+ svn_error_t *err = svn_client_args_to_target_array2(targets,
+ os,
+ known_targets,
+ ctx,
+ keep_last_origpath_on_truepath_collision,
+ pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_RESERVED_FILENAME_SPECIFIED)
+ {
+ svn_handle_error2(err, stderr, FALSE, "svn: Skipping argument: ");
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__check_target_is_local_path(const char *target)
+{
+ if (svn_path_is_url(target))
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a local path"), target);
+ return SVN_NO_ERROR;
+}
+
+const char *
+svn_cl__local_style_skip_ancestor(const char *parent_path,
+ const char *path,
+ apr_pool_t *pool)
+{
+ const char *relpath = NULL;
+
+ if (parent_path)
+ relpath = svn_dirent_skip_ancestor(parent_path, path);
+
+ return svn_dirent_local_style(relpath ? relpath : path, pool);
+}
+