2 * svnbench.c: Subversion benchmark client.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
33 #include <apr_signal.h>
35 #include "svn_cmdline.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_pools.h"
39 #include "svn_version.h"
43 #include "private/svn_opt_private.h"
44 #include "private/svn_cmdline_private.h"
46 #include "svn_private_config.h"
49 /*** Option Processing ***/
51 /* Add an identifier here for long options that don't have a short
52 option. Options that have both long and short options should just
53 use the short option letter as identifier. */
54 typedef enum svn_cl__longopt_t {
55 opt_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
67 opt_with_all_revprops,
69 opt_trust_server_cert,
70 opt_trust_server_cert_failures,
75 /* Option codes and descriptions for the command line client.
77 * The entire list must be terminated with an entry of nulls.
79 const apr_getopt_option_t svn_cl__options[] =
81 {"help", 'h', 0, N_("show help on a subcommand")},
82 {NULL, '?', 0, N_("show help on a subcommand")},
83 {"quiet", 'q', 0, N_("print nothing, or only summary information")},
84 {"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")},
85 {"non-recursive", 'N', 0, N_("obsolete; try --depth=files or --depth=immediates")},
87 N_("the change made by revision ARG (like -r ARG-1:ARG)\n"
89 "If ARG is negative this is like -r ARG:ARG-1\n"
91 "If ARG is of the form ARG1-ARG2 then this is like\n"
93 "ARG1:ARG2, where ARG1 is inclusive")},
95 N_("ARG (some commands also take ARG1:ARG2 range)\n"
97 "A revision argument can be one of:\n"
99 " NUMBER revision number\n"
101 " '{' DATE '}' revision at start of the date\n"
103 " 'HEAD' latest in repository\n"
105 " 'BASE' base rev of item's working copy\n"
107 " 'COMMITTED' last commit at or before BASE\n"
109 " 'PREV' revision just before COMMITTED")},
110 {"version", opt_version, 0, N_("show program version information")},
111 {"verbose", 'v', 0, N_("print extra information")},
112 {"username", opt_auth_username, 1, N_("specify a username ARG")},
113 {"password", opt_auth_password, 1, N_("specify a password ARG")},
114 {"targets", opt_targets, 1,
115 N_("pass contents of file ARG as additional args")},
116 {"depth", opt_depth, 1,
117 N_("limit operation by depth ARG ('empty', 'files',\n"
119 "'immediates', or 'infinity')")},
120 {"strict", opt_strict, 0, N_("use strict semantics")},
121 {"stop-on-copy", opt_stop_on_copy, 0,
122 N_("do not cross copies while traversing history")},
123 {"no-auth-cache", opt_no_auth_cache, 0,
124 N_("do not cache authentication tokens")},
125 {"trust-server-cert", opt_trust_server_cert, 0,
126 N_("deprecated; same as\n"
128 "--trust-server-cert-failures=unknown-ca")},
129 {"trust-server-cert-failures", opt_trust_server_cert_failures, 1,
130 N_("with --non-interactive, accept SSL server\n"
132 "certificates with failures; ARG is comma-separated\n"
134 "list of 'unknown-ca' (Unknown Authority),\n"
136 "'cn-mismatch' (Hostname mismatch), 'expired'\n"
138 "(Expired certificate), 'not-yet-valid' (Not yet\n"
140 "valid certificate) and 'other' (all other not\n"
142 "separately classified certificate errors).")},
143 {"non-interactive", opt_non_interactive, 0,
144 N_("do no interactive prompting")},
145 {"config-dir", opt_config_dir, 1,
146 N_("read user configuration files from directory ARG")},
147 {"config-option", opt_config_options, 1,
148 N_("set user configuration option in the format:\n"
150 " FILE:SECTION:OPTION=[VALUE]\n"
154 " servers:global:http-library=serf")},
155 {"limit", 'l', 1, N_("maximum number of log entries")},
156 {"with-all-revprops", opt_with_all_revprops, 0,
157 N_("retrieve all revision properties")},
158 {"with-no-revprops", opt_with_no_revprops, 0,
159 N_("retrieve no revision properties")},
160 {"with-revprop", opt_with_revprop, 1,
161 N_("set revision property ARG in new revision\n"
163 "using the name[=value] format")},
164 {"use-merge-history", 'g', 0,
165 N_("use/display additional information from merge\n"
171 * These have NULL desriptions, but an option code that matches some
172 * other option (whose description should probably mention its aliases).
180 /*** Command dispatch. ***/
182 /* Our array of available subcommands.
184 * The entire list must be terminated with an entry of nulls.
186 * In most of the help text "PATH" is used where a working copy path is
187 * required, "URL" where a repository URL is required and "TARGET" when
188 * either a path or a url can be used. Hmm, should this be part of the
192 /* Options that apply to all commands. (While not every command may
193 currently require authentication or be interactive, allowing every
194 command to take these arguments allows scripts to just pass them
195 willy-nilly to every invocation of 'svn') . */
196 const int svn_cl__global_options[] =
197 { opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive,
198 opt_trust_server_cert, opt_trust_server_cert_failures,
199 opt_config_dir, opt_config_options, 0
202 const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
204 { "help", svn_cl__help, {"?", "h"}, N_
205 ("Describe the usage of this program or its subcommands.\n"
206 "usage: help [SUBCOMMAND...]\n"),
208 /* This command is also invoked if we see option "--help", "-h" or "-?". */
210 { "null-blame", svn_cl__null_blame, {0}, N_
211 ("Fetch all versions of a file in a batch.\n"
212 "usage: null-blame [-rM:N] TARGET[@REV]...\n"
214 " With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n"
215 " annotate each line that is present in revision N of the file, with\n"
216 " the last revision at or before rN that changed or added the line,\n"
217 " looking back no further than rM.\n"
219 " With a reverse revision range '-r M:N' where M > N,\n"
220 " annotate each line that is present in revision N of the file, with\n"
221 " the next revision after rN that changed or deleted the line,\n"
222 " looking forward no further than rM.\n"
224 " If specified, REV determines in which revision the target is first\n"
227 " Write the annotated result to standard output.\n"),
230 { "null-export", svn_cl__null_export, {0}, N_
231 ("Create an unversioned copy of a tree.\n"
232 "usage: null-export [-r REV] URL[@PEGREV]\n"
234 " Exports a clean directory tree from the repository specified by\n"
235 " URL, at revision REV if it is given, otherwise at HEAD.\n"
237 " If specified, PEGREV determines in which revision the target is first\n"
239 {'r', 'q', 'N', opt_depth} },
241 { "null-list", svn_cl__null_list, {"ls"}, N_
242 ("List directory entries in the repository.\n"
243 "usage: null-list [TARGET[@REV]...]\n"
245 " List each TARGET file and the contents of each TARGET directory as\n"
246 " they exist in the repository. If TARGET is a working copy path, the\n"
247 " corresponding repository URL will be used. If specified, REV determines\n"
248 " in which revision the target is first looked up.\n"
250 " The default TARGET is '.', meaning the repository URL of the current\n"
251 " working directory.\n"
253 " With --verbose, the following fields will be fetched for each item:\n"
255 " Revision number of the last commit\n"
256 " Author of the last commit\n"
257 " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
259 " Date and time of the last commit\n"),
260 {'r', 'v', 'q', 'R', opt_depth} },
262 { "null-log", svn_cl__null_log, {0}, N_
263 ("Fetch the log messages for a set of revision(s) and/or path(s).\n"
264 "usage: 1. null-log [PATH][@REV]\n"
265 " 2. null-log URL[@REV] [PATH...]\n"
267 " 1. Fetch the log messages for the URL corresponding to PATH\n"
268 " (default: '.'). If specified, REV is the revision in which the\n"
269 " URL is first looked up, and the default revision range is REV:1.\n"
270 " If REV is not specified, the default revision range is BASE:1,\n"
271 " since the URL might not exist in the HEAD revision.\n"
273 " 2. Fetch the log messages for the PATHs (default: '.') under URL.\n"
274 " If specified, REV is the revision in which the URL is first\n"
275 " looked up, and the default revision range is REV:1; otherwise,\n"
276 " the URL is looked up in HEAD, and the default revision range is\n"
279 " Multiple '-c' or '-r' options may be specified (but not a\n"
280 " combination of '-c' and '-r' options), and mixing of forward and\n"
281 " reverse ranges is allowed.\n"
283 " With -v, also print all affected paths with each log message.\n"
284 " With -q, don't print the log message body itself (note that this is\n"
285 " compatible with -v).\n"
287 " Each log message is printed just once, even if more than one of the\n"
288 " affected paths for that revision were explicitly requested. Logs\n"
289 " follow copy history by default. Use --stop-on-copy to disable this\n"
290 " behavior, which can be useful for determining branchpoints.\n"),
291 {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy,
292 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,},
293 {{opt_with_revprop, N_("retrieve revision property ARG")},
294 {'c', N_("the change made in revision ARG")}} },
296 { "null-info", svn_cl__null_info, {0}, N_
297 ("Display information about a local or remote item.\n"
298 "usage: null-info [TARGET[@REV]...]\n"
300 " Print information about each TARGET (default: '.').\n"
301 " TARGET may be either a working-copy path or URL. If specified, REV\n"
302 " determines in which revision the target is first looked up.\n"),
303 {'r', 'R', opt_depth, opt_targets, opt_changelist}
306 { NULL, NULL, {0}, NULL, {0} }
310 /* Version compatibility check */
312 check_lib_versions(void)
314 static const svn_version_checklist_t checklist[] =
316 { "svn_subr", svn_subr_version },
317 { "svn_client", svn_client_version },
318 { "svn_wc", svn_wc_version },
319 { "svn_ra", svn_ra_version },
320 { "svn_delta", svn_delta_version },
323 SVN_VERSION_DEFINE(my_version);
325 return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
329 /* A flag to see if we've been cancelled by the client or not. */
330 static volatile sig_atomic_t cancelled = FALSE;
332 /* A signal handler to support cancellation. */
334 signal_handler(int signum)
336 apr_signal(signum, SIG_IGN);
340 /* Our cancellation callback. */
342 svn_cl__check_cancel(void *baton)
345 return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
354 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
355 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
356 * return SVN_NO_ERROR.
359 sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
364 svn_cl__opt_state_t opt_state = { 0, { 0 } };
365 svn_client_ctx_t *ctx;
366 apr_array_header_t *received_opts;
368 const svn_opt_subcommand_desc2_t *subcommand = NULL;
369 svn_cl__cmd_baton_t command_baton;
370 svn_auth_baton_t *ab;
371 svn_config_t *cfg_config;
372 svn_boolean_t descend = TRUE;
373 svn_boolean_t use_notifier = TRUE;
375 received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
377 /* Check library versions */
378 SVN_ERR(check_lib_versions());
380 #if defined(WIN32) || defined(__CYGWIN__)
381 /* Set the working copy administrative directory name. */
382 if (getenv("SVN_ASP_DOT_NET_HACK"))
384 SVN_ERR(svn_wc_set_adm_dir("_svn", pool));
388 /* Initialize the RA library. */
389 SVN_ERR(svn_ra_initialize(pool));
391 /* Begin processing arguments. */
392 opt_state.start_revision.kind = svn_opt_revision_unspecified;
393 opt_state.end_revision.kind = svn_opt_revision_unspecified;
394 opt_state.revision_ranges =
395 apr_array_make(pool, 0, sizeof(svn_opt_revision_range_t *));
396 opt_state.depth = svn_depth_unknown;
398 /* No args? Show usage. */
401 SVN_ERR(svn_cl__help(NULL, NULL, pool));
402 *exit_code = EXIT_FAILURE;
406 /* Else, parse options. */
407 SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
413 const char *utf8_opt_arg;
415 /* Parse the next option. */
416 apr_status_t apr_err = apr_getopt_long(os, svn_cl__options, &opt_id,
418 if (APR_STATUS_IS_EOF(apr_err))
422 SVN_ERR(svn_cl__help(NULL, NULL, pool));
423 *exit_code = EXIT_FAILURE;
427 /* Stash the option code in an array before parsing it. */
428 APR_ARRAY_PUSH(received_opts, int) = opt_id;
433 err = svn_cstring_atoi(&opt_state.limit, opt_arg);
436 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err,
437 _("Non-numeric limit argument given"));
439 if (opt_state.limit <= 0)
441 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
442 _("Argument to --limit must be positive"));
448 apr_array_header_t *change_revs =
449 svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, pool);
451 for (i = 0; i < change_revs->nelts; i++)
454 svn_revnum_t changeno, changeno_end;
455 const char *change_str =
456 APR_ARRAY_IDX(change_revs, i, const char *);
457 const char *s = change_str;
458 svn_boolean_t is_negative;
460 /* Check for a leading minus to allow "-c -r42".
461 * The is_negative flag is used to handle "-c -42" and "-c -r42".
462 * The "-c r-42" case is handled by strtol() returning a
463 * negative number. */
464 is_negative = (*s == '-');
468 /* Allow any number of 'r's to prefix a revision number. */
471 changeno = changeno_end = strtol(s, &end, 10);
472 if (end != s && *end == '-')
474 if (changeno < 0 || is_negative)
476 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR,
478 _("Negative number in range (%s)"
479 " not supported with -c"),
485 changeno_end = strtol(s, &end, 10);
487 if (end == change_str || *end != '\0')
489 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
490 _("Non-numeric change argument (%s) "
491 "given to -c"), change_str);
496 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
497 _("There is no change 0"));
501 changeno = -changeno;
503 /* Figure out the range:
506 -c M-N -> -r M-1:N for M < N
507 -c M-N -> -r M:N-1 for M > N
508 -c -M-N -> error (too confusing/no valid use case)
512 if (changeno <= changeno_end)
519 changeno = -changeno;
520 changeno_end = changeno - 1;
523 opt_state.used_change_arg = TRUE;
524 APR_ARRAY_PUSH(opt_state.revision_ranges,
525 svn_opt_revision_range_t *)
526 = svn_opt__revision_range_from_revnums(changeno, changeno_end,
532 opt_state.used_revision_arg = TRUE;
533 if (svn_opt_parse_revision_to_range(opt_state.revision_ranges,
536 SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
537 return svn_error_createf
538 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
539 _("Syntax error in revision argument '%s'"),
544 opt_state.verbose = TRUE;
548 opt_state.help = TRUE;
551 opt_state.quiet = TRUE;
555 svn_stringbuf_t *buffer, *buffer_utf8;
557 /* We need to convert to UTF-8 now, even before we divide
558 the targets into an array, because otherwise we wouldn't
559 know what delimiter to use for svn_cstring_split(). */
561 SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
562 SVN_ERR(svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool));
563 SVN_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool));
564 opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r",
569 opt_state.depth = svn_depth_infinity;
575 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
577 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
578 _("Error converting depth "
579 "from locale to UTF-8"));
580 opt_state.depth = svn_depth_from_word(utf8_opt_arg);
581 if (opt_state.depth == svn_depth_unknown
582 || opt_state.depth == svn_depth_exclude)
584 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
585 _("'%s' is not a valid depth; try "
586 "'empty', 'files', 'immediates', "
592 opt_state.version = TRUE;
594 case opt_auth_username:
595 SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_username,
598 case opt_auth_password:
599 SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_password,
602 case opt_stop_on_copy:
603 opt_state.stop_on_copy = TRUE;
606 opt_state.strict = TRUE;
608 case opt_no_auth_cache:
609 opt_state.no_auth_cache = TRUE;
611 case opt_non_interactive:
612 opt_state.non_interactive = TRUE;
614 case opt_trust_server_cert: /* backwards compat to 1.8 */
615 opt_state.trust_server_cert_unknown_ca = TRUE;
617 case opt_trust_server_cert_failures:
618 SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
619 SVN_ERR(svn_cmdline__parse_trust_options(
620 &opt_state.trust_server_cert_unknown_ca,
621 &opt_state.trust_server_cert_cn_mismatch,
622 &opt_state.trust_server_cert_expired,
623 &opt_state.trust_server_cert_not_yet_valid,
624 &opt_state.trust_server_cert_other_failure,
625 utf8_opt_arg, pool));
629 const char *path_utf8;
630 SVN_ERR(svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool));
631 opt_state.config_dir = svn_dirent_internal_style(path_utf8, pool);
634 case opt_config_options:
635 if (!opt_state.config_options)
636 opt_state.config_options =
637 apr_array_make(pool, 1,
638 sizeof(svn_cmdline__config_argument_t*));
640 SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool));
641 SVN_ERR(svn_cmdline__parse_config_option(opt_state.config_options,
642 opt_arg, "svnbench: ", pool));
644 case opt_with_all_revprops:
645 /* If --with-all-revprops is specified along with one or more
646 * --with-revprops options, --with-all-revprops takes precedence. */
647 opt_state.all_revprops = TRUE;
649 case opt_with_no_revprops:
650 opt_state.no_revprops = TRUE;
652 case opt_with_revprop:
653 SVN_ERR(svn_opt_parse_revprop(&opt_state.revprop_table,
657 opt_state.use_merge_history = TRUE;
660 /* Hmmm. Perhaps this would be a good place to squirrel away
661 opts that commands like svn diff might need. Hmmm indeed. */
666 /* ### This really belongs in libsvn_client. The trouble is,
667 there's no one place there to run it from, no
668 svn_client_init(). We'd have to add it to all the public
669 functions that a client might call. It's unmaintainable to do
670 initialization from within libsvn_client itself, but it seems
671 burdensome to demand that all clients call svn_client_init()
672 before calling any other libsvn_client function... On the other
673 hand, the alternative is effectively to demand that they call
674 svn_config_ensure() instead, so maybe we should have a generic
675 init function anyway. Thoughts? */
676 SVN_ERR(svn_config_ensure(opt_state.config_dir, pool));
678 /* If the user asked for help, then the rest of the arguments are
679 the names of subcommands to get help on (if any), or else they're
680 just typos/mistakes. Whatever the case, the subcommand to
681 actually run is svn_cl__help(). */
683 subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, "help");
685 /* If we're not running the `help' subcommand, then look for a
686 subcommand in the first argument. */
687 if (subcommand == NULL)
689 if (os->ind >= os->argc)
691 if (opt_state.version)
693 /* Use the "help" subcommand to handle the "--version" option. */
694 static const svn_opt_subcommand_desc2_t pseudo_cmd =
695 { "--version", svn_cl__help, {0}, "",
696 {opt_version, /* must accept its own option */
697 'q', /* brief output */
698 'v', /* verbose output */
699 opt_config_dir /* all commands accept this */
702 subcommand = &pseudo_cmd;
707 (svn_cmdline_fprintf(stderr, pool,
708 _("Subcommand argument required\n")));
709 SVN_ERR(svn_cl__help(NULL, NULL, pool));
710 *exit_code = EXIT_FAILURE;
716 const char *first_arg = os->argv[os->ind++];
717 subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
719 if (subcommand == NULL)
721 const char *first_arg_utf8;
722 SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
725 (svn_cmdline_fprintf(stderr, pool,
726 _("Unknown subcommand: '%s'\n"),
728 SVN_ERR(svn_cl__help(NULL, NULL, pool));
729 *exit_code = EXIT_FAILURE;
735 /* Check that the subcommand wasn't passed any inappropriate options. */
736 for (i = 0; i < received_opts->nelts; i++)
738 opt_id = APR_ARRAY_IDX(received_opts, i, int);
740 /* All commands implicitly accept --help, so just skip over this
741 when we see it. Note that we don't want to include this option
742 in their "accepted options" list because it would be awfully
743 redundant to display it in every commands' help text. */
744 if (opt_id == 'h' || opt_id == '?')
747 if (! svn_opt_subcommand_takes_option3(subcommand, opt_id,
748 svn_cl__global_options))
751 const apr_getopt_option_t *badopt =
752 svn_opt_get_option_from_code2(opt_id, svn_cl__options,
754 svn_opt_format_option(&optstr, badopt, FALSE, pool);
755 if (subcommand->name[0] == '-')
756 SVN_ERR(svn_cl__help(NULL, NULL, pool));
760 (stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n"
761 "Type 'svnbench help %s' for usage.\n"),
762 subcommand->name, optstr, subcommand->name));
763 *exit_code = EXIT_FAILURE;
768 /* Only merge and log support multiple revisions/revision ranges. */
769 if (subcommand->cmd_func != svn_cl__null_log)
771 if (opt_state.revision_ranges->nelts > 1)
773 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
774 _("Multiple revision arguments "
775 "encountered; can't specify -c twice, "
776 "or both -c and -r"));
780 /* Disallow simultaneous use of both --with-all-revprops and
781 --with-no-revprops. */
782 if (opt_state.all_revprops && opt_state.no_revprops)
784 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
785 _("--with-all-revprops and --with-no-revprops "
786 "are mutually exclusive"));
789 /* Disallow simultaneous use of both --with-revprop and
790 --with-no-revprops. */
791 if (opt_state.revprop_table && opt_state.no_revprops)
793 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
794 _("--with-revprop and --with-no-revprops "
795 "are mutually exclusive"));
798 /* --trust-* options can only be used with --non-interactive */
799 if (!opt_state.non_interactive)
801 if (opt_state.trust_server_cert_unknown_ca
802 || opt_state.trust_server_cert_cn_mismatch
803 || opt_state.trust_server_cert_expired
804 || opt_state.trust_server_cert_not_yet_valid
805 || opt_state.trust_server_cert_other_failure)
806 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
807 _("--trust-server-cert-failures requires "
808 "--non-interactive"));
811 /* Ensure that 'revision_ranges' has at least one item, and make
812 'start_revision' and 'end_revision' match that item. */
813 if (opt_state.revision_ranges->nelts == 0)
815 svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
816 range->start.kind = svn_opt_revision_unspecified;
817 range->end.kind = svn_opt_revision_unspecified;
818 APR_ARRAY_PUSH(opt_state.revision_ranges,
819 svn_opt_revision_range_t *) = range;
821 opt_state.start_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
822 svn_opt_revision_range_t *)->start;
823 opt_state.end_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
824 svn_opt_revision_range_t *)->end;
826 /* Create a client context object. */
827 command_baton.opt_state = &opt_state;
828 SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
829 command_baton.ctx = ctx;
831 /* Only a few commands can accept a revision range; the rest can take at
832 most one revision number. */
833 if (subcommand->cmd_func != svn_cl__null_blame
834 && subcommand->cmd_func != svn_cl__null_log)
836 if (opt_state.end_revision.kind != svn_opt_revision_unspecified)
838 return svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL);
842 /* -N has a different meaning depending on the command */
844 opt_state.depth = svn_depth_files;
846 err = svn_config_get_config(&(ctx->config),
847 opt_state.config_dir, pool);
850 /* Fallback to default config if the config directory isn't readable
851 or is not a directory. */
852 if (APR_STATUS_IS_EACCES(err->apr_err)
853 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
855 svn_handle_warning2(stderr, err, "svn: ");
856 svn_error_clear(err);
862 cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
863 APR_HASH_KEY_STRING);
865 /* Update the options in the config */
866 if (opt_state.config_options)
869 svn_cmdline__apply_config_options(ctx->config,
870 opt_state.config_options,
871 "svn: ", "--config-option"));
874 /* Set up the notifier.
876 In general, we use it any time we aren't in --quiet mode. 'svn
877 status' is unique, though, in that we don't want it in --quiet mode
878 unless we're also in --verbose mode. When in --xml mode,
879 though, we never want it. */
881 use_notifier = FALSE;
884 SVN_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2,
888 /* Set up our cancellation support. */
889 ctx->cancel_func = svn_cl__check_cancel;
890 apr_signal(SIGINT, signal_handler);
892 /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
893 apr_signal(SIGBREAK, signal_handler);
896 apr_signal(SIGHUP, signal_handler);
899 apr_signal(SIGTERM, signal_handler);
903 /* Disable SIGPIPE generation for the platforms that have it. */
904 apr_signal(SIGPIPE, SIG_IGN);
908 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
909 * working with large files when compiled against an APR that doesn't have
910 * large file support will crash the program, which is uncool. */
911 apr_signal(SIGXFSZ, SIG_IGN);
914 /* Set up Authentication stuff. */
915 SVN_ERR(svn_cmdline_create_auth_baton2(
917 opt_state.non_interactive,
918 opt_state.auth_username,
919 opt_state.auth_password,
920 opt_state.config_dir,
921 opt_state.no_auth_cache,
922 opt_state.trust_server_cert_unknown_ca,
923 opt_state.trust_server_cert_cn_mismatch,
924 opt_state.trust_server_cert_expired,
925 opt_state.trust_server_cert_not_yet_valid,
926 opt_state.trust_server_cert_other_failure,
932 ctx->auth_baton = ab;
934 /* The new svn behavior is to postpone everything until after the operation
936 ctx->conflict_func = NULL;
937 ctx->conflict_baton = NULL;
938 ctx->conflict_func2 = NULL;
939 ctx->conflict_baton2 = NULL;
941 /* And now we finally run the subcommand. */
942 err = (*subcommand->cmd_func)(os, &command_baton, pool);
945 /* For argument-related problems, suggest using the 'help'
947 if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
948 || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
950 err = svn_error_quick_wrapf(
951 err, _("Try 'svnbench help %s' for more information"),
954 if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
956 err = svn_error_quick_wrap(err,
957 _("Please see the 'svn upgrade' command"));
960 /* Tell the user about 'svn cleanup' if any error on the stack
961 was about locked working copies. */
962 if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED))
964 err = svn_error_quick_wrap(
965 err, _("Run 'svn cleanup' to remove locks "
966 "(type 'svn help cleanup' for details)"));
976 main(int argc, const char *argv[])
979 int exit_code = EXIT_SUCCESS;
982 /* Initialize the app. */
983 if (svn_cmdline_init("svnbench", stderr) != EXIT_SUCCESS)
986 /* Create our top-level pool. Use a separate mutexless allocator,
987 * given this application is single threaded.
989 pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
991 err = sub_main(&exit_code, argc, argv, pool);
993 /* Flush stdout and report if it fails. It would be flushed on exit anyway
994 but this makes sure that output is not silently lost if it fails. */
995 err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
999 exit_code = EXIT_FAILURE;
1000 svn_cmdline_handle_exit_error(err, NULL, "svnbench: ");
1003 svn_pool_destroy(pool);