diff options
Diffstat (limited to 'subversion/svnbench')
-rw-r--r-- | subversion/svnbench/cl.h | 203 | ||||
-rw-r--r-- | subversion/svnbench/help-cmd.c | 90 | ||||
-rw-r--r-- | subversion/svnbench/notify.c | 1045 | ||||
-rw-r--r-- | subversion/svnbench/null-blame-cmd.c | 276 | ||||
-rw-r--r-- | subversion/svnbench/null-export-cmd.c | 346 | ||||
-rw-r--r-- | subversion/svnbench/null-info-cmd.c | 287 | ||||
-rw-r--r-- | subversion/svnbench/null-list-cmd.c | 169 | ||||
-rw-r--r-- | subversion/svnbench/null-log-cmd.c | 243 | ||||
-rw-r--r-- | subversion/svnbench/svnbench.c | 1005 | ||||
-rw-r--r-- | subversion/svnbench/util.c | 92 |
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); +} + |